From cca4818eb7769d6e776bdc30516a5f871f1d6393 Mon Sep 17 00:00:00 2001 From: Joshua Haberman Date: Sat, 31 Mar 2012 12:17:32 -0700 Subject: Sync from internal Google development. --- upb/refcount.c | 122 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 67 insertions(+), 55 deletions(-) (limited to 'upb/refcount.c') 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 -#include #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; } -- cgit v1.2.3