On 04/20/2011 03:15 AM, Hu Tao wrote: > virObject is the base struct that manages reference-counting > for all structs that need the ability of reference-counting. > > virAtomic provides atomic operations which are thread-safe. > --- > src/Makefile.am | 2 + > src/libvirt_private.syms | 11 ++++++ > src/util/viratomic.c | 80 ++++++++++++++++++++++++++++++++++++++++++ > src/util/viratomic.h | 32 +++++++++++++++++ > src/util/virobject.c | 64 ++++++++++++++++++++++++++++++++++ > src/util/virobject.h | 86 > ++++++++++++++++++++++++++++++++++++++++++++++ > 6 files changed, 275 insertions(+), 0 deletions(-) > create mode 100644 src/util/viratomic.c > create mode 100644 src/util/viratomic.h > create mode 100644 src/util/virobject.c > create mode 100644 src/util/virobject.h
> +++ b/src/libvirt_private.syms > @@ -958,6 +958,17 @@ virUUIDGenerate; > virUUIDParse; > > > +# virobject.h > +virObjectInit; > +virObjectRef; > +virObjectUnref; > + > + > +# viratomic.h > +virAtomicInc; > +virAtomicDec; Swap these two sections (chunks are alphabetic by header), as well as swap these two lines (within a header, lines are alphabetic). > +++ b/src/util/viratomic.c > +#else > + > +#include "threads.h" > + > +static virMutex virAtomicLock = VIR_MUTEX_INITIALIZER; Hmm, if I implement virOnce first, maybe we should use that instead. > + > +long virAtomicInc(long *value) > +{ > + long result; > + virMutexLock(&virAtomicLock); > + *value += 1; > + result = *value; > + virMutexUnlock(&virAtomicLock); Clunky, but does the job ;) > +#include <stdlib.h> > + > +#include "viratomic.h" > +#include "virobject.h" > +#include "logging.h" > + > +int virObjectInit(virObjectPtr obj, virObjectFree f) > +{ > + if (obj->free) { > + /* This means the obj is already initialized. A second > + * initialization will destroy ref field, which is bad. > + */ > + VIR_ERROR("Object %p is already initialized.", obj); > + return -1; > + } > + > + if (!f) { > + VIR_ERROR0("virObjectFree method is required."); > + abort(); Either both failures should abort(), or both should return -1. They are both programming errors, so I'm personally okay with abort(), but this would be the first use of abort() outside gnulib files, so I'll see if either Dan has opinions as well. In fact, if we go with abort() in both places, then this can return void instead of int. Likewise, by adding ATTRIBUTE_NONNULL(2) to the prototype, then the compiler (well, at least clang) will help catch coding errors of failing to pass a callback function. Oops - you used TAB instead of space; 'make syntax-check' should have caught that. > + > +void virObjectRef(virObjectPtr obj) > +{ > + if (virAtomicInc(&obj->ref) < 2) > + abort(); /* BUG if we get here */ If we keep the abort, then we _need_ the VIR_ERROR before hand saying why we quit. > +} > + > +void virObjectUnref(virObjectPtr obj) > +{ > + long ref = virAtomicDec(&obj->ref); > + if (ref == 0) > + obj->free(obj); > + else if (ref < 0) > + abort(); /* BUG if we get here */ Again, don't ever abort() without VIR_ERROR stating why. And maybe we should think about installing a SIGABRT handler so that aborts print a nicer message for the end user? > + > +typedef struct _virObject virObject; > +typedef virObject *virObjectPtr; > + > +typedef void (*virObjectFree)(virObjectPtr obj); I dislike extra casts, and this signature forces every client to have an explicit cast in their free function. Could we instead make this: typedef void (*virObjectFree)(void *obj); with some additional fallout documented below? Also, can we attach ATTRIBUTE_NONNULL(1) to a typedef? (I'm not quite sure how that works with gcc). > + > +/* A thread owns a virObject by incrementing its reference-count by 1. > + * If a thread owns a virObject, the virObject is guarenteed not be s/guarenteed/guaranteed to/ > + * freed until the thread releases ownership by decrementing its > + * reference-count by 1. A thread can't access a virObject after it > + * releases the ownership of virObject because it can be freed at > + * anytime. > + * > + * A thread can own a virObject legally in these ways: > + * > + * - a thread owns a virObject that it creates. > + * - a thread owns a virObject if another thread passes the ownership > + * to it. Example: qemuMonitorOpen > + * - a thread gets a virObject from a container. > + * Example: virDomainFindByUUID > + * - a container owns a virObject by incrementing its reference-count > + * by 1 before adding it to the container > + * - if a virObject is removed from a container its reference-count > + * must be decremented by 1 > + */ > +/* > + * Usage of virObject: > + * > + * 1. embed virObject into your struct as the first member. > + * > + * struct foo { > + * virObject obj; // must be the first member > + * ... > + * } > + * > + * 2. call virObjectInit(obj, fooFree) in your struct's initialization > + * function. fooFree is the free method which be called automatically > + * when the ref count becomes 0. The argument to fooFree will be &obj, which in turn can be cast as struct foo* because obj is the first member. struct foo * fooCreate() { struct foo *myfoo; if (VIR_ALLOC(myfoo) < 0) { virReportOOMError(); return NULL; } virObjectInit(&myfoo->obj, fooFree); ... // all other contents of myfoo return myfoo; } > + * > + * 3. implement two functions to ref/unref your struct like this: > + * > + * void FooRef(struct foo *f) > + * { > + * virObjectRef(f->obj); > + * } > + * > + * void FooUnref(struct foo *f) > + * { > + * virObjectUnref(f->obj); > + * } 4. implement the free method for your struct like this: static void fooFree(void *obj) { struct foo *myfoo = obj; ... // all other contents of myfoo VIR_FREE(myfoo); } > + */ > +struct _virObject { > + long ref; > + virObjectFree free; > +}; > + > +int virObjectInit(virObjectPtr obj, virObjectFree f); ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) > +void virObjectRef(virObjectPtr obj); ATTRIBUTE_NONNULL(1) > +void virObjectUnref(virObjectPtr obj); ATTRIBUTE_NONNULL(1) -- Eric Blake ebl...@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org
signature.asc
Description: OpenPGP digital signature
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list