Fix autorelease pool emptying when new references are added (#218)

* Add test for emptying autorelease pool

* Fix arc autorelease pool emptying when adding further references
When releasing a reference in the arc autorelease pool, it is
possible and anticipated that new references may added to the pool.
This fix addresses an edge case where releasing a reference in the
same pool page as the stop position can add more references which
cause the insertion of a new page and emptyPool() returns early
after emptying the new page.
main
Graham--M 4 years ago committed by GitHub
parent b32ee7787d
commit 14619f2905
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -27,6 +27,7 @@ set(TESTS
Category.m Category.m
ExceptionTest.m ExceptionTest.m
FastARC.m FastARC.m
FastARCPool.m
FastRefCount.m FastRefCount.m
Forward.m Forward.m
ManyManySelectors.m ManyManySelectors.m

@ -0,0 +1,41 @@
#include "Test.h"
#define POOL_SIZE (4096 / sizeof(void*))
static BOOL called;
@interface Canary : Test
@end
@implementation Canary
- (void)dealloc
{
called = YES;
[super dealloc];
}
@end
@interface Creator : Test
@end
@implementation Creator
- (void)dealloc
{
// Add a new page of autorelease references to see if we can still release
// the reference on the canary object.
for (int i = 0; i < POOL_SIZE; i++)
[[Test new] autorelease];
[super dealloc];
}
@end
int main()
{
called = NO;
@autoreleasepool
{
[[Canary new] autorelease];
[[Creator new] autorelease];
}
assert(called == YES);
return 0;
}

@ -169,28 +169,28 @@ static void emptyPool(struct arc_tls *tls, void *stop)
stopPool = stopPool->previous; stopPool = stopPool->previous;
} }
} }
while (tls->pool != stopPool) do {
{ while (tls->pool != stopPool)
while (tls->pool->insert > tls->pool->pool)
{ {
tls->pool->insert--; while (tls->pool->insert > tls->pool->pool)
// This may autorelease some other objects, so we have to work in {
// the case where the autorelease pool is extended during a -release. tls->pool->insert--;
release(*tls->pool->insert); // This may autorelease some other objects, so we have to work in
// the case where the autorelease pool is extended during a -release.
release(*tls->pool->insert);
}
void *old = tls->pool;
tls->pool = tls->pool->previous;
free(old);
} }
void *old = tls->pool; if (NULL == tls->pool) break;
tls->pool = tls->pool->previous;
free(old);
}
if (NULL != tls->pool)
{
while ((stop == NULL || (tls->pool->insert > stop)) && while ((stop == NULL || (tls->pool->insert > stop)) &&
(tls->pool->insert > tls->pool->pool)) (tls->pool->insert > tls->pool->pool))
{ {
tls->pool->insert--; tls->pool->insert--;
release(*tls->pool->insert); release(*tls->pool->insert);
} }
} } while (tls->pool != stopPool);
//fprintf(stderr, "New insert: %p. Stop: %p\n", tls->pool->insert, stop); //fprintf(stderr, "New insert: %p. Stop: %p\n", tls->pool->insert, stop);
} }

Loading…
Cancel
Save