Skip to content

Commit

Permalink
Fix autorelease pool emptying when new references are added (#218)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
Graham--M authored Dec 18, 2021
1 parent b32ee77 commit 14619f2
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 14 deletions.
1 change: 1 addition & 0 deletions Test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ set(TESTS
Category.m
ExceptionTest.m
FastARC.m
FastARCPool.m
FastRefCount.m
Forward.m
ManyManySelectors.m
Expand Down
41 changes: 41 additions & 0 deletions Test/FastARCPool.m
Original file line number Diff line number Diff line change
@@ -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;
}
28 changes: 14 additions & 14 deletions arc.mm
Original file line number Diff line number Diff line change
Expand Up @@ -169,28 +169,28 @@ static void emptyPool(struct arc_tls *tls, void *stop)
stopPool = stopPool->previous;
}
}
while (tls->pool != stopPool)
{
while (tls->pool->insert > tls->pool->pool)
do {
while (tls->pool != stopPool)
{
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);
while (tls->pool->insert > tls->pool->pool)
{
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;
tls->pool = tls->pool->previous;
free(old);
}
if (NULL != tls->pool)
{
if (NULL == tls->pool) break;
while ((stop == NULL || (tls->pool->insert > stop)) &&
(tls->pool->insert > tls->pool->pool))
{
tls->pool->insert--;
release(*tls->pool->insert);
}
}
} while (tls->pool != stopPool);
//fprintf(stderr, "New insert: %p. Stop: %p\n", tls->pool->insert, stop);
}

Expand Down

0 comments on commit 14619f2

Please sign in to comment.