Index: src/OFBlock.m ================================================================== --- src/OFBlock.m +++ src/OFBlock.m @@ -155,11 +155,12 @@ } alloc_failed_exception; #ifndef OF_HAVE_ATOMIC_OPS # define NUM_SPINLOCKS 8 /* needs to be a power of 2 */ # define SPINLOCK_HASH(p) ((uintptr_t)p >> 4) & (NUM_SPINLOCKS - 1) -static of_spinlock_t spinlocks[NUM_SPINLOCKS]; +static of_spinlock_t blockSpinlocks[NUM_SPINLOCKS]; +static of_spinlock_t byrefSpinlocks[NUM_SPINLOCKS]; #endif void* _Block_copy(const void *block_) { @@ -188,13 +189,13 @@ #ifdef OF_HAVE_ATOMIC_OPS of_atomic_int_inc(&block->flags); #else unsigned hash = SPINLOCK_HASH(block); - OF_ENSURE(of_spinlock_lock(&spinlocks[hash])); + OF_ENSURE(of_spinlock_lock(&blockSpinlocks[hash])); block->flags++; - OF_ENSURE(of_spinlock_unlock(&spinlocks[hash])); + OF_ENSURE(of_spinlock_unlock(&blockSpinlocks[hash])); #endif } return block; } @@ -215,22 +216,22 @@ free(block); } #else unsigned hash = SPINLOCK_HASH(block); - OF_ENSURE(of_spinlock_lock(&spinlocks[hash])); + OF_ENSURE(of_spinlock_lock(&blockSpinlocks[hash])); if ((--block->flags & OF_BLOCK_REFCOUNT_MASK) == 0) { - OF_ENSURE(of_spinlock_unlock(&spinlocks[hash])); + OF_ENSURE(of_spinlock_unlock(&blockSpinlocks[hash])); if (block->flags & OF_BLOCK_HAS_COPY_DISPOSE) block->descriptor->dispose_helper(block); free(block); return; } - OF_ENSURE(of_spinlock_unlock(&spinlocks[hash])); + OF_ENSURE(of_spinlock_unlock(&blockSpinlocks[hash])); #endif } void _Block_object_assign(void *dst_, const void *src_, const int flags_) @@ -250,10 +251,12 @@ *(id*)dst_ = [(id)src_ retain]; break; case OF_BLOCK_FIELD_IS_BYREF:; of_block_byref_t *src = (of_block_byref_t*)src_; of_block_byref_t **dst = (of_block_byref_t**)dst_; + + src = src->forwarding; if ((src->flags & OF_BLOCK_REFCOUNT_MASK) == 0) { if ((*dst = malloc(src->size)) == NULL) { alloc_failed_exception.isa = [OFAllocFailedException class]; @@ -260,20 +263,51 @@ @throw (OFAllocFailedException*) &alloc_failed_exception; } memcpy(*dst, src, src->size); - - if (src == src->forwarding) - (*dst)->forwarding = *dst; + (*dst)->flags = + ((*dst)->flags & ~OF_BLOCK_REFCOUNT_MASK) | 1; + (*dst)->forwarding = *dst; if (src->flags & OF_BLOCK_HAS_COPY_DISPOSE) src->byref_keep(*dst, src); + +#ifdef OF_HAVE_ATOMIC_OPS + if (!of_atomic_ptr_cmpswap((void**)&src->forwarding, + src, *dst)) { + src->byref_dispose(*dst); + free(*dst); + + *dst = src->forwarding; + } +#else + unsigned hash = SPINLOCK_HASH(src); + + OF_ENSURE(of_spinlock_lock(&byrefSpinlocks[hash])); + if (src->forwarding == src) + src->forwarding = *dst; + else { + src->byref_dispose(*dst); + free(*dst); + + *dst = src->forwarding; + } + OF_ENSURE(of_spinlock_unlock(&byrefSpinlocks[hash])); +#endif } else *dst = src; +#ifdef OF_HAVE_ATOMIC_OPS + of_atomic_int_inc(&(*dst)->flags); +#else + unsigned hash = SPINLOCK_HASH(*dst); + + OF_ENSURE(of_spinlock_lock(&byrefSpinlocks[hash])); (*dst)->flags++; + OF_ENSURE(of_spinlock_unlock(&byrefSpinlocks[hash])); +#endif break; } } void @@ -294,26 +328,45 @@ [(id)obj_ release]; break; case OF_BLOCK_FIELD_IS_BYREF:; of_block_byref_t *obj = (of_block_byref_t*)obj_; + obj = obj->forwarding; + +#ifdef OF_HAVE_ATOMIC_OPS + if ((of_atomic_int_dec(&obj->flags) & + OF_BLOCK_REFCOUNT_MASK) == 0) { + if (obj->flags & OF_BLOCK_HAS_COPY_DISPOSE) + obj->byref_dispose(obj); + + free(obj); + } +#else + unsigned hash = SPINLOCK_HASH(obj); + + OF_ENSURE(of_spinlock_lock(&byrefSpinlocks[hash])); if ((--obj->flags & OF_BLOCK_REFCOUNT_MASK) == 0) { + OF_ENSURE(of_spinlock_unlock(&byrefSpinlocks[hash])); + if (obj->flags & OF_BLOCK_HAS_COPY_DISPOSE) obj->byref_dispose(obj); free(obj); } + OF_ENSURE(of_spinlock_unlock(&byrefSpinlocks[hash])); +#endif break; } } @implementation OFBlock + (void)load { #ifndef OF_HAVE_ATOMIC_OPS for (size_t i = 0; i < NUM_SPINLOCKS; i++) - if (!of_spinlock_new(&spinlocks[i])) + if (!of_spinlock_new(&blockSpinlocks[i]) || + !of_spinlock_new(&byrefSpinlocks[i])) @throw [OFInitializationFailedException exceptionWithClass: self]; #endif #ifdef OF_APPLE_RUNTIME Index: tests/OFBlockTests.m ================================================================== --- tests/OFBlockTests.m +++ tests/OFBlockTests.m @@ -37,14 +37,28 @@ static void (^g)() = ^ {}; static int (^returnStackBlock(void))(void) { - __block int i = 0; + __block int i = 42; return Block_copy(^ int { return ++i; }); } + +static double +forwardTest(void) +{ + __block double d; + void (^b)(void) = Block_copy(^ { + d = 5; + }); + + b(); + Block_release(b); + + return d; +} @implementation TestsAppDelegate (OFBlockTests) - (void)blockTests { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; @@ -67,12 +81,15 @@ TEST(@"Copying a stack block", (m = [[s copy] autorelease]) && [m class] == objc_getClass("OFMallocBlock") && [m isKindOfClass: [OFBlock class]]) - TEST(@"Copying a stack block and using its variable", - (v = returnStackBlock()) && v() == 1 && v() == 2 && v() == 3) + TEST(@"Copying a stack block and referencing its variable", + forwardTest() == 5) + + TEST(@"Copying a stack block and using its copied variable", + (v = returnStackBlock()) && v() == 43 && v() == 44 && v() == 45) TEST(@"Copying a global block", (id)g == [[g copy] autorelease]) TEST(@"Copying a malloc block", (id)m == [m copy] && [m retainCount] == 2)