diff --git a/arc.m b/arc.m index 8ebd24f..a6e6849 100644 --- a/arc.m +++ b/arc.m @@ -201,12 +201,13 @@ extern BOOL FastARCAutorelease; static BOOL useARCAutoreleasePool; +static const long refcount_shift = 1; /** * We use the top bit of the reference count to indicate whether an object has * ever had a weak reference taken. This lets us avoid acquiring the weak * table lock for most objects on deallocation. */ -static const long weak_mask = ((size_t)1)<<((sizeof(size_t)*8)-1); +static const long weak_mask = ((size_t)1)<<((sizeof(size_t)*8)-refcount_shift); /** * All of the bits other than the top bit are the real reference count. */ @@ -780,6 +781,7 @@ PUBLIC id objc_storeWeak(id *addr, id obj) } else { + assert(ref->obj == obj); ref->weak_count++; } *addr = (id)ref; @@ -797,7 +799,11 @@ PUBLIC BOOL objc_delete_weak_refs(id obj) // have done so in between this thread's decrementing the reference // count and its acquiring the lock. In this case, report failure. uintptr_t *refCount = ((uintptr_t*)obj) - 1; - if ((long)((__sync_fetch_and_add(refCount, 0) & refcount_mask)) >= 0) + // Reconstruct the sign bit. We don't need to do this on any other + // operations, because even on increment the overflow will be correct + // after truncation. + uintptr_t refCountVal = (__sync_fetch_and_add(refCount, 0) & refcount_mask) << refcount_shift; + if ((((intptr_t)refCountVal) >> refcount_shift) >= 0) { return NO; }