/* * virobject.c: libvirt reference counted object * * Copyright (C) 2012-2013 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, see * . * */ #include #define VIR_PARENT_REQUIRED /* empty, to allow virObject to have no parent */ #include "virobject.h" #include "virthread.h" #include "viralloc.h" #include "viratomic.h" #include "virerror.h" #include "virlog.h" #include "virstring.h" #define VIR_FROM_THIS VIR_FROM_NONE static unsigned int magicCounter = 0xCAFE0000; struct _virClass { virClassPtr parent; unsigned int magic; char *name; size_t objectSize; virObjectDisposeCallback dispose; }; static virClassPtr virObjectClass; static virClassPtr virObjectLockableClass; static void virObjectLockableDispose(void *anyobj); static int virObjectOnceInit(void) { if (!(virObjectClass = virClassNew(NULL, "virObject", sizeof(virObject), NULL))) return -1; if (!(virObjectLockableClass = virClassNew(virObjectClass, "virObjectLockable", sizeof(virObjectLockable), virObjectLockableDispose))) return -1; return 0; } VIR_ONCE_GLOBAL_INIT(virObject); /** * virClassForObject: * * Returns the class instance for the base virObject type */ virClassPtr virClassForObject(void) { if (virObjectInitialize() < 0) return NULL; return virObjectClass; } /** * virClassForObjectLockable: * * Returns the class instance for the virObjectLockable type */ virClassPtr virClassForObjectLockable(void) { if (virObjectInitialize() < 0) return NULL; return virObjectLockableClass; } /** * virClassNew: * @parent: the parent class * @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(virClassPtr parent, const char *name, size_t objectSize, virObjectDisposeCallback dispose) { virClassPtr klass; if (parent == NULL && STRNEQ(name, "virObject")) { virReportInvalidNonNullArg(parent); return NULL; } else if (parent && objectSize <= parent->objectSize) { virReportInvalidArg(objectSize, _("object size %zu of %s is smaller than parent class %zu"), objectSize, name, parent->objectSize); return NULL; } if (VIR_ALLOC(klass) < 0) goto error; klass->parent = parent; if (VIR_STRDUP(klass->name, name) < 0) goto error; klass->magic = virAtomicIntInc(&magicCounter); klass->objectSize = objectSize; klass->dispose = dispose; return klass; error: VIR_FREE(klass); return NULL; } /** * virClassIsDerivedFrom: * @klass: the klass to check * @parent: the possible parent class * * Determine if @klass is derived from @parent * * Return true if @klass is derived from @parent, false otherwise */ bool virClassIsDerivedFrom(virClassPtr klass, virClassPtr parent) { while (klass) { if (klass->magic == parent->magic) return true; klass = klass->parent; } return false; } /** * 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; if (VIR_ALLOC_VAR(obj, char, klass->objectSize - sizeof(virObject)) < 0) return NULL; obj->magic = klass->magic; obj->klass = klass; virAtomicIntSet(&obj->refs, 1); PROBE(OBJECT_NEW, "obj=%p classname=%s", obj, obj->klass->name); return obj; } void *virObjectLockableNew(virClassPtr klass) { virObjectLockablePtr obj; if (!virClassIsDerivedFrom(klass, virClassForObjectLockable())) { virReportInvalidArg(klass, _("Class %s must derive from virObjectLockable"), virClassName(klass)); return NULL; } if (!(obj = virObjectNew(klass))) return NULL; if (virMutexInit(&obj->lock) < 0) { virReportSystemError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unable to initialize mutex")); virObjectUnref(obj); return NULL; } return obj; } static void virObjectLockableDispose(void *anyobj) { virObjectLockablePtr obj = anyobj; virMutexDestroy(&obj->lock); } /** * 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 and frees @anyobj. * * 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); virClassPtr klass = obj->klass; while (klass) { if (klass->dispose) klass->dispose(obj); klass = klass->parent; } /* 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; } /** * virObjectLock: * @anyobj: any instance of virObjectLockablePtr * * Acquire a lock on @anyobj. The lock must be * released by virObjectUnlock. * * The caller is expected to have acquired a reference * on the object before locking it (eg virObjectRef). * The object must be unlocked before releasing this * reference. */ void virObjectLock(void *anyobj) { virObjectLockablePtr obj = anyobj; if (!virObjectIsClass(obj, virObjectLockableClass)) { VIR_WARN("Object %p (%s) is not a virObjectLockable instance", obj, obj ? obj->parent.klass->name : "(unknown)"); return; } virMutexLock(&obj->lock); } /** * virObjectUnlock: * @anyobj: any instance of virObjectLockablePtr * * Release a lock on @anyobj. The lock must have been * acquired by virObjectLock. */ void virObjectUnlock(void *anyobj) { virObjectLockablePtr obj = anyobj; if (!virObjectIsClass(obj, virObjectLockableClass)) { VIR_WARN("Object %p (%s) is not a virObjectLockable instance", obj, obj ? obj->parent.klass->name : "(unknown)"); return; } virMutexUnlock(&obj->lock); } /** * 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; if (!obj) return false; return virClassIsDerivedFrom(obj->klass, klass); } /** * virClassName: * @klass: the object class * * Returns the name of @klass */ const char *virClassName(virClassPtr klass) { return klass->name; } /** * virObjectFreeCallback: * @opaque: a pointer to a virObject instance * * Provides identical functionality to virObjectUnref, * but with the signature matching the virFreeCallback * typedef. */ void virObjectFreeCallback(void *opaque) { virObjectUnref(opaque); }