From ea1d2b0045e2c2ad82a2162a58f9fc36f9d07b20 Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Sun, 31 May 2015 15:16:41 +0200 Subject: core: add "gf_ref_t" for common refcounting structures Checks for compiler supported atomic operations comes from client_t.h. An example usage of this change can be found in adding reference counting to "struct auth_cache_entry" in http://review.gluster.org/11023 Basic usage looks like this: #include "refcount.h" struct my_struct { GF_REF_DECL; ... /* more members */ } void my_destructor (void *data) { struct my_struct *my_ptr = (struct my_struct *) data; ... /* do some more cleanups */ GF_FREE (my_ptr); } void init_ptr (struct parent *parent) { struct my_struct *my_ptr = malloc (sizeof (struct my_struct)); GF_REF_INIT (my_ptr, my_destructor); /* refcount is set to 1 */ ... /* my_ptr probably gets added to some parent structure */ parent_add_ptr (parent, my_ptr); } void do_something (struct parent *parent) { struct my_struct *my_ptr = NULL; /* likely need to lock parent, depends on its access pattern */ my_ptr = parent_remove_first_ptr (parent); /* unlock parent */ ... /* do something */ GF_REF_PUT (my_ptr); /* calls my_destructor on refcount = 0 */ } URL: http://thread.gmane.org/gmane.comp.file-systems.gluster.devel/11202 Change-Id: Idb98a5861a44c31676108ed8876db12c320912ef BUG: 1228157 Signed-off-by: Niels de Vos Reviewed-on: http://review.gluster.org/11022 Reviewed-by: Xavier Hernandez Reviewed-by: Krishnan Parthasarathi Tested-by: Gluster Build System Tested-by: NetBSD Build System --- libglusterfs/src/refcount.c | 112 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 libglusterfs/src/refcount.c (limited to 'libglusterfs/src/refcount.c') diff --git a/libglusterfs/src/refcount.c b/libglusterfs/src/refcount.c new file mode 100644 index 00000000000..96edc10982a --- /dev/null +++ b/libglusterfs/src/refcount.c @@ -0,0 +1,112 @@ +/* + Copyright (c) 2015 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#include "common-utils.h" +#include "refcount.h" + +#ifndef REFCOUNT_NEEDS_LOCK + +unsigned int +_gf_ref_get (gf_ref_t *ref) +{ + unsigned int cnt = __sync_fetch_and_add (&ref->cnt, 1); + + /* if cnt == 0, we're in a fatal position, the object will be free'd + * + * There is a race when two threads do a _gf_ref_get(). Only one of + * them may get a 0 returned. That is acceptible, because one + * _gf_ref_get() returning 0 should be handled as a fatal problem and + * when correct usage/locking is used, it should never happen. + */ + GF_ASSERT (cnt != 0); + + return cnt; +} + +unsigned int +_gf_ref_put (gf_ref_t *ref) +{ + unsigned int cnt = __sync_fetch_and_sub (&ref->cnt, 1); + + /* if cnt == 1, the last user just did a _gf_ref_put() + * + * When cnt == 0, one _gf_ref_put() was done too much and there has + * been a thread using the refcounted structure when it was not + * supposed to. + */ + GF_ASSERT (cnt != 0); + + if (cnt == 1 && ref->release) { + ref->release (ref->data); + /* set return value to 0 to inform the caller correctly */ + cnt = 0; + } + + return cnt; +} + +#else + +unsigned int +_gf_ref_get (gf_ref_t *ref) +{ + unsigned int cnt = 0; + + LOCK (&ref->lk); + { + /* never can be 0, should have been free'd */ + if (ref->cnt > 0) + cnt = ++ref->cnt; + else + GF_ASSERT (ref->cnt > 0); + } + UNLOCK (&ref->lk); + + return cnt; +} + +unsigned int +_gf_ref_put (gf_ref_t *ref) +{ + unsigned int cnt = 0; + int release = 0; + + LOCK (&ref->lk); + { + if (ref->cnt != 0) { + cnt = --ref->cnt; + /* call release() only when cnt == 0 */ + release = (cnt == 0); + } else + GF_ASSERT (ref->cnt != 0); + } + UNLOCK (&ref->lk); + + if (release && ref->release) + ref->release (ref->data); + + return cnt; +} + +#endif /* REFCOUNT_NEEDS_LOCK */ + + +void +_gf_ref_init (gf_ref_t *ref, gf_ref_release_t release, void *data) +{ + GF_ASSERT (ref); + +#ifdef REFCOUNT_NEEDS_LOCK + LOCK_INIT (&ref->lk); +#endif + ref->cnt = 1; + ref->release = release; + ref->data = data; +} -- cgit