/*
- * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
- * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
- *
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
__kCFBagHasCustomCallBacks = 3 /* callbacks are at end of header */
};
+enum { /* Bit 4 */
+ __kCFCollectionIsWeak = 0,
+ __kCFCollectionIsStrong = 1,
+};
+
+
struct __CFBagBucket {
const void *_key;
CFIndex _count;
/* Bits 1-0 of the base reserved bits are used for mutability variety */
/* Bits 3-2 of the base reserved bits are used for callback indicator bits */
+/* Bits 4-5 are used by GC */
+
+static bool isStrongMemory(CFTypeRef collection) {
+ return ! __CFBitfieldGetValue(((const CFRuntimeBase *)collection)->_info, 4, 4);
+}
+
+static bool needsRestore(CFTypeRef collection) {
+ return __CFBitfieldGetValue(((const CFRuntimeBase *)collection)->_info, 5, 5);
+}
+
CF_INLINE CFIndex __CFBagGetType(CFBagRef bag) {
return __CFBitfieldGetValue(((const CFRuntimeBase *)bag)->_info, 1, 0);
return;
} else if (__CFBagBucketIsDeleted(bag, currentBucket)) {
/* do nothing */
- } else if (currentBucket->_key == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(void *, void *, void *))cb->equal, currentBucket->_key, key, bag->_context))) {
+ } else if (currentBucket->_key == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(const void *, const void *, void *))cb->equal, currentBucket->_key, key, bag->_context))) {
*match = currentBucket;
return;
}
return;
} else if (__CFBagBucketIsDeleted(bag, currentBucket)) {
if (!*nomatch) *nomatch = currentBucket;
- } else if (!*match && (currentBucket->_key == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(void *, void *, void *))cb->equal, currentBucket->_key, key, bag->_context)))) {
+ } else if (!*match && (currentBucket->_key == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(const void *, const void *, void *))cb->equal, currentBucket->_key, key, bag->_context)))) {
*match = currentBucket;
- if (*nomatch) return;
+ return;
}
probe = (probe + probeskip) % bag->_bucketsNum;
if (start == probe) return;
static void __CFBagDeallocate(CFTypeRef cf) {
CFMutableBagRef bag = (CFMutableBagRef)cf;
CFAllocatorRef allocator = CFGetAllocator(bag);
+ if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) {
+ const CFBagCallBacks *cb = __CFBagGetCallBacks(bag);
+ if (cb->retain == NULL && cb->release == NULL)
+ return; // XXX_PCB keep bag intact during finalization.
+ }
if (__CFBagGetType(bag) == __kCFBagImmutable) {
__CFBitfieldSetValue(((CFRuntimeBase *)bag)->_info, 1, 0, __kCFBagFixedMutable);
}
CFBagRemoveAllValues(bag);
if (__CFBagGetType(bag) == __kCFBagMutable && bag->_buckets) {
- CFAllocatorDeallocate(allocator, bag->_buckets);
+ _CFAllocatorDeallocateGC(allocator, bag->_buckets);
}
}
static CFTypeID __kCFBagTypeID = _kCFRuntimeNotATypeID;
static const CFRuntimeClass __CFBagClass = {
- 0,
+ _kCFRuntimeScannedObject,
"CFBag",
NULL, // init
NULL, // copy
struct __CFBag *memory;
UInt32 size;
CFIndex idx;
+ CFBagCallBacks nonRetainingCallbacks;
__CFBitfieldSetValue(flags, 31, 2, 0);
+ if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) {
+ if (!callBacks || (callBacks->retain == NULL && callBacks->release == NULL)) {
+ __CFBitfieldSetValue(flags, 4, 4, 1); // setWeak
+ }
+ else {
+ if (callBacks->retain == __CFTypeCollectionRetain && callBacks->release == __CFTypeCollectionRelease) {
+ nonRetainingCallbacks = *callBacks;
+ nonRetainingCallbacks.retain = NULL;
+ nonRetainingCallbacks.release = NULL;
+ callBacks = &nonRetainingCallbacks;
+ __CFBitfieldSetValue(flags, 5, 5, 1); // setNeedsRestore
+ }
+ }
+ }
if (__CFBagCallBacksMatchNull(callBacks)) {
__CFBitfieldSetValue(flags, 3, 2, __kCFBagHasNullCallBacks);
} else if (__CFBagCallBacksMatchCFType(callBacks)) {
memory->_context = NULL;
switch (__CFBitfieldGetValue(flags, 1, 0)) {
case __kCFBagImmutable:
+ if (!isStrongMemory(memory)) { // if weak, don't scan
+ auto_zone_set_layout_type(__CFCollectableZone, memory, AUTO_OBJECT_UNSCANNED);
+ }
if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFBag (immutable)");
memory->_capacity = capacity; /* Don't round up capacity */
memory->_bucketsNum = __CFBagNumBucketsForCapacity(memory->_capacity);
}
break;
case __kCFBagFixedMutable:
+ if (!isStrongMemory(memory)) { // if weak, don't scan
+ auto_zone_set_layout_type(__CFCollectableZone, memory, AUTO_OBJECT_UNSCANNED);
+ }
if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFBag (mutable-fixed)");
memory->_capacity = capacity; /* Don't round up capacity */
memory->_bucketsNum = __CFBagNumBucketsForCapacity(memory->_capacity);
const CFBagCallBacks *cb;
CFIndex numValues = CFBagGetCount(bag);
const void **list, *buffer[256];
- list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0);
+ list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); // XXX_PCB GC OK
if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFBag (temp)");
CFBagGetValues(bag, list);
- cb = CF_IS_OBJC(__kCFBagTypeID, bag) ? &kCFTypeBagCallBacks : __CFBagGetCallBacks(bag);
+ CFBagCallBacks patchedCB;
+ if (CF_IS_OBJC(__kCFBagTypeID, bag)) {
+ cb = &kCFTypeBagCallBacks;
+ }
+ else {
+ cb = __CFBagGetCallBacks(bag);
+ if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) {
+ if (needsRestore(bag)) {
+ patchedCB = *cb; // copy
+ cb = &patchedCB; // reset to copy
+ patchedCB.retain = __CFTypeCollectionRetain;
+ patchedCB.release = __CFTypeCollectionRelease;
+ }
+ }
+ }
result = CFBagCreate(allocator, list, numValues, cb);
- if (list != buffer) CFAllocatorDeallocate(allocator, list);
+ if (list != buffer) CFAllocatorDeallocate(allocator, list); // XXX_PCB GC OK
return result;
}
CFIndex idx, numValues = CFBagGetCount(bag);
const void **list, *buffer[256];
CFAssert3(0 == capacity || numValues <= capacity, __kCFLogAssertion, "%s(): for fixed-mutable bags, capacity (%d) must be greater than or equal to initial number of values (%d)", __PRETTY_FUNCTION__, capacity, numValues);
- list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0);
+ list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); // XXX_PCB GC OK
if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFBag (temp)");
CFBagGetValues(bag, list);
- cb = CF_IS_OBJC(__kCFBagTypeID, bag) ? &kCFTypeBagCallBacks : __CFBagGetCallBacks(bag);
+ CFBagCallBacks patchedCB;
+ if (CF_IS_OBJC(__kCFBagTypeID, bag)) {
+ cb = &kCFTypeBagCallBacks;
+ }
+ else {
+ cb = __CFBagGetCallBacks(bag);
+ if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) {
+ if (needsRestore(bag)) {
+ patchedCB = *cb; // copy
+ cb = &patchedCB; // reset to copy
+ patchedCB.retain = __CFTypeCollectionRetain;
+ patchedCB.release = __CFTypeCollectionRelease;
+ }
+ }
+ }
result = CFBagCreateMutable(allocator, capacity, cb);
if (0 == capacity) _CFBagSetCapacity(result, numValues);
for (idx = 0; idx < numValues; idx++) {
CFBagAddValue(result, list[idx]);
}
- if (list != buffer) CFAllocatorDeallocate(allocator, list);
+ if (list != buffer) CFAllocatorDeallocate(allocator, list); // GC OK
return result;
}
+
void _CFBagSetContext(CFBagRef bag, void *context) {
- ((struct __CFBag *)bag)->_context = context;
+ CFAllocatorRef allocator = CFGetAllocator(bag);
+ CF_WRITE_BARRIER_BASE_ASSIGN(allocator, bag, bag->_context, context);
}
CFIndex CFBagGetCount(CFBagRef bag) {
__CFGenericValidateType(bag, __kCFBagTypeID);
if (0 == bag->_count) return false;
__CFBagFindBuckets1(bag, candidate, &match);
- return (match ? ((value ? *value = match->_key : NULL), true) : false);
+ return (match ? ((value ? __CFObjCStrongAssign(match->_key, value) : NULL), true) : false);
}
void CFBagGetValues(CFBagRef bag, const void **values) {
struct __CFBagBucket *buckets;
CFIndex idx, cnt, nbuckets;
__CFGenericValidateType(bag, __kCFBagTypeID);
+ if (CF_USING_COLLECTABLE_MEMORY) {
+ // GC: speculatively issue a write-barrier on the copied to buffers (3743553).
+ __CFObjCWriteBarrierRange(values, bag->_count * sizeof(void *));
+ }
buckets = bag->_buckets;
nbuckets = bag->_bucketsNum;
for (idx = 0; idx < nbuckets; idx++) {
struct __CFBagBucket *oldbuckets = bag->_buckets;
CFIndex idx, oldnbuckets = bag->_bucketsNum;
CFIndex oldCount = bag->_count;
+ CFAllocatorRef allocator = CFGetAllocator(bag);
bag->_capacity = __CFBagRoundUpCapacity(oldCount + numNewValues);
bag->_bucketsNum = __CFBagNumBucketsForCapacity(bag->_capacity);
- bag->_buckets = CFAllocatorAllocate(CFGetAllocator(bag), bag->_bucketsNum * sizeof(struct __CFBagBucket), 0);
+ void *bucket = _CFAllocatorAllocateGC(allocator, bag->_bucketsNum * sizeof(struct __CFBagBucket), isStrongMemory(bag) ? AUTO_MEMORY_SCANNED : AUTO_MEMORY_UNSCANNED);
+ CF_WRITE_BARRIER_BASE_ASSIGN(allocator, bag, bag->_buckets, bucket);
if (__CFOASafe) __CFSetLastAllocationEventName(bag->_buckets, "CFBag (store)");
if (NULL == bag->_buckets) HALT;
for (idx = bag->_bucketsNum; idx--;) {
struct __CFBagBucket *match, *nomatch;
__CFBagFindBuckets2(bag, oldbuckets[idx]._key, &match, &nomatch);
CFAssert3(!match, __kCFLogAssertion, "%s(): two values (%p, %p) now hash to the same slot; mutable value changed while in table or hash value is not immutable", __PRETTY_FUNCTION__, oldbuckets[idx]._key, match->_key);
- nomatch->_key = oldbuckets[idx]._key;
- nomatch->_count = oldbuckets[idx]._count;
+ if (nomatch) {
+ CF_WRITE_BARRIER_ASSIGN(allocator, nomatch->_key, oldbuckets[idx]._key);
+ nomatch->_count = oldbuckets[idx]._count;
+ }
}
}
CFAssert1(bag->_count == oldCount, __kCFLogAssertion, "%s(): bag count differs after rehashing; error", __PRETTY_FUNCTION__);
- CFAllocatorDeallocate(CFGetAllocator(bag), oldbuckets);
+ _CFAllocatorDeallocateGC(CFGetAllocator(bag), oldbuckets);
}
// This function is for Foundation's benefit; no one else should use it.
void CFBagAddValue(CFMutableBagRef bag, const void *value) {
struct __CFBagBucket *match, *nomatch;
+ CFAllocatorRef allocator;
const CFBagCallBacks *cb;
const void *newValue;
__CFGenericValidateType(bag, __kCFBagTypeID);
if (match) {
match->_count++; bag->_count++;
} else {
+ allocator = CFGetAllocator(bag);
cb = __CFBagGetCallBacks(bag);
if (cb->retain) {
- newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), CFGetAllocator(bag), value, bag->_context);
+ newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), allocator, value, bag->_context);
} else {
newValue = value;
}
if (bag->_deletedMarker == newValue) {
__CFBagFindNewDeletedMarker(bag);
}
- nomatch->_key = newValue;
+ CF_WRITE_BARRIER_ASSIGN(allocator, nomatch->_key, newValue);
nomatch->_count = 1;
bag->_bucketsUsed++;
bag->_count++;
void CFBagReplaceValue(CFMutableBagRef bag, const void *value) {
struct __CFBagBucket *match;
+ CFAllocatorRef allocator;
const CFBagCallBacks *cb;
const void *newValue;
__CFGenericValidateType(bag, __kCFBagTypeID);
if (0 == bag->_count) return;
__CFBagFindBuckets1(bag, value, &match);
if (!match) return;
+ allocator = CFGetAllocator(bag);
cb = __CFBagGetCallBacks(bag);
if (cb->retain) {
- newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), CFGetAllocator(bag), value, bag->_context);
+ newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), allocator, value, bag->_context);
} else {
newValue = value;
}
if (cb->release) {
- INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), CFGetAllocator(bag), match->_key, bag->_context);
+ INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), allocator, match->_key, bag->_context);
match->_key = bag->_deletedMarker;
}
if (bag->_emptyMarker == newValue) {
if (bag->_deletedMarker == newValue) {
__CFBagFindNewDeletedMarker(bag);
}
- match->_key = newValue;
+ CF_WRITE_BARRIER_ASSIGN(allocator, match->_key, newValue);
}
void CFBagSetValue(CFMutableBagRef bag, const void *value) {
struct __CFBagBucket *match, *nomatch;
+ CFAllocatorRef allocator;
const CFBagCallBacks *cb;
const void *newValue;
__CFGenericValidateType(bag, __kCFBagTypeID);
break;
}
__CFBagFindBuckets2(bag, value, &match, &nomatch);
+ allocator = CFGetAllocator(bag);
cb = __CFBagGetCallBacks(bag);
if (cb->retain) {
- newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), CFGetAllocator(bag), value, bag->_context);
+ newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), allocator, value, bag->_context);
} else {
newValue = value;
}
if (match) {
if (cb->release) {
- INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), CFGetAllocator(bag), match->_key, bag->_context);
+ INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), allocator, match->_key, bag->_context);
match->_key = bag->_deletedMarker;
}
if (bag->_emptyMarker == newValue) {
if (bag->_deletedMarker == newValue) {
__CFBagFindNewDeletedMarker(bag);
}
- match->_key = newValue;
+ CF_WRITE_BARRIER_ASSIGN(allocator, match->_key, newValue);
} else {
CFAssert3(__kCFBagFixedMutable != __CFBagGetType(bag) || bag->_count < bag->_capacity, __kCFLogAssertion, "%s(): capacity exceeded on fixed-capacity bag %p (capacity = %d)", __PRETTY_FUNCTION__, bag, bag->_capacity);
if (bag->_emptyMarker == newValue) {
if (bag->_deletedMarker == newValue) {
__CFBagFindNewDeletedMarker(bag);
}
- nomatch->_key = newValue;
+ CF_WRITE_BARRIER_ASSIGN(allocator, nomatch->_key, newValue);
nomatch->_count = 1;
bag->_bucketsUsed++;
bag->_count++;