summaryrefslogtreecommitdiff
path: root/upb/refcount.c
diff options
context:
space:
mode:
authorJoshua Haberman <jhaberman@gmail.com>2012-03-31 12:17:32 -0700
committerJoshua Haberman <jhaberman@gmail.com>2012-03-31 12:17:32 -0700
commitcca4818eb7769d6e776bdc30516a5f871f1d6393 (patch)
treee67dd65d5c016028ae976b09b2d69f6b7525aa5f /upb/refcount.c
parent26ed1e996171c8ffa2ced42ac69b1b82c1956e1f (diff)
Sync from internal Google development.
Diffstat (limited to 'upb/refcount.c')
-rw-r--r--upb/refcount.c122
1 files changed, 67 insertions, 55 deletions
diff --git a/upb/refcount.c b/upb/refcount.c
index a15547a..d729a2a 100644
--- a/upb/refcount.c
+++ b/upb/refcount.c
@@ -6,7 +6,6 @@
*/
#include <stdlib.h>
-#include <limits.h>
#include "upb/refcount.h"
// TODO(haberman): require client to define these if ref debugging is on.
@@ -120,10 +119,55 @@ bool upb_refcount_findscc(upb_refcount **refs, int n, upb_getsuccessors *func) {
return true;
}
+#ifdef UPB_DEBUG_REFS
+static void upb_refcount_track(const upb_refcount *r, const void *owner) {
+ // Caller must not already own a ref.
+ assert(upb_inttable_lookup(r->refs, (uintptr_t)owner) == NULL);
+
+ // If a ref is leaked we want to blame the leak on the whoever leaked the
+ // ref, not on who originally allocated the refcounted object. We accomplish
+ // this as follows. When a ref is taken in DEBUG_REFS mode, we malloc() some
+ // memory and arrange setup pointers like so:
+ //
+ // upb_refcount
+ // +----------+ +---------+
+ // | count |<-+ |
+ // +----------+ +----------+
+ // | table |---X-->| malloc'd |
+ // +----------+ | memory |
+ // +----------+
+ //
+ // Since the "malloc'd memory" is allocated inside of "ref" and free'd in
+ // unref, it will cause a leak if not unref'd. And since the leaked memory
+ // points to the object itself, the object will be considered "indirectly
+ // lost" by tools like Valgrind and not shown unless requested (which is good
+ // because the object's creator may not be responsible for the leak). But we
+ // have to hide the pointer marked "X" above from Valgrind, otherwise the
+ // malloc'd memory will appear to be indirectly leaked and the object itself
+ // will still be considered the primary leak. We hide this pointer from
+ // Valgrind (et all) by doing a bitwise not on it.
+ const upb_refcount **target = malloc(sizeof(void*));
+ uintptr_t obfuscated = ~(uintptr_t)target;
+ *target = r;
+ upb_inttable_insert(r->refs, (uintptr_t)owner, upb_value_uint64(obfuscated));
+}
+
+static void upb_refcount_untrack(const upb_refcount *r, const void *owner) {
+ upb_value v;
+ bool success = upb_inttable_remove(r->refs, (uintptr_t)owner, &v);
+ assert(success);
+ if (success) {
+ // Must un-obfuscate the pointer (see above).
+ free((void*)(~upb_value_getuint64(v)));
+ }
+}
+#endif
+
/* upb_refcount **************************************************************/
-bool upb_refcount_init(upb_refcount *r, void *owner) {
+bool upb_refcount_init(upb_refcount *r, const void *owner) {
+ (void)owner;
r->count = malloc(sizeof(uint32_t));
if (!r->count) return false;
// Initializing this here means upb_refcount_findscc() can only run once for
@@ -132,7 +176,8 @@ bool upb_refcount_init(upb_refcount *r, void *owner) {
r->next = r;
#ifdef UPB_DEBUG_REFS
// We don't detect malloc() failures for UPB_DEBUG_REFS.
- upb_inttable_init(&r->refs);
+ r->refs = malloc(sizeof(*r->refs));
+ upb_inttable_init(r->refs);
*r->count = 0;
upb_refcount_ref(r, owner);
#else
@@ -144,81 +189,48 @@ bool upb_refcount_init(upb_refcount *r, void *owner) {
void upb_refcount_uninit(upb_refcount *r) {
(void)r;
#ifdef UPB_DEBUG_REFS
- assert(upb_inttable_count(&r->refs) == 0);
- upb_inttable_uninit(&r->refs);
-#endif
-}
-
-// Moves an existing ref from ref_donor to new_owner, without changing the
-// overall ref count.
-void upb_refcount_donateref(upb_refcount *r, void *from, void *to) {
- (void)r; (void)from; (void)to;
- assert(from != to);
-#ifdef UPB_DEBUG_REFS
- upb_refcount_ref(r, to);
- upb_refcount_unref(r, from);
+ assert(upb_inttable_count(r->refs) == 0);
+ upb_inttable_uninit(r->refs);
+ free(r->refs);
#endif
}
// Thread-safe operations //////////////////////////////////////////////////////
-// Ref and unref are thread-safe.
-void upb_refcount_ref(upb_refcount *r, void *owner) {
+void upb_refcount_ref(const upb_refcount *r, const void *owner) {
(void)owner;
upb_atomic_inc(r->count);
#ifdef UPB_DEBUG_REFS
UPB_LOCK;
- // Caller must not already own a ref.
- assert(upb_inttable_lookup(&r->refs, (uintptr_t)owner) == NULL);
-
- // If a ref is leaked we want to blame the leak on the whoever leaked the
- // ref, not on who originally allocated the refcounted object. We accomplish
- // this as follows. When a ref is taken in DEBUG_REFS mode, we malloc() some
- // memory and arrange setup pointers like so:
- //
- // upb_refcount
- // +----------+ +---------+
- // | count |<-+ |
- // +----------+ +----------+
- // | table |---X-->| malloc'd |
- // +----------+ | memory |
- // +----------+
- //
- // Since the "malloc'd memory" is allocated inside of "ref" and free'd in
- // unref, it will cause a leak if not unref'd. And since the leaked memory
- // points to the object itself, the object will be considered "indirectly
- // lost" by tools like Valgrind and not shown unless requested (which is good
- // because the object's creator may not be responsible for the leak). But we
- // have to hide the pointer marked "X" above from Valgrind, otherwise the
- // malloc'd memory will appear to be indirectly leaked and the object itself
- // will still be considered the primary leak. We hide this pointer from
- // Valgrind (et all) by doing a bitwise not on it.
- upb_refcount **target = malloc(sizeof(void*));
- uintptr_t obfuscated = ~(uintptr_t)target;
- *target = r;
- upb_inttable_insert(&r->refs, (uintptr_t)owner, upb_value_uint64(obfuscated));
+ upb_refcount_track(r, owner);
UPB_UNLOCK;
#endif
}
-bool upb_refcount_unref(upb_refcount *r, void *owner) {
+bool upb_refcount_unref(const upb_refcount *r, const void *owner) {
(void)owner;
bool ret = upb_atomic_dec(r->count);
#ifdef UPB_DEBUG_REFS
UPB_LOCK;
- upb_value v;
- bool success = upb_inttable_remove(&r->refs, (uintptr_t)owner, &v);
- assert(success);
- if (success) {
- // Must un-obfuscate the pointer (see above).
- free((void*)(~upb_value_getuint64(v)));
- }
+ upb_refcount_untrack(r, owner);
UPB_UNLOCK;
#endif
if (ret) free(r->count);
return ret;
}
+void upb_refcount_donateref(
+ const upb_refcount *r, const void *from, const void *to) {
+ (void)r; (void)from; (void)to;
+ assert(from != to);
+#ifdef UPB_DEBUG_REFS
+ UPB_LOCK;
+ upb_refcount_track(r, to);
+ upb_refcount_untrack(r, from);
+ UPB_UNLOCK;
+#endif
+}
+
bool upb_refcount_merged(const upb_refcount *r, const upb_refcount *r2) {
return r->count == r2->count;
}
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback