Use the runtime's ARC autorelease pools if available.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@35031 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
David Chisnall 2012-04-06 12:02:59 +00:00
parent b1449b2cba
commit b9684ff5a7
3 changed files with 165 additions and 52 deletions

View file

@ -34,6 +34,13 @@
#import "Foundation/NSException.h"
#import "Foundation/NSThread.h"
#if __has_include(<objc/capabilities.h>)
# include <objc/capabilities.h>
# ifdef OBJC_ARC_AUTORELEASE_DEBUG
# include <objc/objc-arc.h>
# define ARC_RUNTIME 1
# endif
#endif
#if GS_WITH_GC || __OBJC_GC__
@ -209,6 +216,8 @@ static Class PoolClass;
#endif
#if !GS_WITH_GC
/* When this is `NO', autoreleased objects are never actually recorded
@ -333,6 +342,73 @@ pop_pool_from_cache (struct autorelease_thread_vars *tv)
return (*initImp)(arp, @selector(init));
}
#ifdef ARC_RUNTIME
- (id) init
{
_released = objc_autoreleasePoolPush();
{
struct autorelease_thread_vars *tv = ARP_THREAD_VARS;
unsigned level = 0;
_parent = tv->current_pool;
if (_parent)
{
NSAutoreleasePool *pool = _parent;
while (nil != pool)
{
level++;
pool = pool->_parent;
}
_parent->_child = self;
}
tv->current_pool = self;
if (level > pool_number_warning_threshhold)
{
[NSException raise: NSGenericException
format: @"Too many (%u) autorelease pools ... leaking them?", level];
}
}
return self;
}
- (unsigned) autoreleaseCountForObject: (id)anObject
{
return objc_arc_autorelease_count_for_object_np(anObject);
}
+ (unsigned) autoreleaseCountForObject: (id)anObject
{
return objc_arc_autorelease_count_for_object_np(anObject);
}
- (unsigned) autoreleaseCount
{
return objc_arc_autorelease_count_np();
}
+ (void) addObject: (id)anObj
{
if (autorelease_enabled)
objc_autorelease(anObj);
}
- (void) addObject: (id)anObj
{
if (autorelease_enabled)
objc_autorelease(anObj);
}
- (void) emptyPool
{
objc_autoreleasePoolPop(_released);
}
/**
* Indicate to the runtime that we have an ARC-compatible implementation of
* NSAutoreleasePool and that it doesn't need to bother creating objects for
* pools.
*/
- (void)_ARCCompatibleAutoreleasePool {}
#else
- (id) init
{
if (!_released_head)
@ -429,11 +505,6 @@ pop_pool_from_cache (struct autorelease_thread_vars *tv)
return count;
}
+ (id) currentPool
{
return ARP_THREAD_VARS->current_pool;
}
+ (void) addObject: (id)anObj
{
NSThread *t = GSCurrentThread();
@ -512,52 +583,6 @@ pop_pool_from_cache (struct autorelease_thread_vars *tv)
_released_count++;
}
- (void) drain
{
// Don't call -release, make both -release and -drain have the same cost in
// non-GC mode.
[self dealloc];
}
- (id) retain
{
[NSException raise: NSGenericException
format: @"Don't call `-retain' on a NSAutoreleasePool"];
return self;
}
- (oneway void) release
{
[self dealloc];
}
- (void) dealloc
{
struct autorelease_thread_vars *tv = ARP_THREAD_VARS;
[self emptyPool];
/* Remove self from the linked list of pools in use.
* We already know that we have deallocated any child (in -emptyPool),
* but we may have a parent which needs to know we have gone.
* The only other place where the parent/child linked list is modified
* should be in -init
*/
if (tv->current_pool == self)
{
tv->current_pool = _parent;
}
if (_parent != nil)
{
_parent->_child = nil;
_parent = nil;
}
/* Don't deallocate ourself, just save us for later use. */
push_pool_to_cache (tv, self);
GSNOSUPERDEALLOC;
}
- (void) emptyPool
{
unsigned i;
@ -662,6 +687,60 @@ pop_pool_from_cache (struct autorelease_thread_vars *tv)
}
}
#endif // ARC_RUNTIME
+ (id) currentPool
{
return ARP_THREAD_VARS->current_pool;
}
- (void) drain
{
// Don't call -release, make both -release and -drain have the same cost in
// non-GC mode.
[self dealloc];
}
- (id) retain
{
[NSException raise: NSGenericException
format: @"Don't call `-retain' on a NSAutoreleasePool"];
return self;
}
- (oneway void) release
{
[self dealloc];
}
- (void) dealloc
{
struct autorelease_thread_vars *tv = ARP_THREAD_VARS;
[self emptyPool];
/* Remove self from the linked list of pools in use.
* We already know that we have deallocated any child (in -emptyPool),
* but we may have a parent which needs to know we have gone.
* The only other place where the parent/child linked list is modified
* should be in -init
*/
if (tv->current_pool == self)
{
tv->current_pool = _parent;
}
if (_parent != nil)
{
_parent->_child = nil;
_parent = nil;
}
/* Don't deallocate ourself, just save us for later use. */
push_pool_to_cache (tv, self);
GSNOSUPERDEALLOC;
}
- (void) _reallyDealloc
{
struct autorelease_array_list *a;
@ -735,4 +814,4 @@ pop_pool_from_cache (struct autorelease_thread_vars *tv)
}
@end
#endif
#endif // !GS_WITH_GC

View file

View file

@ -0,0 +1,34 @@
#import "ObjectTesting.h"
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSObject.h>
static BOOL freed;
@interface Test : NSObject @end
@implementation Test
- (void)dealloc
{
freed = YES;
[super dealloc];
}
@end
int main()
{
NSAutoreleasePool *arp = [NSAutoreleasePool new];
NSObject *o = [NSObject new];
for (unsigned i=0 ; i<1000 ; i++)
{
[[o retain] autorelease];
}
NSUInteger totalCount = [arp autoreleaseCount];
PASS(totalCount == 1000, "Autorelease count is correct");
PASS([NSAutoreleasePool autoreleaseCountForObject: o] == 1000,
"Autorelease count for object is correct");
PASS(freed == NO, "Object not prematurely freed");
[arp release];
arp = [NSAutoreleasePool new];
[o release];
PASS(freed == NO, "Object freed by autoreleasing");
[arp release];
return 0;
}