rr-autorelease2.m 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. // Define FOUNDATION=1 for NSObject and NSAutoreleasePool
  2. // Define FOUNDATION=0 for _objc_root* and _objc_autoreleasePool*
  3. #include "test.h"
  4. #if FOUNDATION
  5. # define RR_PUSH() [[NSAutoreleasePool alloc] init]
  6. # define RR_POP(p) [(id)p release]
  7. # define RR_RETAIN(o) [o retain]
  8. # define RR_RELEASE(o) [o release]
  9. # define RR_AUTORELEASE(o) [o autorelease]
  10. # define RR_RETAINCOUNT(o) [o retainCount]
  11. #else
  12. # define RR_PUSH() _objc_autoreleasePoolPush()
  13. # define RR_POP(p) _objc_autoreleasePoolPop(p)
  14. # define RR_RETAIN(o) _objc_rootRetain((id)o)
  15. # define RR_RELEASE(o) _objc_rootRelease((id)o)
  16. # define RR_AUTORELEASE(o) _objc_rootAutorelease((id)o)
  17. # define RR_RETAINCOUNT(o) _objc_rootRetainCount((id)o)
  18. #endif
  19. #include <objc/objc-internal.h>
  20. #include <Foundation/Foundation.h>
  21. static int state;
  22. static pthread_attr_t smallstack;
  23. #define NESTED_COUNT 8
  24. @interface Deallocator : NSObject @end
  25. @implementation Deallocator
  26. -(void) dealloc
  27. {
  28. // testprintf("-[Deallocator %p dealloc]\n", self);
  29. state++;
  30. [super dealloc];
  31. }
  32. @end
  33. @interface AutoreleaseDuringDealloc : NSObject @end
  34. @implementation AutoreleaseDuringDealloc
  35. -(void) dealloc
  36. {
  37. state++;
  38. RR_AUTORELEASE([[Deallocator alloc] init]);
  39. [super dealloc];
  40. }
  41. @end
  42. @interface AutoreleasePoolDuringDealloc : NSObject @end
  43. @implementation AutoreleasePoolDuringDealloc
  44. -(void) dealloc
  45. {
  46. // caller's pool
  47. for (int i = 0; i < NESTED_COUNT; i++) {
  48. RR_AUTORELEASE([[Deallocator alloc] init]);
  49. }
  50. // local pool, popped
  51. void *pool = RR_PUSH();
  52. for (int i = 0; i < NESTED_COUNT; i++) {
  53. RR_AUTORELEASE([[Deallocator alloc] init]);
  54. }
  55. RR_POP(pool);
  56. // caller's pool again
  57. for (int i = 0; i < NESTED_COUNT; i++) {
  58. RR_AUTORELEASE([[Deallocator alloc] init]);
  59. }
  60. #if FOUNDATION
  61. {
  62. static bool warned;
  63. if (!warned) testwarn("rdar://7138159 NSAutoreleasePool leaks");
  64. warned = true;
  65. }
  66. state += NESTED_COUNT;
  67. #else
  68. // local pool, not popped
  69. RR_PUSH();
  70. for (int i = 0; i < NESTED_COUNT; i++) {
  71. RR_AUTORELEASE([[Deallocator alloc] init]);
  72. }
  73. #endif
  74. [super dealloc];
  75. }
  76. @end
  77. void *autorelease_lots_fn(void *singlePool)
  78. {
  79. // Enough to blow out the stack if AutoreleasePoolPage is recursive.
  80. const int COUNT = 1024*1024;
  81. state = 0;
  82. int p = 0;
  83. void **pools = (void**)malloc((COUNT+1) * sizeof(void*));
  84. pools[p++] = RR_PUSH();
  85. id obj = RR_AUTORELEASE([[Deallocator alloc] init]);
  86. // last pool has only 1 autorelease in it
  87. pools[p++] = RR_PUSH();
  88. for (int i = 0; i < COUNT; i++) {
  89. if (rand() % 1000 == 0 && !singlePool) {
  90. pools[p++] = RR_PUSH();
  91. } else {
  92. RR_AUTORELEASE(RR_RETAIN(obj));
  93. }
  94. }
  95. testassert(state == 0);
  96. while (--p) {
  97. RR_POP(pools[p]);
  98. }
  99. testassert(state == 0);
  100. testassert(RR_RETAINCOUNT(obj) == 1);
  101. RR_POP(pools[0]);
  102. testassert(state == 1);
  103. free(pools);
  104. return NULL;
  105. }
  106. void *nsthread_fn(void *arg __unused)
  107. {
  108. [NSThread currentThread];
  109. void *pool = RR_PUSH();
  110. RR_AUTORELEASE([[Deallocator alloc] init]);
  111. RR_POP(pool);
  112. return NULL;
  113. }
  114. void cycle(void)
  115. {
  116. // Normal autorelease.
  117. testprintf("-- Normal autorelease.\n");
  118. {
  119. void *pool = RR_PUSH();
  120. state = 0;
  121. RR_AUTORELEASE([[Deallocator alloc] init]);
  122. testassert(state == 0);
  123. RR_POP(pool);
  124. testassert(state == 1);
  125. }
  126. // Autorelease during dealloc during autoreleasepool-pop.
  127. // That autorelease is handled by the popping pool, not the one above it.
  128. testprintf("-- Autorelease during dealloc during autoreleasepool-pop.\n");
  129. {
  130. void *pool = RR_PUSH();
  131. state = 0;
  132. RR_AUTORELEASE([[AutoreleaseDuringDealloc alloc] init]);
  133. testassert(state == 0);
  134. RR_POP(pool);
  135. testassert(state == 2);
  136. }
  137. // Autorelease pool during dealloc during autoreleasepool-pop.
  138. testprintf("-- Autorelease pool during dealloc during autoreleasepool-pop.\n");
  139. {
  140. void *pool = RR_PUSH();
  141. state = 0;
  142. RR_AUTORELEASE([[AutoreleasePoolDuringDealloc alloc] init]);
  143. testassert(state == 0);
  144. RR_POP(pool);
  145. testassert(state == 4 * NESTED_COUNT);
  146. }
  147. // Top-level thread pool popped normally.
  148. // Check twice - once for empty placeholder, once without.
  149. # if DEBUG_POOL_ALLOCATION || FOUNDATION
  150. // DebugPoolAllocation disables the empty placeholder pool.
  151. // Guard Malloc disables the empty placeholder pool (checked at runtime)
  152. // Foundation makes RR_PUSH return an NSAutoreleasePool not the raw token.
  153. # define CHECK_PLACEHOLDER 0
  154. # else
  155. # define CHECK_PLACEHOLDER 1
  156. # endif
  157. testprintf("-- Thread-level pool popped normally.\n");
  158. {
  159. state = 0;
  160. testonthread(^{
  161. void *pool = RR_PUSH();
  162. #if CHECK_PLACEHOLDER
  163. if (!is_guardmalloc()) {
  164. testassert(pool == (void*)1);
  165. }
  166. #endif
  167. RR_AUTORELEASE([[Deallocator alloc] init]);
  168. RR_POP(pool);
  169. pool = RR_PUSH();
  170. #if CHECK_PLACEHOLDER
  171. if (!is_guardmalloc()) {
  172. testassert(pool != (void*)1);
  173. }
  174. #endif
  175. RR_AUTORELEASE([[Deallocator alloc] init]);
  176. RR_POP(pool);
  177. });
  178. testassert(state == 2);
  179. }
  180. // Autorelease with no pool.
  181. testprintf("-- Autorelease with no pool.\n");
  182. {
  183. state = 0;
  184. testonthread(^{
  185. RR_AUTORELEASE([[Deallocator alloc] init]);
  186. });
  187. testassert(state == 1);
  188. }
  189. // Autorelease with no pool after popping the top-level pool.
  190. testprintf("-- Autorelease with no pool after popping the last pool.\n");
  191. {
  192. state = 0;
  193. testonthread(^{
  194. void *pool = RR_PUSH();
  195. RR_AUTORELEASE([[Deallocator alloc] init]);
  196. RR_POP(pool);
  197. RR_AUTORELEASE([[Deallocator alloc] init]);
  198. });
  199. testassert(state == 2);
  200. }
  201. // Top-level thread pool not popped.
  202. // The runtime should clean it up.
  203. #if FOUNDATION
  204. {
  205. static bool warned;
  206. if (!warned) testwarn("rdar://7138159 NSAutoreleasePool leaks");
  207. warned = true;
  208. }
  209. #else
  210. testprintf("-- Thread-level pool not popped.\n");
  211. {
  212. state = 0;
  213. testonthread(^{
  214. RR_PUSH();
  215. RR_AUTORELEASE([[Deallocator alloc] init]);
  216. // pool not popped
  217. });
  218. testassert(state == 1);
  219. }
  220. #endif
  221. // Intermediate pool not popped.
  222. // Popping the containing pool should clean up the skipped pool first.
  223. #if FOUNDATION
  224. {
  225. static bool warned;
  226. if (!warned) testwarn("rdar://7138159 NSAutoreleasePool leaks");
  227. warned = true;
  228. }
  229. #else
  230. testprintf("-- Intermediate pool not popped.\n");
  231. {
  232. void *pool = RR_PUSH();
  233. void *pool2 = RR_PUSH();
  234. RR_AUTORELEASE([[Deallocator alloc] init]);
  235. state = 0;
  236. (void)pool2; // pool2 not popped
  237. RR_POP(pool);
  238. testassert(state == 1);
  239. }
  240. #endif
  241. }
  242. static void
  243. slow_cycle(void)
  244. {
  245. // Large autorelease stack.
  246. // Do this only once because it's slow.
  247. testprintf("-- Large autorelease stack.\n");
  248. {
  249. // limit stack size: autorelease pop should not be recursive
  250. pthread_t th;
  251. pthread_create(&th, &smallstack, &autorelease_lots_fn, NULL);
  252. pthread_join(th, NULL);
  253. }
  254. // Single large autorelease pool.
  255. // Do this only once because it's slow.
  256. testprintf("-- Large autorelease pool.\n");
  257. {
  258. // limit stack size: autorelease pop should not be recursive
  259. pthread_t th;
  260. pthread_create(&th, &smallstack, &autorelease_lots_fn, (void*)1);
  261. pthread_join(th, NULL);
  262. }
  263. }
  264. int main()
  265. {
  266. pthread_attr_init(&smallstack);
  267. pthread_attr_setstacksize(&smallstack, 32768);
  268. // inflate the refcount side table so it doesn't show up in leak checks
  269. {
  270. int count = 10000;
  271. id *objs = (id *)malloc(count*sizeof(id));
  272. for (int i = 0; i < count; i++) {
  273. objs[i] = RR_RETAIN([NSObject new]);
  274. }
  275. for (int i = 0; i < count; i++) {
  276. RR_RELEASE(objs[i]);
  277. RR_RELEASE(objs[i]);
  278. }
  279. free(objs);
  280. }
  281. #if FOUNDATION
  282. // inflate NSAutoreleasePool's instance cache
  283. {
  284. int count = 32;
  285. id *objs = (id *)malloc(count * sizeof(id));
  286. for (int i = 0; i < count; i++) {
  287. objs[i] = [[NSAutoreleasePool alloc] init];
  288. }
  289. for (int i = 0; i < count; i++) {
  290. [objs[count-i-1] release];
  291. }
  292. free(objs);
  293. }
  294. #endif
  295. // preheat
  296. {
  297. for (int i = 0; i < 100; i++) {
  298. cycle();
  299. }
  300. slow_cycle();
  301. }
  302. // check for leaks using top-level pools
  303. {
  304. leak_mark();
  305. for (int i = 0; i < 1000; i++) {
  306. cycle();
  307. }
  308. leak_check(0);
  309. slow_cycle();
  310. leak_check(0);
  311. }
  312. // check for leaks using pools not at top level
  313. // fixme for FOUNDATION this leak mark/check needs
  314. // to be outside the autorelease pool for some reason
  315. leak_mark();
  316. void *pool = RR_PUSH();
  317. {
  318. for (int i = 0; i < 1000; i++) {
  319. cycle();
  320. }
  321. slow_cycle();
  322. }
  323. RR_POP(pool);
  324. leak_check(0);
  325. // NSThread.
  326. // Can't leak check this because it's too noisy.
  327. testprintf("-- NSThread.\n");
  328. {
  329. pthread_t th;
  330. pthread_create(&th, &smallstack, &nsthread_fn, 0);
  331. pthread_join(th, NULL);
  332. }
  333. // NO LEAK CHECK HERE
  334. succeed(NAME);
  335. }