1/* 2 * Copyright (c) 2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_APACHE_LICENSE_HEADER_START@ 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 * @APPLE_APACHE_LICENSE_HEADER_END@ 19 */ 20// 21// AssociativeRefRecovery.m 22// Copyright (c) 2008-2011 Apple Inc. All rights reserved. 23// 24 25#import <auto_zone.h> 26#import <malloc/malloc.h> 27#import <objc/runtime.h> 28 29#import "BlackBoxTest.h" 30 31/* 32 This test verifies that associatied blocks are reclaimed along with the main block. 33 It tests a Foundation object, bridged CF object, and a non-object block. 34 */ 35 36static void *_foundation_key; 37static void *_cf_bridged_key; 38static void *_non_object_key; 39 40 41@interface AssociativeRefRecovery : BlackBoxTest { 42 BOOL _main_block_finalized; 43 id _main_block; 44 vm_address_t _disguised_main_block; 45 __weak id _foundation_ref; 46 __weak CFArrayRef _cf_bridged_ref; 47 __weak void *_non_object; 48} 49 50- (void)verifyNotCollected; 51- (void)verifyCollected; 52 53@end 54 55@implementation AssociativeRefRecovery 56- (void)performTest 57{ 58 _main_block = [TestFinalizer new]; 59 _disguised_main_block = [self disguise:_main_block]; 60 61 id obj = [NSObject new]; 62 _foundation_ref = obj; 63 auto_zone_set_associative_ref([self auto_zone], _main_block, &_foundation_key, obj); 64 65 CFArrayRef cf = CFArrayCreate(NULL, NULL, 0, NULL); 66 CFMakeCollectable(cf); 67 _cf_bridged_ref = cf; 68 auto_zone_set_associative_ref([self auto_zone], (void *)_main_block, &_cf_bridged_key, (void *)cf); 69 70 void *block = malloc_zone_malloc([self auto_zone], 1); 71 _non_object = block; 72 auto_zone_set_associative_ref([self auto_zone], (void *)_main_block, &_non_object_key, block); 73 auto_zone_release([self auto_zone], block); 74 75 // Run a full collection 76 [self requestFullCollectionWithCompletionCallback:^{ [self verifyNotCollected]; }]; 77} 78 79- (void)verifyNotCollected 80{ 81 if (_main_block_finalized) 82 [self fail:@"main block was incorrectly collected"]; 83 84 if (!_foundation_ref) 85 [self fail:@"associated foundation object was incorrectly collected"]; 86 if (auto_zone_get_associative_ref([self auto_zone], _main_block, &_foundation_key) != _foundation_ref) 87 [self fail:@"failed to retrieve associated foundation object"]; 88 89 if (!_cf_bridged_ref) 90 [self fail:@"associated bridged CF object was incorrectly collected"]; 91 if (auto_zone_get_associative_ref([self auto_zone], _main_block, &_cf_bridged_key) != _cf_bridged_ref) 92 [self fail:@"failed to retrieve associated bridged CF object"]; 93 94 if (!_non_object) 95 [self fail:@"associated non-object block was incorrectly collected"]; 96 if (auto_zone_get_associative_ref([self auto_zone], _main_block, &_non_object_key) != _non_object) 97 [self fail:@"failed to retrieve associated non-object block"]; 98 99 if ([self result] != FAILED) { 100 _main_block = nil; 101 [self requestFullCollectionWithCompletionCallback:^{ [self verifyCollected]; }]; 102 } 103} 104 105- (void)verifyCollected 106{ 107 if (!_main_block_finalized) 108 [self fail:@"main block was not collected"]; 109 if (_foundation_ref) 110 [self fail:@"failed to collect block associated with foundation object"]; 111 if (_cf_bridged_ref) 112 [self fail:@"failed to collect block associated with bridged CF object"]; 113 if (_non_object) 114 [self fail:@"failed to collect block associated with non-object block"]; 115 116 [self passed]; 117 [self testFinished]; 118} 119 120- (void)didFinalize:(TestFinalizer *)finalizer 121{ 122 if (finalizer == [self undisguise:_disguised_main_block]) 123 _main_block_finalized = YES; 124} 125 126@end 127 128 129@interface AssociativeLinkedList : BlackBoxTest { 130 NSPointerArray *objects; 131 NSUInteger count; 132} 133- (void)performTest; 134- (void)verifyNotCollected; 135@end 136 137@implementation AssociativeLinkedList 138 139static char next_key; 140 141static NSData *unscannedData() { 142 const NSUInteger length = 16; 143 NSMutableData *data = [NSMutableData dataWithLength:16]; 144 uint8_t *bytes = (uint8_t *)[data mutableBytes]; 145 for (NSUInteger i = 0; i < length; ++i) { 146 *bytes++ = (uint8_t)random(); 147 } 148 return [data copyWithZone:NULL]; 149} 150 151- (void)performTest { 152 objects = [NSPointerArray pointerArrayWithOptions:NSPointerFunctionsZeroingWeakMemory]; 153 154 // created an associative linked list using unscanned objects. then verify that none of the elements have been prematurely collected. 155 id *list = auto_zone_allocate_object([self auto_zone], sizeof(id), AUTO_POINTERS_ONLY, false, true); 156 *list = unscannedData(); 157 id current = *list; 158 for (int i = 0; i < 1000; i++) { 159 id next = unscannedData(); 160 objc_setAssociatedObject(current, &next_key, next, OBJC_ASSOCIATION_ASSIGN); 161 current = next; 162 } 163 objc_setAssociatedObject(self, &next_key, (id)list, OBJC_ASSOCIATION_ASSIGN); 164 165 current = *list; 166 while (current != nil) { 167 [objects addPointer:current]; 168 current = objc_getAssociatedObject(current, &next_key); 169 } 170 count = [objects count]; 171 172 // Run a full collection 173 [self requestExhaustiveCollectionWithCompletionCallback:^{ [self verifyNotCollected]; }]; 174} 175 176- (void)verifyNotCollected { 177 [objects compact]; 178 if ([objects count] != count) { 179 [self fail:@"unscanned objects failed to stay alive across a collection."]; 180 } else { 181 [self passed]; 182 } 183 [self testFinished]; 184} 185 186@end 187