2001-12-17 14:31:42 +00:00
|
|
|
|
/** Implementation of auto release pool for delayed disposal
|
1997-03-03 19:56:37 +00:00
|
|
|
|
Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
1996-04-17 20:17:45 +00:00
|
|
|
|
Written by: Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
|
2003-06-15 05:18:27 +00:00
|
|
|
|
Written by: Richard Frith-Macdonald <rfm@gnu.org>
|
1995-03-12 19:35:17 +00:00
|
|
|
|
Date: January 1995
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
1996-05-12 00:56:10 +00:00
|
|
|
|
This file is part of the GNUstep Base Library.
|
1995-03-12 19:35:17 +00:00
|
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
2007-09-14 11:36:11 +00:00
|
|
|
|
modify it under the terms of the GNU Lesser General Public
|
1995-03-12 19:35:17 +00:00
|
|
|
|
License as published by the Free Software Foundation; either
|
2008-06-08 10:38:33 +00:00
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
1995-03-12 19:35:17 +00:00
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
2019-12-09 23:36:00 +00:00
|
|
|
|
Lesser General Public License for more details.
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
2007-09-14 11:36:11 +00:00
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
1995-03-12 19:35:17 +00:00
|
|
|
|
License along with this library; if not, write to the Free
|
2024-11-07 13:37:59 +00:00
|
|
|
|
Software Foundation, Inc., 31 Milk Street #960789 Boston, MA 02196 USA.
|
2001-12-18 16:54:15 +00:00
|
|
|
|
|
|
|
|
|
<title>NSAutoreleasePool class reference</title>
|
|
|
|
|
$Date$ $Revision$
|
2005-02-22 11:22:44 +00:00
|
|
|
|
*/
|
1995-03-12 19:35:17 +00:00
|
|
|
|
|
2010-02-19 08:12:46 +00:00
|
|
|
|
#import "common.h"
|
2010-02-14 10:48:10 +00:00
|
|
|
|
#define EXPOSE_NSAutoreleasePool_IVARS 1
|
|
|
|
|
#define EXPOSE_NSThread_IVARS 1
|
2010-02-17 11:47:06 +00:00
|
|
|
|
#import "Foundation/NSAutoreleasePool.h"
|
|
|
|
|
#import "Foundation/NSException.h"
|
|
|
|
|
#import "Foundation/NSThread.h"
|
1995-03-12 19:35:17 +00:00
|
|
|
|
|
2012-04-06 12:02:59 +00:00
|
|
|
|
#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
|
2011-05-28 14:51:40 +00:00
|
|
|
|
|
|
|
|
|
|
2012-04-06 12:02:59 +00:00
|
|
|
|
|
|
|
|
|
|
2024-11-21 14:35:54 +00:00
|
|
|
|
#define LOG_LIFETIME 0
|
|
|
|
|
|
1996-03-30 22:39:48 +00:00
|
|
|
|
/* When this is `NO', autoreleased objects are never actually recorded
|
|
|
|
|
in an NSAutoreleasePool, and are not sent a `release' message.
|
|
|
|
|
Thus memory for objects use grows, and grows, and... */
|
1995-04-14 15:01:24 +00:00
|
|
|
|
static BOOL autorelease_enabled = YES;
|
|
|
|
|
|
1996-07-15 18:41:44 +00:00
|
|
|
|
/* When the _released_count of a pool gets over this value, we raise
|
2016-05-18 06:11:00 +00:00
|
|
|
|
an exception. This can be adjusted with +setPoolCountThreshold */
|
|
|
|
|
static unsigned pool_count_warning_threshold = UINT_MAX-1;
|
1995-04-14 15:01:24 +00:00
|
|
|
|
|
2011-05-24 10:52:25 +00:00
|
|
|
|
/* When the number of pools in a thread gets over this value, we raise
|
2016-05-18 06:11:00 +00:00
|
|
|
|
an exception. This can be adjusted with +setPoolNumberThreshold */
|
|
|
|
|
static unsigned pool_number_warning_threshold = 10000;
|
2011-05-24 10:52:25 +00:00
|
|
|
|
|
1996-03-30 22:39:48 +00:00
|
|
|
|
/* The size of the first _released array. */
|
|
|
|
|
#define BEGINNING_POOL_SIZE 32
|
|
|
|
|
|
1996-07-15 18:41:44 +00:00
|
|
|
|
/* Easy access to the thread variables belonging to NSAutoreleasePool. */
|
2010-02-14 10:48:10 +00:00
|
|
|
|
#define ARP_THREAD_VARS (&((GSCurrentThread())->_autorelease_vars))
|
1996-07-15 18:41:44 +00:00
|
|
|
|
|
1996-03-30 22:39:48 +00:00
|
|
|
|
|
|
|
|
|
@interface NSAutoreleasePool (Private)
|
2000-09-30 05:16:53 +00:00
|
|
|
|
+ (unsigned) autoreleaseCountForObject: (id)anObject;
|
2001-05-10 20:00:02 +00:00
|
|
|
|
- (void) _reallyDealloc;
|
1996-03-30 22:39:48 +00:00
|
|
|
|
@end
|
|
|
|
|
|
1996-07-15 18:41:44 +00:00
|
|
|
|
|
|
|
|
|
/* Functions for managing a per-thread cache of NSAutoreleasedPool's
|
2005-02-22 11:22:44 +00:00
|
|
|
|
already alloc'ed. The cache is kept in the autorelease_thread_var
|
1996-07-15 18:41:44 +00:00
|
|
|
|
structure, which is an ivar of NSThread. */
|
|
|
|
|
|
2024-11-21 14:35:54 +00:00
|
|
|
|
static id pop_pool_from_cache(struct autorelease_thread_vars *tv);
|
2001-05-10 20:00:02 +00:00
|
|
|
|
|
|
|
|
|
static inline void
|
2024-11-21 14:35:54 +00:00
|
|
|
|
free_pool_cache(struct autorelease_thread_vars *tv)
|
2001-05-10 20:00:02 +00:00
|
|
|
|
{
|
|
|
|
|
while (tv->pool_cache_count)
|
|
|
|
|
{
|
|
|
|
|
NSAutoreleasePool *pool = pop_pool_from_cache(tv);
|
|
|
|
|
|
|
|
|
|
[pool _reallyDealloc];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tv->pool_cache)
|
|
|
|
|
{
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), tv->pool_cache);
|
|
|
|
|
tv->pool_cache = 0;
|
2004-11-03 13:20:39 +00:00
|
|
|
|
tv->pool_cache_size = 0;
|
2001-05-10 20:00:02 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1996-07-15 18:41:44 +00:00
|
|
|
|
static inline void
|
2024-11-21 14:35:54 +00:00
|
|
|
|
init_pool_cache(struct autorelease_thread_vars *tv)
|
1996-07-15 18:41:44 +00:00
|
|
|
|
{
|
|
|
|
|
tv->pool_cache_size = 32;
|
|
|
|
|
tv->pool_cache_count = 0;
|
1999-09-28 11:10:34 +00:00
|
|
|
|
tv->pool_cache = (id*)NSZoneMalloc(NSDefaultMallocZone(),
|
|
|
|
|
sizeof(id) * tv->pool_cache_size);
|
1996-07-15 18:41:44 +00:00
|
|
|
|
}
|
1996-03-30 22:39:48 +00:00
|
|
|
|
|
|
|
|
|
static void
|
2024-11-21 14:35:54 +00:00
|
|
|
|
push_pool_to_cache(struct autorelease_thread_vars *tv, id p)
|
1996-03-30 22:39:48 +00:00
|
|
|
|
{
|
1996-07-15 18:41:44 +00:00
|
|
|
|
if (!tv->pool_cache)
|
2001-05-10 20:00:02 +00:00
|
|
|
|
{
|
2024-11-21 14:35:54 +00:00
|
|
|
|
init_pool_cache(tv);
|
2001-05-10 20:00:02 +00:00
|
|
|
|
}
|
1996-07-15 18:41:44 +00:00
|
|
|
|
else if (tv->pool_cache_count == tv->pool_cache_size)
|
1996-03-30 22:39:48 +00:00
|
|
|
|
{
|
1996-07-15 18:41:44 +00:00
|
|
|
|
tv->pool_cache_size *= 2;
|
1999-09-28 11:10:34 +00:00
|
|
|
|
tv->pool_cache = (id*)NSZoneRealloc(NSDefaultMallocZone(),
|
|
|
|
|
tv->pool_cache, sizeof(id) * tv->pool_cache_size);
|
1996-03-30 22:39:48 +00:00
|
|
|
|
}
|
1996-07-15 18:41:44 +00:00
|
|
|
|
tv->pool_cache[tv->pool_cache_count++] = p;
|
1996-03-30 22:39:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static id
|
2024-11-21 14:35:54 +00:00
|
|
|
|
pop_pool_from_cache(struct autorelease_thread_vars *tv)
|
1996-03-30 22:39:48 +00:00
|
|
|
|
{
|
1996-07-15 18:41:44 +00:00
|
|
|
|
return tv->pool_cache[--(tv->pool_cache_count)];
|
1996-03-30 22:39:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
1995-03-12 19:35:17 +00:00
|
|
|
|
@implementation NSAutoreleasePool
|
|
|
|
|
|
1996-03-30 22:39:48 +00:00
|
|
|
|
+ (void) initialize
|
|
|
|
|
{
|
2011-02-11 16:04:05 +00:00
|
|
|
|
/* Do nothing here which might interact with ther classes since this
|
|
|
|
|
* is called by [NSObject+initialize] !
|
|
|
|
|
*/
|
|
|
|
|
return;
|
1996-03-30 22:39:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-09-30 05:16:53 +00:00
|
|
|
|
+ (id) allocWithZone: (NSZone*)zone
|
1996-03-30 22:39:48 +00:00
|
|
|
|
{
|
1996-07-15 18:41:44 +00:00
|
|
|
|
struct autorelease_thread_vars *tv = ARP_THREAD_VARS;
|
2024-11-21 14:35:54 +00:00
|
|
|
|
NSAutoreleasePool *p;
|
2016-04-28 20:35:00 +00:00
|
|
|
|
|
1996-07-15 18:41:44 +00:00
|
|
|
|
if (tv->pool_cache_count)
|
2016-04-28 20:35:00 +00:00
|
|
|
|
{
|
2024-11-21 14:35:54 +00:00
|
|
|
|
p = pop_pool_from_cache(tv);
|
1996-03-30 22:39:48 +00:00
|
|
|
|
|
2016-04-29 07:31:48 +00:00
|
|
|
|
/* When we cache a 'deallocated' pool, we set its _released_count to
|
|
|
|
|
* UINT_MAX, so when we rtrieve it fromm the cache we must increment
|
|
|
|
|
* it to start with a count of zero.
|
|
|
|
|
*/
|
|
|
|
|
if (++(p->_released_count) != 0)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
|
format: @"NSAutoreleasePool corrupted pool in cache"];
|
|
|
|
|
}
|
2016-04-28 20:35:00 +00:00
|
|
|
|
return p;
|
|
|
|
|
}
|
2024-11-21 14:35:54 +00:00
|
|
|
|
p = (NSAutoreleasePool*)NSAllocateObject (self, 0, zone);
|
|
|
|
|
|
|
|
|
|
#if LOG_LIFETIME
|
|
|
|
|
fprintf(stderr, "*** %p autorelease pool allocated in %p\n",
|
|
|
|
|
p, GSCurrentThread());
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return p;
|
1996-03-30 22:39:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-09-30 05:16:53 +00:00
|
|
|
|
+ (id) new
|
1998-11-20 13:44:59 +00:00
|
|
|
|
{
|
2011-02-11 16:04:05 +00:00
|
|
|
|
static IMP allocImp = 0;
|
|
|
|
|
static IMP initImp = 0;
|
|
|
|
|
id arp;
|
|
|
|
|
|
|
|
|
|
if (0 == allocImp)
|
|
|
|
|
{
|
|
|
|
|
allocImp
|
|
|
|
|
= [NSAutoreleasePool methodForSelector: @selector(allocWithZone:)];
|
|
|
|
|
initImp
|
|
|
|
|
= [NSAutoreleasePool instanceMethodForSelector: @selector(init)];
|
|
|
|
|
}
|
|
|
|
|
arp = (*allocImp)(self, @selector(allocWithZone:), NSDefaultMallocZone());
|
1998-11-20 13:44:59 +00:00
|
|
|
|
return (*initImp)(arp, @selector(init));
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-21 14:35:54 +00:00
|
|
|
|
- (void) _emptyChild
|
|
|
|
|
{
|
|
|
|
|
/* If there are NSAutoreleasePool below us in the list of
|
|
|
|
|
* NSAutoreleasePools, then deallocate them also.
|
|
|
|
|
* The (only) way we could get in this situation (in correctly
|
|
|
|
|
* written programs, that don't release NSAutoreleasePools in
|
|
|
|
|
* weird ways), is if an exception threw us up the stack.
|
|
|
|
|
* However, if a program has leaked pools we may be deallocating
|
|
|
|
|
* a pool with LOTS of children. To avoid stack overflow we
|
|
|
|
|
* therefore deallocate children starting with the oldest first.
|
|
|
|
|
*/
|
|
|
|
|
if (nil != _child)
|
|
|
|
|
{
|
|
|
|
|
NSAutoreleasePool *pool = _child;
|
|
|
|
|
|
|
|
|
|
/* Find other end of linked list ... oldest child.
|
|
|
|
|
*/
|
|
|
|
|
while (nil != pool->_child)
|
|
|
|
|
{
|
|
|
|
|
pool = pool->_child;
|
|
|
|
|
}
|
|
|
|
|
/* Deallocate the children in the list.
|
|
|
|
|
*/
|
|
|
|
|
while (pool != self)
|
|
|
|
|
{
|
|
|
|
|
pool = pool->_parent;
|
|
|
|
|
[pool->_child dealloc];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-06 12:02:59 +00:00
|
|
|
|
#ifdef ARC_RUNTIME
|
|
|
|
|
|
|
|
|
|
- (id) init
|
|
|
|
|
{
|
|
|
|
|
_released = objc_autoreleasePoolPush();
|
|
|
|
|
{
|
|
|
|
|
struct autorelease_thread_vars *tv = ARP_THREAD_VARS;
|
|
|
|
|
unsigned level = 0;
|
2016-04-29 07:31:48 +00:00
|
|
|
|
|
2012-04-06 12:02:59 +00:00
|
|
|
|
_parent = tv->current_pool;
|
|
|
|
|
if (_parent)
|
|
|
|
|
{
|
|
|
|
|
NSAutoreleasePool *pool = _parent;
|
|
|
|
|
|
2024-11-21 14:35:54 +00:00
|
|
|
|
_child = _parent->_child;
|
2012-04-06 12:02:59 +00:00
|
|
|
|
while (nil != pool)
|
|
|
|
|
{
|
|
|
|
|
level++;
|
|
|
|
|
pool = pool->_parent;
|
|
|
|
|
}
|
|
|
|
|
_parent->_child = self;
|
|
|
|
|
}
|
|
|
|
|
tv->current_pool = self;
|
2016-05-18 06:11:00 +00:00
|
|
|
|
if (level > pool_number_warning_threshold)
|
2012-04-06 12:02:59 +00:00
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSGenericException
|
|
|
|
|
format: @"Too many (%u) autorelease pools ... leaking them?", level];
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-11-15 16:34:54 +00:00
|
|
|
|
|
|
|
|
|
/* Catch the case where the receiver is a pool still in use (wrongly put in
|
|
|
|
|
the pool cache previously). */
|
|
|
|
|
NSCAssert(_child != self, @"Invalid child pool");
|
|
|
|
|
NSCAssert(_parent != self, @"Invalid parent pool");
|
|
|
|
|
|
2012-04-06 12:02:59 +00:00
|
|
|
|
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
|
|
|
|
|
{
|
2012-04-23 12:47:09 +00:00
|
|
|
|
struct autorelease_thread_vars *tv = ARP_THREAD_VARS;
|
2024-11-21 14:35:54 +00:00
|
|
|
|
|
|
|
|
|
if (nil != _child)
|
|
|
|
|
{
|
|
|
|
|
[self _emptyChild];
|
|
|
|
|
}
|
2012-04-06 12:02:59 +00:00
|
|
|
|
objc_autoreleasePoolPop(_released);
|
2024-11-21 14:35:54 +00:00
|
|
|
|
tv->current_pool = self;
|
2012-04-06 12:02:59 +00:00
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 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
|
1999-09-28 11:10:34 +00:00
|
|
|
|
- (id) init
|
1996-03-30 22:39:48 +00:00
|
|
|
|
{
|
|
|
|
|
if (!_released_head)
|
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
|
_addImp = (void (*)(id, SEL, id))
|
1999-05-06 20:22:16 +00:00
|
|
|
|
[self methodForSelector: @selector(addObject:)];
|
1996-03-30 22:39:48 +00:00
|
|
|
|
/* Allocate the array that will be the new head of the list of arrays. */
|
|
|
|
|
_released = (struct autorelease_array_list*)
|
1999-09-28 11:10:34 +00:00
|
|
|
|
NSZoneMalloc(NSDefaultMallocZone(),
|
|
|
|
|
sizeof(struct autorelease_array_list)
|
|
|
|
|
+ (BEGINNING_POOL_SIZE * sizeof(id)));
|
1996-03-30 22:39:48 +00:00
|
|
|
|
/* Currently no NEXT array in the list, so NEXT == NULL. */
|
|
|
|
|
_released->next = NULL;
|
|
|
|
|
_released->size = BEGINNING_POOL_SIZE;
|
|
|
|
|
_released->count = 0;
|
|
|
|
|
_released_head = _released;
|
2005-03-06 15:08:12 +00:00
|
|
|
|
_released_count = 0;
|
1996-03-30 22:39:48 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
/* Already initialized; (it came from autorelease_pool_cache);
|
|
|
|
|
we don't have to allocate new array list memory. */
|
|
|
|
|
{
|
|
|
|
|
_released = _released_head;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-24 06:24:05 +00:00
|
|
|
|
/* Install ourselves as the current pool.
|
|
|
|
|
* The only other place where the parent/child linked list is modified
|
|
|
|
|
* should be in -dealloc
|
|
|
|
|
*/
|
1996-07-15 18:41:44 +00:00
|
|
|
|
{
|
|
|
|
|
struct autorelease_thread_vars *tv = ARP_THREAD_VARS;
|
2011-05-24 10:52:25 +00:00
|
|
|
|
unsigned level = 0;
|
2024-11-21 14:35:54 +00:00
|
|
|
|
|
1996-07-15 18:41:44 +00:00
|
|
|
|
_parent = tv->current_pool;
|
1999-05-06 19:37:45 +00:00
|
|
|
|
if (_parent)
|
2011-05-24 10:52:25 +00:00
|
|
|
|
{
|
|
|
|
|
NSAutoreleasePool *pool = _parent;
|
|
|
|
|
|
2024-11-21 14:35:54 +00:00
|
|
|
|
_child = _parent->_child;
|
2011-05-24 10:52:25 +00:00
|
|
|
|
while (nil != pool)
|
|
|
|
|
{
|
|
|
|
|
level++;
|
|
|
|
|
pool = pool->_parent;
|
|
|
|
|
}
|
|
|
|
|
_parent->_child = self;
|
|
|
|
|
}
|
1996-07-15 18:41:44 +00:00
|
|
|
|
tv->current_pool = self;
|
2016-05-18 06:11:00 +00:00
|
|
|
|
if (level > pool_number_warning_threshold)
|
2011-05-24 10:52:25 +00:00
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSGenericException
|
|
|
|
|
format: @"Too many (%u) autorelease pools ... leaking them?", level];
|
|
|
|
|
}
|
1996-07-15 18:41:44 +00:00
|
|
|
|
}
|
1996-03-30 22:39:48 +00:00
|
|
|
|
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
1995-04-14 15:01:24 +00:00
|
|
|
|
- (unsigned) autoreleaseCount
|
|
|
|
|
{
|
1996-03-30 22:39:48 +00:00
|
|
|
|
unsigned count = 0;
|
|
|
|
|
struct autorelease_array_list *released = _released_head;
|
2007-01-30 18:05:34 +00:00
|
|
|
|
while (released != 0)
|
1996-03-30 22:39:48 +00:00
|
|
|
|
{
|
|
|
|
|
count += released->count;
|
|
|
|
|
released = released->next;
|
|
|
|
|
}
|
|
|
|
|
return count;
|
1995-04-14 15:01:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-09-30 05:16:53 +00:00
|
|
|
|
- (unsigned) autoreleaseCountForObject: (id)anObject
|
1995-04-14 15:01:24 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned count = 0;
|
1996-03-30 22:39:48 +00:00
|
|
|
|
struct autorelease_array_list *released = _released_head;
|
2003-01-03 20:14:47 +00:00
|
|
|
|
unsigned int i;
|
1995-04-14 15:01:24 +00:00
|
|
|
|
|
2007-01-30 18:05:34 +00:00
|
|
|
|
while (released != 0)
|
1996-03-30 22:39:48 +00:00
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < released->count; i++)
|
|
|
|
|
if (released->objects[i] == anObject)
|
|
|
|
|
count++;
|
|
|
|
|
released = released->next;
|
|
|
|
|
}
|
1995-04-14 15:01:24 +00:00
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
2000-09-30 05:16:53 +00:00
|
|
|
|
+ (unsigned) autoreleaseCountForObject: (id)anObject
|
1995-04-14 15:01:24 +00:00
|
|
|
|
{
|
1995-08-02 15:40:33 +00:00
|
|
|
|
unsigned count = 0;
|
2005-03-06 15:08:12 +00:00
|
|
|
|
NSAutoreleasePool *pool = ARP_THREAD_VARS->current_pool;
|
|
|
|
|
|
1995-04-14 15:01:24 +00:00
|
|
|
|
while (pool)
|
|
|
|
|
{
|
1996-03-30 22:39:48 +00:00
|
|
|
|
count += [pool autoreleaseCountForObject: anObject];
|
2005-03-06 15:08:12 +00:00
|
|
|
|
pool = pool->_parent;
|
1995-04-14 15:01:24 +00:00
|
|
|
|
}
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
2000-09-30 05:16:53 +00:00
|
|
|
|
+ (void) addObject: (id)anObj
|
1995-03-12 19:35:17 +00:00
|
|
|
|
{
|
2010-02-14 10:48:10 +00:00
|
|
|
|
NSThread *t = GSCurrentThread();
|
2006-01-19 06:15:27 +00:00
|
|
|
|
NSAutoreleasePool *pool;
|
2011-07-11 14:31:36 +00:00
|
|
|
|
NSAssert(nil != t, @"Creating autorelease pool on nonexistent thread!");
|
1998-10-01 03:05:27 +00:00
|
|
|
|
|
2006-01-20 17:12:33 +00:00
|
|
|
|
pool = t->_autorelease_vars.current_pool;
|
|
|
|
|
if (pool == nil && t->_active == NO)
|
2006-01-19 06:15:27 +00:00
|
|
|
|
{
|
2011-03-09 10:24:18 +00:00
|
|
|
|
// Don't leak while exiting thread.
|
|
|
|
|
pool = t->_autorelease_vars.current_pool = [self new];
|
2006-01-19 06:15:27 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (pool != nil)
|
|
|
|
|
{
|
|
|
|
|
(*pool->_addImp)(pool, @selector(addObject:), anObj);
|
|
|
|
|
}
|
1998-10-01 03:05:27 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
NSAutoreleasePool *arp = [NSAutoreleasePool new];
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (anObj != nil)
|
|
|
|
|
{
|
2010-02-14 10:48:10 +00:00
|
|
|
|
NSLog(@"autorelease called without pool for object (%p) "
|
2002-01-30 15:28:50 +00:00
|
|
|
|
@"of class %@ in thread %@", anObj,
|
|
|
|
|
NSStringFromClass([anObj class]), [NSThread currentThread]);
|
|
|
|
|
}
|
1998-10-01 03:05:27 +00:00
|
|
|
|
else
|
2002-01-30 15:28:50 +00:00
|
|
|
|
{
|
|
|
|
|
NSLog(@"autorelease called without pool for nil object.");
|
|
|
|
|
}
|
2011-05-27 12:00:57 +00:00
|
|
|
|
[arp drain];
|
1998-10-01 03:05:27 +00:00
|
|
|
|
}
|
1995-03-12 19:35:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-09-30 05:16:53 +00:00
|
|
|
|
- (void) addObject: (id)anObj
|
1995-03-12 19:35:17 +00:00
|
|
|
|
{
|
1996-03-30 22:39:48 +00:00
|
|
|
|
/* If the global, static variable AUTORELEASE_ENABLED is not set,
|
|
|
|
|
do nothing, just return. */
|
1995-04-14 15:01:24 +00:00
|
|
|
|
if (!autorelease_enabled)
|
|
|
|
|
return;
|
|
|
|
|
|
2016-05-18 06:11:00 +00:00
|
|
|
|
if (_released_count >= pool_count_warning_threshold)
|
2016-04-29 07:31:48 +00:00
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSGenericException
|
2016-05-18 06:11:00 +00:00
|
|
|
|
format: @"AutoreleasePool count threshold exceeded."];
|
2016-04-29 07:31:48 +00:00
|
|
|
|
}
|
1995-04-14 15:01:24 +00:00
|
|
|
|
|
1996-03-30 22:39:48 +00:00
|
|
|
|
/* Get a new array for the list, if the current one is full. */
|
2005-03-06 15:08:12 +00:00
|
|
|
|
while (_released->count == _released->size)
|
1995-03-12 19:35:17 +00:00
|
|
|
|
{
|
1996-03-30 22:39:48 +00:00
|
|
|
|
if (_released->next)
|
|
|
|
|
{
|
|
|
|
|
/* There is an already-allocated one in the chain; use it. */
|
|
|
|
|
_released = _released->next;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* We are at the end of the chain, and need to allocate a new one. */
|
|
|
|
|
struct autorelease_array_list *new_released;
|
|
|
|
|
unsigned new_size = _released->size * 2;
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
1996-03-30 22:39:48 +00:00
|
|
|
|
new_released = (struct autorelease_array_list*)
|
1999-09-28 11:10:34 +00:00
|
|
|
|
NSZoneMalloc(NSDefaultMallocZone(),
|
|
|
|
|
sizeof(struct autorelease_array_list) + (new_size * sizeof(id)));
|
1996-03-30 22:39:48 +00:00
|
|
|
|
new_released->next = NULL;
|
|
|
|
|
new_released->size = new_size;
|
|
|
|
|
new_released->count = 0;
|
|
|
|
|
_released->next = new_released;
|
|
|
|
|
_released = new_released;
|
|
|
|
|
}
|
1995-03-12 19:35:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
1996-03-30 22:39:48 +00:00
|
|
|
|
/* Put the object at the end of the list. */
|
|
|
|
|
_released->objects[_released->count] = anObj;
|
|
|
|
|
(_released->count)++;
|
|
|
|
|
|
|
|
|
|
/* Keep track of the total number of objects autoreleased in this pool */
|
|
|
|
|
_released_count++;
|
1995-03-12 19:35:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-06-17 17:20:22 +00:00
|
|
|
|
- (void) emptyPool
|
|
|
|
|
{
|
2005-03-06 15:08:12 +00:00
|
|
|
|
unsigned i;
|
|
|
|
|
Class classes[16];
|
|
|
|
|
IMP imps[16];
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 16; i++)
|
|
|
|
|
{
|
|
|
|
|
classes[i] = 0;
|
|
|
|
|
imps[i] = 0;
|
|
|
|
|
}
|
2005-02-28 17:18:54 +00:00
|
|
|
|
|
|
|
|
|
/*
|
2005-03-06 15:08:12 +00:00
|
|
|
|
* Loop throught the deallocation code repeatedly ... since we deallocate
|
|
|
|
|
* objects in the receiver while the receiver remains set as the current
|
|
|
|
|
* autorelease pool ... so if any object which is being deallocated adds
|
|
|
|
|
* any object to the current autorelease pool, we may need to release it
|
|
|
|
|
* again.
|
2005-02-28 17:18:54 +00:00
|
|
|
|
*/
|
2005-03-06 15:08:12 +00:00
|
|
|
|
while (_child != nil || _released_count > 0)
|
2005-02-28 17:18:54 +00:00
|
|
|
|
{
|
2010-02-01 21:08:45 +00:00
|
|
|
|
volatile struct autorelease_array_list *released;
|
2005-03-06 15:08:12 +00:00
|
|
|
|
|
2011-05-24 10:52:25 +00:00
|
|
|
|
if (nil != _child)
|
2005-02-28 17:18:54 +00:00
|
|
|
|
{
|
2024-11-21 14:35:54 +00:00
|
|
|
|
[self _emptyChild];
|
2005-03-06 15:08:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Take the object out of the released list just before releasing it,
|
|
|
|
|
* so if we are doing "double_release_check"ing, then
|
|
|
|
|
* autoreleaseCountForObject: won't find the object we are currently
|
|
|
|
|
* releasing. */
|
2010-02-01 21:08:45 +00:00
|
|
|
|
released = _released_head;
|
2005-03-06 15:08:12 +00:00
|
|
|
|
while (released != 0)
|
|
|
|
|
{
|
2006-10-19 13:51:19 +00:00
|
|
|
|
id *objects = (id*)(released->objects);
|
2005-03-06 15:08:12 +00:00
|
|
|
|
|
|
|
|
|
for (i = 0; i < released->count; i++)
|
|
|
|
|
{
|
2008-02-18 09:59:16 +00:00
|
|
|
|
id anObject;
|
|
|
|
|
Class c;
|
|
|
|
|
unsigned hash;
|
2005-03-06 15:08:12 +00:00
|
|
|
|
|
2008-02-18 09:59:16 +00:00
|
|
|
|
anObject = objects[i];
|
2005-03-06 15:08:12 +00:00
|
|
|
|
objects[i] = nil;
|
2008-02-18 09:59:16 +00:00
|
|
|
|
if (anObject == nil)
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr,
|
|
|
|
|
"nil object encountered in autorelease pool\n");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2010-02-22 10:13:20 +00:00
|
|
|
|
c = object_getClass(anObject);
|
2008-02-18 09:59:16 +00:00
|
|
|
|
if (c == 0)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
|
format: @"nul class for object in autorelease pool"];
|
|
|
|
|
}
|
|
|
|
|
hash = (((unsigned)(uintptr_t)c) >> 3) & 0x0f;
|
2005-03-06 15:08:12 +00:00
|
|
|
|
if (classes[hash] != c)
|
|
|
|
|
{
|
2008-03-19 08:57:14 +00:00
|
|
|
|
/* If anObject was an instance, c is it's class.
|
|
|
|
|
* If anObject was a class, c is its metaclass.
|
2011-02-20 08:32:54 +00:00
|
|
|
|
* Either way, we should get the appropriate pointer.
|
2008-03-19 08:57:14 +00:00
|
|
|
|
* If anObject is a proxy to something,
|
|
|
|
|
* the +instanceMethodForSelector: and -methodForSelector:
|
2011-02-20 08:32:54 +00:00
|
|
|
|
* methods may not exist, but this will return the
|
2008-03-19 08:57:14 +00:00
|
|
|
|
* address of the forwarding method if necessary.
|
|
|
|
|
*/
|
2011-02-20 08:32:54 +00:00
|
|
|
|
imps[hash]
|
|
|
|
|
= class_getMethodImplementation(c, @selector(release));
|
2008-02-18 09:59:16 +00:00
|
|
|
|
classes[hash] = c;
|
2005-03-06 15:08:12 +00:00
|
|
|
|
}
|
2011-02-11 16:04:05 +00:00
|
|
|
|
(imps[hash])(anObject, @selector(release));
|
2005-03-06 15:08:12 +00:00
|
|
|
|
}
|
|
|
|
|
_released_count -= released->count;
|
|
|
|
|
released->count = 0;
|
|
|
|
|
released = released->next;
|
2005-02-28 17:18:54 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2004-05-14 16:22:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-04-06 12:02:59 +00:00
|
|
|
|
#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;
|
|
|
|
|
|
2016-04-29 07:31:48 +00:00
|
|
|
|
if (UINT_MAX == _released_count)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
|
format: @"NSAutoreleasePool -dealloc of deallocated pool"];
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-06 12:02:59 +00:00
|
|
|
|
[self emptyPool];
|
2016-04-29 07:31:48 +00:00
|
|
|
|
NSAssert(0 == _released_count, NSInternalInconsistencyException);
|
2012-04-06 12:02:59 +00:00
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-29 07:31:48 +00:00
|
|
|
|
/* Mark pool as cached so that any attempt to add an object to use it
|
|
|
|
|
* or to deallocate it again will raise an exception.
|
|
|
|
|
* We reset to zero when we get i out of the cache as a new allocation.
|
|
|
|
|
*/
|
|
|
|
|
_released_count = UINT_MAX;
|
2016-04-28 20:35:00 +00:00
|
|
|
|
|
2012-04-06 12:02:59 +00:00
|
|
|
|
/* Don't deallocate ourself, just save us for later use. */
|
2024-11-21 14:35:54 +00:00
|
|
|
|
push_pool_to_cache(tv, self);
|
2012-04-06 12:02:59 +00:00
|
|
|
|
GSNOSUPERDEALLOC;
|
|
|
|
|
}
|
|
|
|
|
|
2001-05-10 20:00:02 +00:00
|
|
|
|
- (void) _reallyDealloc
|
1996-07-15 18:41:44 +00:00
|
|
|
|
{
|
|
|
|
|
struct autorelease_array_list *a;
|
2024-11-21 14:35:54 +00:00
|
|
|
|
|
2005-02-22 11:22:44 +00:00
|
|
|
|
for (a = _released_head; a;)
|
1996-07-15 18:41:44 +00:00
|
|
|
|
{
|
|
|
|
|
void *n = a->next;
|
1999-09-28 11:10:34 +00:00
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), a);
|
1996-07-15 18:41:44 +00:00
|
|
|
|
a = n;
|
|
|
|
|
}
|
2010-02-01 21:08:45 +00:00
|
|
|
|
_released = _released_head = 0;
|
2024-11-21 14:35:54 +00:00
|
|
|
|
#if LOG_LIFETIME
|
|
|
|
|
fprintf(stderr, "*** %p autorelease pool really dealloc\n", self);
|
|
|
|
|
#endif
|
|
|
|
|
|
1996-07-15 18:41:44 +00:00
|
|
|
|
[super dealloc];
|
1995-03-12 19:35:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-09-30 05:16:53 +00:00
|
|
|
|
- (id) autorelease
|
1995-03-12 19:35:17 +00:00
|
|
|
|
{
|
1996-03-26 19:35:47 +00:00
|
|
|
|
[NSException raise: NSGenericException
|
|
|
|
|
format: @"Don't call `-autorelease' on a NSAutoreleasePool"];
|
1995-03-12 19:35:17 +00:00
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
2000-11-22 08:41:07 +00:00
|
|
|
|
+ (void) _endThread: (NSThread*)thread
|
1999-04-21 20:16:25 +00:00
|
|
|
|
{
|
|
|
|
|
struct autorelease_thread_vars *tv;
|
2008-08-25 15:39:02 +00:00
|
|
|
|
NSAutoreleasePool *pool;
|
1999-04-21 20:16:25 +00:00
|
|
|
|
|
2010-02-14 10:48:10 +00:00
|
|
|
|
tv = &((thread)->_autorelease_vars);
|
2010-02-01 21:08:45 +00:00
|
|
|
|
|
|
|
|
|
/* First release any objects in the pool... bearing in mind that
|
|
|
|
|
* releasing any object could cause other objects to be added to
|
|
|
|
|
* the pool.
|
|
|
|
|
*/
|
|
|
|
|
pool = tv->current_pool;
|
|
|
|
|
while (pool)
|
|
|
|
|
{
|
|
|
|
|
[pool emptyPool];
|
|
|
|
|
pool = pool->_parent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now free the memory (we have finished usingthe pool).
|
|
|
|
|
*/
|
2008-08-25 15:39:02 +00:00
|
|
|
|
pool = tv->current_pool;
|
2015-03-13 18:31:14 +00:00
|
|
|
|
tv->current_pool = nil;
|
2008-08-25 15:39:02 +00:00
|
|
|
|
while (pool)
|
1999-04-21 20:16:25 +00:00
|
|
|
|
{
|
2008-08-25 15:39:02 +00:00
|
|
|
|
NSAutoreleasePool *p = pool->_parent;
|
|
|
|
|
|
2001-05-10 20:00:02 +00:00
|
|
|
|
[pool _reallyDealloc];
|
2008-08-25 15:39:02 +00:00
|
|
|
|
pool = p;
|
1999-04-21 20:16:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2001-05-10 20:00:02 +00:00
|
|
|
|
free_pool_cache(tv);
|
1999-04-21 20:16:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
1995-04-14 15:01:24 +00:00
|
|
|
|
+ (void) enableRelease: (BOOL)enable
|
|
|
|
|
{
|
|
|
|
|
autorelease_enabled = enable;
|
|
|
|
|
}
|
|
|
|
|
|
2001-05-10 20:00:02 +00:00
|
|
|
|
+ (void) freeCache
|
|
|
|
|
{
|
|
|
|
|
free_pool_cache(ARP_THREAD_VARS);
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-18 06:11:00 +00:00
|
|
|
|
+ (void) setPoolCountThreshold: (unsigned)c
|
1995-04-14 15:01:24 +00:00
|
|
|
|
{
|
2016-04-29 07:31:48 +00:00
|
|
|
|
if (c >= UINT_MAX) c = UINT_MAX - 1;
|
2016-05-18 06:11:00 +00:00
|
|
|
|
pool_count_warning_threshold = c;
|
1995-04-14 15:01:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-05-18 06:11:00 +00:00
|
|
|
|
+ (void) setPoolNumberThreshold: (unsigned)c
|
2011-05-24 10:52:25 +00:00
|
|
|
|
{
|
2016-05-18 06:11:00 +00:00
|
|
|
|
pool_number_warning_threshold = c;
|
2011-05-24 10:52:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
1995-03-12 19:35:17 +00:00
|
|
|
|
@end
|
2016-03-25 11:15:28 +00:00
|
|
|
|
|