summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorJustus Winter <4winter@informatik.uni-hamburg.de>2014-05-06 19:52:04 +0200
committerJustus Winter <4winter@informatik.uni-hamburg.de>2014-05-22 07:53:38 +0200
commite30e04d4f5cef2af6400847b5e102b5877372f27 (patch)
tree2cf267b1181584c8206c776d06f4529db10b4b74 /include
parent689b3f9b8fb68218515c05b3b7b13ff4e21c6c76 (diff)
include: add lock-less reference counting primitives
* include/refcount.h: New file.
Diffstat (limited to 'include')
-rw-r--r--include/refcount.h263
1 files changed, 263 insertions, 0 deletions
diff --git a/include/refcount.h b/include/refcount.h
new file mode 100644
index 00000000..5c3302de
--- /dev/null
+++ b/include/refcount.h
@@ -0,0 +1,263 @@
+/* Lock-less reference counting primitives
+
+ Copyright (C) 2014 Free Software Foundation, Inc.
+
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _HURD_REFCOUNT_H_
+#define _HURD_REFCOUNT_H_
+
+#include <assert.h>
+#include <limits.h>
+#include <stdint.h>
+
+/* Simple reference counting. */
+
+/* An opaque type. You must not access these values directly. */
+typedef unsigned int refcount_t;
+
+/* Initialize REF with REFERENCES. */
+static inline void
+refcount_init (refcount_t *ref, unsigned int references)
+{
+ *ref = references;
+}
+
+/* Increment REF. Return the result of the operation. This function
+ uses atomic operations. It is not required to serialize calls to
+ this function. */
+static inline unsigned int
+refcount_ref (refcount_t *ref)
+{
+ unsigned int r;
+ r = __atomic_add_fetch (ref, 1, __ATOMIC_RELAXED);
+ assert (r != UINT_MAX || !"refcount overflowed!");
+ return r;
+}
+
+/* Decrement REF. Return the result of the operation. This function
+ uses atomic operations. It is not required to serialize calls to
+ this function. */
+static inline unsigned int
+refcount_deref (refcount_t *ref)
+{
+ unsigned int r;
+ r = __atomic_sub_fetch (ref, 1, __ATOMIC_RELAXED);
+ assert (r != UINT_MAX || !"refcount underflowed!");
+ return r;
+}
+
+/* Return REF. This function uses atomic operations. It is not
+ required to serialize calls to this function. */
+static inline unsigned int
+refcount_references (refcount_t *ref)
+{
+ return __atomic_load_n (ref, __ATOMIC_RELAXED);
+}
+
+/* Reference counting with weak references. */
+
+/* An opaque type. You must not access these values directly. */
+typedef union _references refcounts_t;
+
+/* Instead, the functions manipulating refcounts_t values write the
+ results into this kind of objects. */
+struct references {
+ /* We chose the layout of this struct so that when it is used in the
+ union _references, the hard reference counts occupy the least
+ significant bits. We rely on this layout for atomic promotion
+ and demotion of references. See refcounts_promote and
+ refcounts_demote for details. */
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ uint32_t hard;
+ uint32_t weak;
+#else
+ uint32_t weak;
+ uint32_t hard;
+#endif
+};
+
+/* We use a union to convert struct reference values to uint64_t which
+ we can manipulate atomically. While this behavior is not
+ guaranteed by the C standard, it is supported by all major
+ compilers. */
+union _references {
+ struct references references;
+ uint64_t value;
+};
+
+/* Initialize REF with HARD and WEAK references. */
+static inline void
+refcounts_init (refcounts_t *ref, uint32_t hard, uint32_t weak)
+{
+ ref->references = (struct references) { .hard = hard, .weak = weak };
+}
+
+/* Increment the hard reference count of REF. If RESULT is not NULL,
+ the result of the operation is written there. This function uses
+ atomic operations. It is not required to serialize calls to this
+ function. */
+static inline void
+refcounts_ref (refcounts_t *ref, struct references *result)
+{
+ const union _references op = { .references = { .hard = 1 } };
+ union _references r;
+ r.value = __atomic_add_fetch (&ref->value, op.value, __ATOMIC_RELAXED);
+ assert (r.references.hard != UINT32_MAX || !"refcount overflowed!");
+ if (result)
+ *result = r.references;
+}
+
+/* Decrement the hard reference count of REF. If RESULT is not NULL,
+ the result of the operation is written there. This function uses
+ atomic operations. It is not required to serialize calls to this
+ function. */
+static inline void
+refcounts_deref (refcounts_t *ref, struct references *result)
+{
+ const union _references op = { .references = { .hard = 1 } };
+ union _references r;
+ r.value = __atomic_sub_fetch (&ref->value, op.value, __ATOMIC_RELAXED);
+ assert (r.references.hard != UINT32_MAX || !"refcount underflowed!");
+ if (result)
+ *result = r.references;
+}
+
+/* Promote a weak reference to a hard reference. If RESULT is not
+ NULL, the result of the operation is written there. This function
+ uses atomic operations. It is not required to serialize calls to
+ this function. */
+static inline void
+refcounts_promote (refcounts_t *ref, struct references *result)
+{
+ /* To promote a weak reference, we need to atomically subtract 1
+ from the weak reference count, and add 1 to the hard reference
+ count.
+
+ We can subtract by 1 by adding the two's complement of 1 = ~0 to
+ a fixed-width value, discarding the overflow.
+
+ We do the same in our uint64_t value, but we have chosen the
+ layout of struct references so that when it is used in the union
+ _references, the weak reference counts occupy the most
+ significant bits. When we add ~0 to the weak references, the
+ overflow will be discarded as unsigned arithmetic is modulo 2^n.
+ So we just add a hard reference. In combination, this is the
+ desired operation. */
+ const union _references op =
+ { .references = { .weak = ~0, .hard = 1} };
+ union _references r;
+ r.value = __atomic_add_fetch (&ref->value, op.value, __ATOMIC_RELAXED);
+ assert (r.references.hard != UINT32_MAX || !"refcount overflowed!");
+ assert (r.references.weak != UINT32_MAX || !"refcount underflowed!");
+ if (result)
+ *result = r.references;
+}
+
+/* Demote a hard reference to a weak reference. If RESULT is not
+ NULL, the result of the operation is written there. This function
+ uses atomic operations. It is not required to serialize calls to
+ this function. */
+static inline void
+refcounts_demote (refcounts_t *ref, struct references *result)
+{
+ /* To demote a hard reference, we need to atomically subtract 1 from
+ the hard reference count, and add 1 to the weak reference count.
+
+ We can subtract by 1 by adding the two's complement of 1 = ~0 to
+ a fixed-width value, discarding the overflow.
+
+ We do the same in our uint64_t value, but we have chosen the
+ layout of struct references so that when it is used in the union
+ _references, the hard reference counts occupy the least
+ significant bits. When we add ~0 to the hard references, it will
+ overflow into the weak references. This is the desired
+ operation. */
+ const union _references op = { .references = { .hard = ~0 } };
+ union _references r;
+ r.value = __atomic_add_fetch (&ref->value, op.value, __ATOMIC_RELAXED);
+ assert (r.references.hard != UINT32_MAX || !"refcount underflowed!");
+ assert (r.references.weak != UINT32_MAX || !"refcount overflowed!");
+ if (result)
+ *result = r.references;
+}
+
+/* Increment the weak reference count of REF. If RESULT is not NULL,
+ the result of the operation is written there. This function uses
+ atomic operations. It is not required to serialize calls to this
+ function. */
+static inline void
+refcounts_ref_weak (refcounts_t *ref, struct references *result)
+{
+ const union _references op = { .references = { .weak = 1 } };
+ union _references r;
+ r.value = __atomic_add_fetch (&ref->value, op.value, __ATOMIC_RELAXED);
+ assert (r.references.weak != UINT32_MAX || !"refcount overflowed!");
+ if (result)
+ *result = r.references;
+}
+
+/* Decrement the weak reference count of REF. If RESULT is not NULL,
+ the result of the operation is written there. This function uses
+ atomic operations. It is not required to serialize calls to this
+ function. */
+static inline void
+refcounts_deref_weak (refcounts_t *ref, struct references *result)
+{
+ const union _references op = { .references = { .weak = 1 } };
+ union _references r;
+ r.value = __atomic_sub_fetch (&ref->value, op.value, __ATOMIC_RELAXED);
+ assert (r.references.weak != UINT32_MAX || !"refcount underflowed!");
+ if (result)
+ *result = r.references;
+}
+
+/* Store the current reference counts of REF in RESULT. This function
+ uses atomic operations. It is not required to serialize calls to
+ this function. */
+static inline void
+refcounts_references (refcounts_t *ref, struct references *result)
+{
+ union _references r;
+ r.value =__atomic_load_n (&ref->value, __ATOMIC_RELAXED);
+ *result = r.references;
+}
+
+/* Return the hard reference count of REF. This function uses atomic
+ operations. It is not required to serialize calls to this
+ function. */
+static inline uint32_t
+refcounts_hard_references (refcounts_t *ref)
+{
+ struct references result;
+ refcounts_references (ref, &result);
+ return result.hard;
+}
+
+/* Return the weak reference count of REF. This function uses atomic
+ operations. It is not required to serialize calls to this
+ function. */
+static inline uint32_t
+refcounts_weak_references (refcounts_t *ref)
+{
+ struct references result;
+ refcounts_references (ref, &result);
+ return result.weak;
+}
+
+#endif /* _HURD_REFCOUNT_H_ */