// API entry point to release a copied Block void _Block_release(const void *arg) { struct Block_layout *aBlock = (struct Block_layout *)arg; if (!aBlock || (aBlock->flags & BLOCK_IS_GLOBAL) || ((aBlock->flags & (BLOCK_IS_GC|BLOCK_NEEDS_FREE)) == 0) ) return; if (aBlock->flags & BLOCK_IS_GC) { if (latching_decr_int_now_zero(&aBlock->flags)) { // Tell GC we no longer have our own refcounts. GC will decr its refcount // and unless someone has done a CFRetain or marked it uncollectable it will // now be subject to GC reclamation. _Block_setHasRefcount(aBlock, false); } } else if (aBlock->flags & BLOCK_NEEDS_FREE) { if (latching_decr_int_should_deallocate(&aBlock->flags)) { _Block_call_dispose_helper(aBlock); _Block_destructInstance(aBlock); _Block_deallocator(aBlock); } } }
// API entry point to release a copied Block void _Block_release(void *arg) { struct Block_layout *aBlock = (struct Block_layout *)arg; int32_t newCount; if (!aBlock) return; newCount = latching_decr_int(&aBlock->flags) & BLOCK_REFCOUNT_MASK; if (newCount > 0) return; // Hit zero if (aBlock->flags & BLOCK_IS_GC) { // Tell GC we no longer have our own refcounts. GC will decr its refcount // and unless someone has done a CFRetain or marked it uncollectable it will // now be subject to GC reclamation. _Block_setHasRefcount(aBlock, false); } else if (aBlock->flags & BLOCK_NEEDS_FREE) { if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)(*aBlock->descriptor->dispose)(aBlock); _Block_deallocator(aBlock); } else if (aBlock->flags & BLOCK_IS_GLOBAL) { ; } else { printf("Block_release called upon a stack Block: %p, ignored\n", (void *)aBlock); } }
/* Copy, or bump refcount, of a block. If really copying, call the copy helper if present. */ static void *_Block_copy_internal(const void *arg, const int flags) { struct Block_layout *aBlock; const bool wantsOne = (WANTS_ONE & flags) == WANTS_ONE; //printf("_Block_copy_internal(%p, %x)\n", arg, flags); if (!arg) return NULL; // The following would be better done as a switch statement aBlock = (struct Block_layout *)arg; if (aBlock->flags & BLOCK_NEEDS_FREE) { // latches on high latching_incr_int(&aBlock->flags); return aBlock; } else if (aBlock->flags & BLOCK_IS_GC) { // GC refcounting is expensive so do most refcounting here. if (wantsOne && ((latching_incr_int(&aBlock->flags) & BLOCK_REFCOUNT_MASK) == 1)) { // Tell collector to hang on this - it will bump the GC refcount version _Block_setHasRefcount(aBlock, true); } return aBlock; } else if (aBlock->flags & BLOCK_IS_GLOBAL) { return aBlock; } // Its a stack block. Make a copy. if (!isGC) { struct Block_layout *result = malloc(aBlock->descriptor->size); if (!result) return (void *)0; memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first // reset refcount result->flags &= ~(BLOCK_REFCOUNT_MASK); // XXX not needed result->flags |= BLOCK_NEEDS_FREE | 1; result->isa = _NSConcreteMallocBlock; if (result->flags & BLOCK_HAS_COPY_DISPOSE) { //printf("calling block copy helper %p(%p, %p)...\n", aBlock->descriptor->copy, result, aBlock); (*aBlock->descriptor->copy)(result, aBlock); // do fixup } return result; } else { // Under GC want allocation with refcount 1 so we ask for "true" if wantsOne // This allows the copy helper routines to make non-refcounted block copies under GC unsigned long int flags = aBlock->flags; bool hasCTOR = (flags & BLOCK_HAS_CTOR) != 0; struct Block_layout *result = _Block_allocator(aBlock->descriptor->size, wantsOne, hasCTOR); if (!result) return (void *)0; memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first // reset refcount // if we copy a malloc block to a GC block then we need to clear NEEDS_FREE. flags &= ~(BLOCK_NEEDS_FREE|BLOCK_REFCOUNT_MASK); // XXX not needed if (wantsOne) flags |= BLOCK_IS_GC | 1; else flags |= BLOCK_IS_GC; result->flags = flags; if (flags & BLOCK_HAS_COPY_DISPOSE) { //printf("calling block copy helper...\n"); (*aBlock->descriptor->copy)(result, aBlock); // do fixup } if (hasCTOR) { result->isa = _NSConcreteFinalizingBlock; } else { result->isa = _NSConcreteAutoBlock; } return result; } }
// Copy, or bump refcount, of a block. If really copying, call the copy helper if present. static void *_Block_copy_internal(const void *arg, const bool wantsOne) { struct Block_layout *aBlock; if (!arg) return NULL; // The following would be better done as a switch statement aBlock = (struct Block_layout *)arg; if (aBlock->flags & BLOCK_NEEDS_FREE) { // latches on high latching_incr_int(&aBlock->flags); return aBlock; } else if (aBlock->flags & BLOCK_IS_GC) { // GC refcounting is expensive so do most refcounting here. if (wantsOne && ((latching_incr_int(&aBlock->flags) & BLOCK_REFCOUNT_MASK) == 2)) { // Tell collector to hang on this - it will bump the GC refcount version _Block_setHasRefcount(aBlock, true); } return aBlock; } else if (aBlock->flags & BLOCK_IS_GLOBAL) { return aBlock; } // Its a stack block. Make a copy. if (!isGC) { struct Block_layout *result = malloc(aBlock->descriptor->size); if (!result) return NULL; memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first // reset refcount result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING); // XXX not needed result->flags |= BLOCK_NEEDS_FREE | 2; // logical refcount 1 result->isa = _NSConcreteMallocBlock; _Block_call_copy_helper(result, aBlock); return result; } else { // Under GC want allocation with refcount 1 so we ask for "true" if wantsOne // This allows the copy helper routines to make non-refcounted block copies under GC int32_t flags = aBlock->flags; bool hasCTOR = (flags & BLOCK_HAS_CTOR) != 0; struct Block_layout *result = _Block_allocator(aBlock->descriptor->size, wantsOne, hasCTOR || _Block_has_layout(aBlock)); if (!result) return NULL; memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first // reset refcount // if we copy a malloc block to a GC block then we need to clear NEEDS_FREE. flags &= ~(BLOCK_NEEDS_FREE|BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING); // XXX not needed if (wantsOne) flags |= BLOCK_IS_GC | 2; else flags |= BLOCK_IS_GC; result->flags = flags; _Block_call_copy_helper(result, aBlock); if (hasCTOR) { result->isa = _NSConcreteFinalizingBlock; } else { result->isa = _NSConcreteAutoBlock; } return result; } }