From 62ed8b36d67e073fd26c2c7353a8b7cc0961be20 Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Sun, 31 May 2015 15:16:41 +0200 Subject: [PATCH 144/190] core: add "gf_ref_t" for common refcounting structures This is a backport of http://review.gluster.org/#/c/11022/ There were errors from checkpatch.pl, it is fixed as part of this backport, below are the changes: added additional parenthesis for macro definition GF_REF_INIT GF_REF_GET GF_REF_PUT > 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 Change-Id: I5630ff3fbd0e0021a83597d778e1037796be5355 BUG: 1235628 Signed-off-by: vmallika Reviewed-on: https://code.engineering.redhat.com/gerrit/51605 Reviewed-by: Raghavendra Gowdappa Tested-by: Raghavendra Gowdappa --- libglusterfs/src/Makefile.am | 4 +- libglusterfs/src/refcount.c | 112 ++++++++++++++++++++++++++++++++++++++++++ libglusterfs/src/refcount.h | 103 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 libglusterfs/src/refcount.c create mode 100644 libglusterfs/src/refcount.h diff --git a/libglusterfs/src/Makefile.am b/libglusterfs/src/Makefile.am index 80ce672..ed441d0 100644 --- a/libglusterfs/src/Makefile.am +++ b/libglusterfs/src/Makefile.am @@ -22,7 +22,7 @@ libglusterfs_la_SOURCES = dict.c xlator.c logging.c \ $(CONTRIBDIR)/rbtree/rb.c rbthash.c store.c latency.c \ graph.c syncop.c graph-print.c trie.c run.c options.c fd-lk.c \ circ-buff.c event-history.c gidcache.c ctx.c client_t.c event-poll.c \ - event-epoll.c syncop-utils.c cluster-syncop.c \ + event-epoll.c syncop-utils.c cluster-syncop.c refcount.c \ $(CONTRIBDIR)/libgen/basename_r.c \ $(CONTRIBDIR)/libgen/dirname_r.c $(CONTRIBDIR)/stdlib/gf_mkostemp.c \ strfd.c parse-utils.c $(CONTRIBDIR)/mount/mntent.c \ @@ -40,7 +40,7 @@ noinst_HEADERS = common-utils.h defaults.h dict.h glusterfs.h hashfn.h timespec. gf-dirent.h locking.h syscall.h iobuf.h globals.h statedump.h \ checksum.h daemon.h $(CONTRIBDIR)/rbtree/rb.h store.h\ rbthash.h iatt.h latency.h mem-types.h syncop.h cluster-syncop.h \ - graph-utils.h trie.h \ + graph-utils.h trie.h refcount.h \ run.h options.h lkowner.h fd-lk.h circ-buff.h event-history.h \ gidcache.h client_t.h glusterfs-acl.h glfs-message-id.h \ template-component-messages.h strfd.h syncop-utils.h parse-utils.h \ diff --git a/libglusterfs/src/refcount.c b/libglusterfs/src/refcount.c new file mode 100644 index 0000000..96edc10 --- /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; +} diff --git a/libglusterfs/src/refcount.h b/libglusterfs/src/refcount.h new file mode 100644 index 0000000..c8113a2 --- /dev/null +++ b/libglusterfs/src/refcount.h @@ -0,0 +1,103 @@ +/* + 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. +*/ + +#ifndef _REFCOUNT_H +#define _REFCOUNT_H + +/* check for compiler support for __sync_*_and_fetch() + * + * a more comprehensive feature test is shown at + * http://lists.iptel.org/pipermail/semsdev/2010-October/005075.html + * this is sufficient for RHEL5 i386 builds + */ +#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && !defined(__i386__) +#undef REFCOUNT_NEEDS_LOCK +#else +#define REFCOUNT_NEEDS_LOCK +#include "locking.h" +#endif /* compiler support for __sync_*_and_fetch() */ + +typedef void (*gf_ref_release_t)(void *data); + +struct _gf_ref_t { +#ifdef REFCOUNT_NEEDS_LOCK + gf_lock_t lk; /* lock for atomically adjust cnt */ +#endif + unsigned int cnt; /* number of users, free on 0 */ + + gf_ref_release_t release; /* cleanup when cnt == 0 */ + void *data; /* parameter passed to release() */ +}; +typedef struct _gf_ref_t gf_ref_t; + + +/* _gf_ref_get -- increase the refcount and return the number of references + * + * @return: greater then 0 when a reference was taken, 0 when not + */ +unsigned int +_gf_ref_get (gf_ref_t *ref); + +/* _gf_ref_put -- decrease the refcount and return the number of references + * + * @return: greater then 0 when there are still references, 0 when cleanup + * should be done + */ +unsigned int +_gf_ref_put (gf_ref_t *ref); + +/* _gf_ref_init -- initalize an embedded refcount object + * + * @release: function to call when the refcount == 0 + * @data: parameter to be passed to @release + */ +void +_gf_ref_init (gf_ref_t *ref, gf_ref_release_t release, void *data); + + +/* + * Strong suggestion to use the simplified GF_REF_* API. + */ + +/* GF_REF_DECL -- declaration to put inside your structure + * + * Example: + * struct my_struct { + * GF_REF_DECL; + * + * ... // additional members + * }; + */ +#define GF_REF_DECL gf_ref_t _ref + +/* GF_REF_INIT -- initialize a GF_REF_DECL structure + * + * @p: allocated structure with GF_REF_DECL + * @d: destructor to call once refcounting reaches 0 + * + * Sets the refcount to 1. + */ +#define GF_REF_INIT(p, d) (_gf_ref_init (&p->_ref, d, p)) + +/* GF_REF_GET -- increase the refcount of a GF_REF_DECL structure + * + * @return: greater then 0 when a reference was taken, 0 when not + */ +#define GF_REF_GET(p) (_gf_ref_get (&p->_ref)) + +/* GF_REF_PUT -- decrease the refcount of a GF_REF_DECL structure + * + * @return: greater then 0 when there are still references, 0 when cleanup + * should be done + */ +#define GF_REF_PUT(p) (_gf_ref_put (&p->_ref)) + + +#endif /* _REFCOUNT_H */ -- 1.7.1