
On 04/06/2011 01:19 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 | 5 ++++ src/util/viratomic.c | 46 ++++++++++++++++++++++++++++++++++++++++ src/util/viratomic.h | 30 ++++++++++++++++++++++++++ src/util/virobject.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/virobject.h | 39 ++++++++++++++++++++++++++++++++++ 6 files changed, 174 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 @@ -993,3 +993,8 @@ virXPathUInt; virXPathULong; virXPathULongHex; virXPathULongLong; + +# object.h
virobject.h, and float this section to appear before virtaudit.h (hmm, maybe we should do a preliminary patch to rename that to viraudit.h, so that we aren't mixing vir vs. virt in quite so many places).
+virObjectInit; +virObjectRef; +virObjectUnref;
Missing exports from viratomic.h.
diff --git a/src/util/viratomic.c b/src/util/viratomic.c new file mode 100644 index 0000000..629f189 --- /dev/null +++ b/src/util/viratomic.c @@ -0,0 +1,46 @@
+ +#ifdef WIN32 +long virAtomicInc(long *value) +{ + return InterlockedIncrement(value); +} + +long virAtomicDec(long *value) +{ + return InterlockedDecrement(value);
This is an OS-specific replacement.
+} +#else /* WIN32 */ +long virAtomicInc(long *value) +{ + return __sync_add_and_fetch(value, 1);
This is a gcc builtin, and will fail to compile with other C99 compilers. Meanwhile, won't the gcc builtin just work for mingw (that is, no need to use the OS-specific InterlockedIncrement if you have the compiler builtin instead). I think this file needs three implementations: #if defined __GNUC__ || <detect Intel's compiler, which also has these> use compiler builtins of __sync_add_and_fetch #elif defined WIN32 use OS primitives, like InterlockedIncrement #else we're hosed when it comes to lightweight versions, but we can still implement a heavyweight replacement that uses virMutex #endif
+++ b/src/util/viratomic.h @@ -0,0 +1,30 @@
+#ifndef __VIR_ATOMIC_H +#define __VIR_ATOMIC_H + +long virAtomicInc(long *value); +long virAtomicDec(long *value);
Mark both of these ATTRIBUTE_NONNULL(1) I'm debating whether they should also be marked ATTRIBUTE_RETURN_CHECK
+++ b/src/util/virobject.c @@ -0,0 +1,52 @@ + +#include "viratomic.h" +#include "virobject.h" +#include "logging.h" + +int virObjectInit(virObjectPtr obj, void (*free)(virObjectPtr obj))
You should declare a typedef: typedef void (*virObjectFreer)(virObjectPtr); then it becomes simpler to read: int virObjectInit(virObjectPtr obj, virObjectFreer f) Use a different name (I used freer/f above) to avoid -Wshadow warnings with free().
+{ + if (!free) {
Especially since shadowing means it's impossible to tell if this statement is always true (the address of free() exists) or conditional (there is a local variable named free shadowing the global function), and context-sensitive code reviews are tougher :)
+ VIR_ERROR0("method free is required."); + return -1; + }
Should this function also check and return -1 if obj->free was not NULL on entry (that is, guarantee that you can't initialize an object twice)?
+ + obj->ref = 1; + obj->free = free; + + return 0; +} + +void virObjectRef(virObjectPtr obj) +{ + sa_assert(obj->ref > 0);
This is useless. It only helps static analyzers (like clang),
+ virAtomicInc(&obj->ref);
but there's nothing to analyze that depends on knowing the value was positive. I'm debating whether to do checking. Maybe we should do: if (virAtomicInc(&obj->ref) < 2) VIR_WARN("invalid call to virObjectRef");
+void virObjectUnref(virObjectPtr obj) +{ + sa_assert(obj->ref > 0);
Again, I'm not sure that this buys anything. But we may want to do: int ref = virAtomicDec(&obj->ref); if (ref < 0) VIR_WARN("invalid call to virObjectUnref"); else if (ref == 0) obj->free(obj)
+ if (virAtomicDec(&obj->ref) == 0) + obj->free(obj); +} diff --git a/src/util/virobject.h b/src/util/virobject.h new file mode 100644 index 0000000..cd7d3e8 --- /dev/null +++ b/src/util/virobject.h
+ +typedef struct _virObject virObject; +typedef virObject *virObjectPtr; + +struct _virObject { + long ref; + void (*free)(virObjectPtr obj);
Is virObjectPtr the right thing to use here? If we assume that all clients will always have a virObjectPtr as their first member, then they can cast that back into the larger object. Is void* opaque any better, although that then it requires a third parameter for virObjectInit? Maybe it's worth some documentation on intended use in this header (am I getting this usage right? I haven't looked at how you used it later in the series, but am just guessing): struct _virFoo { virObject obj; /* Must be first member */ ... }; static void virFooFree(virObjectPtr obj) { virFooPtr foo = obj; ... } virFooPtr virFooNew(void) { virFooPtr foo; if (VIR_ALLOC(foo) < 0) { virReportOOMError(); return NULL; } virObjectInit(&foo->obj, virFooFree); ... return foo; }
+}; + +int virObjectInit(virObjectPtr obj, void (*free)(virObjectPtr obj));
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK
+void virObjectRef(virObjectPtr obj);
ATTRIBUTE_NONNULL(1) Also, should this return the current reference count? Not all callers will need it, but returning void is harsh if someone might be able to use it.
+void virObjectUnref(virObjectPtr obj);
Likewise. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org