From: "Daniel P. Berrange" <berrange@xxxxxxxxxx> This introduces a fairly basic reference counted virObject type and an associated virClass type, that use atomic operations for ref counting. In a global initializer (recommended to be invoked using the virOnceInit API), a virClass type must be allocated for each object type. This requires a class name, a "dispose" callback which will be invoked to free memory associated with the object's fields, and the size in bytes of the object struct. eg, virClassPtr connclass = virClassNew("virConnect", sizeof(virConnect), virConnectDispose); The struct for the object, must include 'virObject' as its first member eg struct _virConnect { virObject object; virURIPtr uri; }; The 'dispose' callback is only responsible for freeing fields in the object, not the object itself. eg a suitable impl for the above struct would be void virConnectDispose(void *obj) { virConnectPtr conn = obj; virURIFree(conn->uri); } There is no need to reset fields to 'NULL' or '0' in the dispose callback, since the entire object will be memset to 0, and the klass pointer & magic integer fields will be poisoned with 0xDEADBEEF When creating an instance of an object, one needs simply pass the virClassPtr eg virConnectPtr conn = virObjectNew(connklass); if (!conn) return NULL; conn->uri = virURIParse("foo:///bar") Object references can be manipulated with virObjectRef(conn) virObjectUnref(conn) The latter returns a true value, if the object has been freed (ie its ref count hit zero) Signed-off-by: Daniel P. Berrange <berrange@xxxxxxxxxx> --- src/Makefile.am | 1 + src/libvirt_private.syms | 9 ++ src/libvirt_probes.d | 7 ++ src/util/virobject.c | 204 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/virobject.h | 60 ++++++++++++++ 5 files changed, 281 insertions(+) create mode 100644 src/util/virobject.c create mode 100644 src/util/virobject.h diff --git a/src/Makefile.am b/src/Makefile.am index 984a743..ec849b4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -84,6 +84,7 @@ UTIL_SOURCES = \ util/virauth.c util/virauth.h \ util/virauthconfig.c util/virauthconfig.h \ util/virfile.c util/virfile.h \ + util/virobject.c util/virobject.h \ util/virnodesuspend.c util/virnodesuspend.h \ util/virpidfile.c util/virpidfile.h \ util/virtypedparam.c util/virtypedparam.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 8b9e2c0..1d91b70 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1609,6 +1609,15 @@ nodeSuspendForDuration; virNodeSuspendGetTargetMask; +# virobject.h +virClassName; +virClassNew; +virObjectIsClass; +virObjectNew; +virObjectRef; +virObjectUnref; + + # virpidfile.h virPidFileAcquire; virPidFileAcquirePath; diff --git a/src/libvirt_probes.d b/src/libvirt_probes.d index 0dac8f3..ceb3caa 100644 --- a/src/libvirt_probes.d +++ b/src/libvirt_probes.d @@ -16,6 +16,13 @@ provider libvirt { probe event_poll_run(int nfds, int timeout); + # file: src/util/virobject.c + # prefix: object + probe object_new(void *obj, const char *klassname); + probe object_ref(void *obj); + probe object_unref(void *obj); + probe object_dispose(void *obj); + # file: src/rpc/virnetsocket.c # prefix: rpc probe rpc_socket_new(void *sock, int refs, int fd, int errfd, pid_t pid, const char *localAddr, const char *remoteAddr); diff --git a/src/util/virobject.c b/src/util/virobject.c new file mode 100644 index 0000000..4e02fd0 --- /dev/null +++ b/src/util/virobject.c @@ -0,0 +1,204 @@ +/* + * virobject.c: libvirt reference counted object + * + * Copyright (C) 2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <config.h> + +#include "virobject.h" +#include "threads.h" +#include "memory.h" +#include "viratomic.h" +#include "virterror_internal.h" +#include "logging.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +#define virObjectError(code, ...) \ + virReportErrorHelper(VIR_FROM_THIS, code, __FILE__, \ + __FUNCTION__, __LINE__, __VA_ARGS__) + +static unsigned int magicCounter = 0xCAFE0000; + +struct _virClass { + unsigned int magic; + const char *name; + size_t objectSize; + + virObjectDisposeCallback dispose; +}; + + +/** + * virClassNew: + * @name: the class name + * @objectSize: total size of the object struct + * @dispose: callback to run to free object fields + * + * Register a new object class with @name. The @objectSize + * should give the total size of the object struct, which + * is expected to have a 'virObject object;' field as its + * first member. When the last reference on the object is + * released, the @dispose callback will be invoked to free + * memory of the object fields + * + * Returns a new class instance + */ +virClassPtr virClassNew(const char *name, + size_t objectSize, + virObjectDisposeCallback dispose) +{ + virClassPtr klass; + + if (VIR_ALLOC(klass) < 0) + return NULL; + + if (!(klass->name = strdup(name))) + goto no_memory; + klass->magic = virAtomicIntAdd(&magicCounter, 1); + klass->objectSize = objectSize; + klass->dispose = dispose; + + return klass; + +no_memory: + VIR_FREE(klass); + virReportOOMError(); + return NULL; +} + + +/** + * virObjectNew: + * @klass: the klass of object to create + * + * Allocates a new object of type @klass. The returned + * object will be an instance of "virObjectPtr", which + * can be cast to the struct associated with @klass. + * + * The initial reference count of the object will be 1. + * + * Returns the new object + */ +void *virObjectNew(virClassPtr klass) +{ + virObjectPtr obj = NULL; + char *somebytes; + + if (VIR_ALLOC_N(somebytes, klass->objectSize) < 0) { + virReportOOMError(); + return NULL; + } + obj = (virObjectPtr)somebytes; + + obj->magic = klass->magic; + obj->klass = klass; + virAtomicIntSet(&obj->refs, 1); + + PROBE(OBJECT_NEW, "obj=%p classname=%s", obj, obj->klass->name); + + return obj; +} + + +/** + * virObjectUnref: + * @anyobj: any instance of virObjectPtr + * + * Decrement the reference count on @anyobj and if + * it hits zero, runs the "dispose" callback associated + * with the object class. + * + * Returns true if the remaining reference count is + * non-zero, false if the object was disposed of + */ +bool virObjectUnref(void *anyobj) +{ + virObjectPtr obj = anyobj; + + if (!obj) + return false; + + bool lastRef = virAtomicIntDecAndTest(&obj->refs); + PROBE(OBJECT_UNREF, "obj=%p", obj); + if (lastRef) { + PROBE(OBJECT_DISPOSE, "obj=%p", obj); + if (obj->klass->dispose) + obj->klass->dispose(obj); + + /* Clear & poison object */ + memset(obj, 0, obj->klass->objectSize); + obj->magic = 0xDEADBEEF; + obj->klass = (void*)0xDEADBEEF; + VIR_FREE(obj); + } + + return !lastRef; +} + + +/** + * virObjectRef: + * @anyobj: any instance of virObjectPtr + * + * Increment the reference count on @anyobj and return + * the same pointer + * + * Returns @anyobj + */ +void *virObjectRef(void *anyobj) +{ + virObjectPtr obj = anyobj; + + if (!obj) + return NULL; + virAtomicIntInc(&obj->refs); + PROBE(OBJECT_REF, "obj=%p", obj); + return anyobj; +} + + +/** + * virObjectIsClass: + * @anyobj: any instance of virObjectPtr + * @klass: the class to check + * + * Checks whether @anyobj is an instance of + * @klass + * + * Returns true if @anyobj is an instance of @klass + */ +bool virObjectIsClass(void *anyobj, + virClassPtr klass) +{ + virObjectPtr obj = anyobj; + return obj != NULL && (obj->magic == klass->magic) && (obj->klass == klass); +} + + +/** + * virClassName: + * @klass: the object class + * + * Returns the name of @klass + */ +const char *virClassName(virClassPtr klass) +{ + return klass->name; +} diff --git a/src/util/virobject.h b/src/util/virobject.h new file mode 100644 index 0000000..3eb551d --- /dev/null +++ b/src/util/virobject.h @@ -0,0 +1,60 @@ +/* + * virobject.h: libvirt reference counted object + * + * Copyright (C) 2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __VIR_OBJECT_H__ +# define __VIR_OBJECT_H__ + +# include "internal.h" + +typedef struct _virClass virClass; +typedef virClass *virClassPtr; + +typedef struct _virObject virObject; +typedef virObject *virObjectPtr; + +typedef void (*virObjectDisposeCallback)(void *obj); + +struct _virObject { + unsigned int magic; + virClassPtr klass; + int refs; +}; + +virClassPtr virClassNew(const char *name, + size_t objectSize, + virObjectDisposeCallback dispose) + ATTRIBUTE_NONNULL(1); + +const char *virClassName(virClassPtr klass) + ATTRIBUTE_NONNULL(1); + +void *virObjectNew(virClassPtr klass) + ATTRIBUTE_NONNULL(1); +bool virObjectUnref(void *obj) + ATTRIBUTE_NONNULL(1); +void *virObjectRef(void *obj) + ATTRIBUTE_NONNULL(1); + +bool virObjectIsClass(void *obj, + virClassPtr klass) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + +#endif /* __VIR_OBJECT_H */ -- 1.7.10.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list