On Thu, Apr 07, 2011 at 03:49:15PM +0800, Hu Tao wrote:
On Wed, Apr 06, 2011 at 02:19:57PM -0600, Eric Blake wrote:
> 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,
Oops, there are always things escape from under your nose:(
> 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.
Why the linker doesn't complain about this this time?
>
> > 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
Yes.
> compilers. Meanwhile, won't the gcc builtin just work for mingw (that
Not sure about this. I don't have a mingw environment to test, but I
trust gcc and guess it does.
> 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
Agreed, this is a better way.
>
> > +++ 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)
OK.
>
> I'm debating whether they should also be marked ATTRIBUTE_RETURN_CHECK
No, there are cases you just want to inc/dec a value but do not check
the modified value.
>
> > +++ 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().
OK.
>
> > +{
> > + 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)?
Agreed. And it is dangerous to initialize an object more than once
because the ref count may already changed and a second initializaton
just do the wrong thing.
>
> > +
> > + 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)
This means bad things happened, and it is not enough to just give a
warning. Think about this:
two threads visit an object whose memory is corrupted that it's ref
count becomes 0 but it doesn't get freed.
thread 1: thread 2:
ref the object
(ref count becomes 1)
unref the object
(ref count becomes 0, object being
freed)
do something with
the object, but it
is already freed by
thread 2
We should catch abnormal ref count by some way(if not sa_assert)
> 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,
Exactly the way(you have already saw my other patches when I was typing
this:))
> although that then it requires a third parameter for virObjectInit?
> Maybe it's worth some documentation on intended use in this header (am I
OK.
> 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.
No. It is meaningless to return the current reference count, because it
may already changed by others after the caller reads the returned value
on which it is not safe to make some choice.
I wondered if you had an updated copy of this single patch, with
the changes from this discussion included ? Even though we're
still debating the rest of this patch series, I'd like us to commit
this first virAtomic / virObject patch, so I can use it in some
other code I have waiting. So if you are able to re-post just this
first virObject patch that would be very helpful
Regards,
Daniel
--
|:
http://berrange.com -o-
http://www.flickr.com/photos/dberrange/ :|
|:
http://libvirt.org -o-
http://virt-manager.org :|
|:
http://autobuild.org -o-
http://search.cpan.org/~danberr/ :|
|:
http://entangle-photo.org -o-
http://live.gnome.org/gtk-vnc :|