mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 16:33:29 +00:00
more leak cleanup stuff
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@33349 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
c6b3b8a097
commit
523366101e
10 changed files with 165 additions and 68 deletions
13
ChangeLog
13
ChangeLog
|
@ -1,3 +1,16 @@
|
|||
2011-06-19 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Headers/GNUstepBase/NSObject+GNUstepBase.h:
|
||||
* Source/Additions/NSObject+GNUstepBase.m:
|
||||
* Source/NSArray.m:
|
||||
* Source/NSNotificationCenter.m:
|
||||
* Source/NSObject.m:
|
||||
* Source/NSString.m:
|
||||
* Source/NSTimeZone.m:
|
||||
* Source/NSUserDefaults.m:
|
||||
* Source/NSValue.m:
|
||||
More leak cleanup.
|
||||
|
||||
2011-06-19 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Headers/GNUstepBase/NSObject+GNUstepBase.h:
|
||||
|
|
|
@ -109,16 +109,19 @@ extern "C" {
|
|||
* and should be sufficient for many classes.
|
||||
* </p>
|
||||
* <p>2. Implement a +atExit method to be run when the process ends and,
|
||||
* within that method call +shouldCleanUp to determine whether cleanup
|
||||
* should be done, and if it returns YES then perform any complex cleanup
|
||||
* tasks for the class.
|
||||
* within your +initialize implementation, call +shouldCleanUp to determine
|
||||
* whether cleanup should be done, and if it returns YES then call
|
||||
* +registerAtExit to have your +atExit method called when the process
|
||||
* terminates.
|
||||
* </p>
|
||||
*/
|
||||
@interface NSObject(atExit)
|
||||
|
||||
/** This method is called on exit for any class which implements it.<br />
|
||||
* The order in which methods for different classes is called is indeterminate
|
||||
* so the method must not depend on any other class being in a usable state
|
||||
/** This method is called on exit for any class which implements it and which
|
||||
* has called +registerAtExit to register it to be called.<br />
|
||||
* The order in which methods for different classes is called is the reverse
|
||||
* of the order in which the classes were registered, but it's best to assume
|
||||
* the method can not depend on any other class being in a usable state
|
||||
* at the point when the method is called (rather like +load).<br />
|
||||
* Typical use would be to release memory occupied by class data structures
|
||||
* so that memory usage analysis software will not think the memory has
|
||||
|
@ -126,10 +129,6 @@ extern "C" {
|
|||
*/
|
||||
+ (void) atExit;
|
||||
|
||||
/** Activates support for the +atExit method.
|
||||
*/
|
||||
+ (void) enableAtExit;
|
||||
|
||||
/** This method simply retains its argument so that it will never be
|
||||
* deallocated during normal operation, but keeps track of it so that
|
||||
* it is released during process exit (at the point immediately before
|
||||
|
@ -138,6 +137,20 @@ extern "C" {
|
|||
*/
|
||||
+ (id) leak: (id)anObject;
|
||||
|
||||
/** This method retains the object at *anAddress so that it will never be
|
||||
* deallocated during normal operation, but keeps track of the address
|
||||
* so that the object is released and the address is zeroed during process
|
||||
* exit (at the point immediately before calls to +atExit methods are
|
||||
* performed) if cleanup is enabled.<br />
|
||||
* Returns the object at *anAddress.
|
||||
*/
|
||||
+ (id) leakAt: (id*)anAddress;
|
||||
|
||||
/** Sets the receiver to have its +atExit method called at the point when
|
||||
* the process terminates.
|
||||
*/
|
||||
+ (void) registerAtExit;
|
||||
|
||||
/** Specifies the default cleanup behavior on process exit ... the value
|
||||
* returned by the NSObject implementation of the +shouldClanUp method.<br />
|
||||
* Calling this method with a YES argument implicitly calls the +enableAtExit
|
||||
|
|
|
@ -127,80 +127,135 @@
|
|||
|
||||
@end
|
||||
|
||||
static NSMutableArray *leaked = nil;
|
||||
static NSLock *leakLock = nil;
|
||||
struct exitLink {
|
||||
struct exitLink *next;
|
||||
Class cls;
|
||||
};
|
||||
|
||||
struct leakLink {
|
||||
struct leakLink *next;
|
||||
id obj;
|
||||
id *at;
|
||||
};
|
||||
|
||||
static struct exitLink *exited = 0;
|
||||
static struct leakLink *leaked = 0;
|
||||
static BOOL enabled = NO;
|
||||
static BOOL shouldCleanUp = NO;
|
||||
|
||||
static void
|
||||
handleExit()
|
||||
{
|
||||
int classCount;
|
||||
|
||||
if (YES == shouldCleanUp)
|
||||
{
|
||||
DESTROY(leaked);
|
||||
DESTROY(leakLock);
|
||||
}
|
||||
classCount = objc_getClassList(NULL, 0);
|
||||
if (classCount > 0)
|
||||
{
|
||||
Class *classes;
|
||||
int index;
|
||||
|
||||
classes = malloc(sizeof(Class) * classCount);
|
||||
classCount = objc_getClassList(classes, classCount);
|
||||
for (index = 0; index < classCount; index++)
|
||||
while (leaked != 0)
|
||||
{
|
||||
Class c = classes[index];
|
||||
Method m = class_getClassMethod(c, @selector(atExit));
|
||||
struct leakLink *tmp = leaked;
|
||||
|
||||
if (m != 0)
|
||||
leaked = tmp->next;
|
||||
if (0 != tmp->at)
|
||||
{
|
||||
Class s = class_getSuperclass(c);
|
||||
|
||||
if (0 == s || class_getClassMethod(s, @selector(atExit)) != m)
|
||||
{
|
||||
[c atExit];
|
||||
}
|
||||
tmp->obj = *(tmp->at);
|
||||
*(tmp->at) = nil;
|
||||
}
|
||||
[tmp->obj release];
|
||||
free(tmp);
|
||||
}
|
||||
free(classes);
|
||||
}
|
||||
|
||||
while (exited != 0)
|
||||
{
|
||||
struct exitLink *tmp = exited;
|
||||
|
||||
exited = tmp->next;
|
||||
[tmp->cls atExit];
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
[gnustep_global_lock release];
|
||||
}
|
||||
|
||||
@implementation NSObject(atExit)
|
||||
|
||||
+ (void) enableAtExit
|
||||
{
|
||||
if (nil == leakLock)
|
||||
{
|
||||
leakLock = [NSLock new];
|
||||
atexit(handleExit);
|
||||
}
|
||||
}
|
||||
|
||||
+ (void) atExit
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
+ (id) leakAt: (id*)anAddress
|
||||
{
|
||||
struct leakLink *l;
|
||||
|
||||
l = (struct leakLink*)malloc(sizeof(struct leakLink));
|
||||
l->at = anAddress;
|
||||
l->obj = [*anAddress retain];
|
||||
[gnustep_global_lock lock];
|
||||
l->next = leaked;
|
||||
leaked = l;
|
||||
[gnustep_global_lock unlock];
|
||||
return l->obj;
|
||||
}
|
||||
|
||||
+ (id) leak: (id)anObject
|
||||
{
|
||||
[leakLock lock];
|
||||
if (nil == leaked)
|
||||
struct leakLink *l;
|
||||
|
||||
l = (struct leakLink*)malloc(sizeof(struct leakLink));
|
||||
l->at = 0;
|
||||
l->obj = [anObject retain];
|
||||
[gnustep_global_lock lock];
|
||||
l->next = leaked;
|
||||
leaked = l;
|
||||
[gnustep_global_lock unlock];
|
||||
return l->obj;
|
||||
}
|
||||
|
||||
+ (void) registerAtExit
|
||||
{
|
||||
Method m = class_getClassMethod(self, @selector(atExit));
|
||||
|
||||
if (m != 0)
|
||||
{
|
||||
leaked = [NSMutableArray new];
|
||||
Class s = class_getSuperclass(self);
|
||||
|
||||
if (0 == s || class_getClassMethod(s, @selector(atExit)) != m)
|
||||
{
|
||||
struct exitLink *l;
|
||||
|
||||
[gnustep_global_lock lock];
|
||||
for (l = exited; l != 0; l = l->next)
|
||||
{
|
||||
if (l->cls == self)
|
||||
{
|
||||
[gnustep_global_lock unlock];
|
||||
return; // Already registered
|
||||
}
|
||||
}
|
||||
l = (struct exitLink*)malloc(sizeof(struct exitLink));
|
||||
l->cls = self;
|
||||
l->next = exited;
|
||||
exited = l;
|
||||
if (NO == enabled)
|
||||
{
|
||||
atexit(handleExit);
|
||||
enabled = YES;
|
||||
}
|
||||
[gnustep_global_lock lock];
|
||||
}
|
||||
}
|
||||
[leaked addObject: anObject];
|
||||
[leakLock unlock];
|
||||
return anObject;
|
||||
}
|
||||
|
||||
+ (void) setShouldCleanUp: (BOOL)aFlag
|
||||
{
|
||||
if (YES == aFlag)
|
||||
{
|
||||
[self enableAtExit];
|
||||
[gnustep_global_lock lock];
|
||||
if (NO == enabled)
|
||||
{
|
||||
atexit(handleExit);
|
||||
enabled = YES;
|
||||
}
|
||||
[gnustep_global_lock lock];
|
||||
shouldCleanUp = YES;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -101,6 +101,13 @@ static SEL oaiSel;
|
|||
static SEL remSel;
|
||||
static SEL rlSel;
|
||||
|
||||
+ (void) atExit
|
||||
{
|
||||
DESTROY(defaultPlaceholderArray);
|
||||
DESTROY(placeholderLock);
|
||||
DESTROY(placeholderMap);
|
||||
}
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
if (self == [NSArray class])
|
||||
|
@ -129,6 +136,7 @@ static SEL rlSel;
|
|||
placeholderMap = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
|
||||
NSNonRetainedObjectMapValueCallBacks, 0);
|
||||
placeholderLock = [NSLock new];
|
||||
[self registerAtExit];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#import "Foundation/NSLock.h"
|
||||
#import "Foundation/NSThread.h"
|
||||
#import "GNUstepBase/GSLock.h"
|
||||
#import "GNUstepBase/NSObject+GNUstepBase.h"
|
||||
|
||||
static NSZone *_zone = 0;
|
||||
|
||||
|
@ -716,6 +717,7 @@ static NSNotificationCenter *default_center = nil;
|
|||
*/
|
||||
default_center = [self alloc];
|
||||
[default_center init];
|
||||
[self registerAtExit];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1101,6 +1101,13 @@ objc_create_block_classes_as_subclasses_of(Class super);
|
|||
GSObjCBehaviorDebug(GSPrivateEnvironmentFlag("GNUSTEP_BEHAVIOR_DEBUG",
|
||||
GSObjCBehaviorDebug(-1)));
|
||||
|
||||
/* See if we should cleanup at process exit.
|
||||
*/
|
||||
if (YES == GSPrivateEnvironmentFlag("GNUSTEP_SHOULD_CLEAN_UP", NO))
|
||||
{
|
||||
[self setShouldCleanUp: YES];
|
||||
}
|
||||
|
||||
/* Set up the autorelease system ... we must do this before using any
|
||||
* other class whose +initialize might autorelease something.
|
||||
*/
|
||||
|
@ -1113,13 +1120,6 @@ objc_create_block_classes_as_subclasses_of(Class super);
|
|||
*/
|
||||
NSConstantStringClass = [NSString constantStringClass];
|
||||
|
||||
/* See if we should cleanup at process exit.
|
||||
*/
|
||||
if (YES == GSPrivateEnvironmentFlag("GNUSTEP_SHOULD_CLEAN_UP", NO))
|
||||
{
|
||||
[self setShouldCleanUp: YES];
|
||||
}
|
||||
|
||||
GSPrivateBuildStrings();
|
||||
|
||||
/* Determine zombie management flags and set up a map to store
|
||||
|
@ -1129,6 +1129,7 @@ objc_create_block_classes_as_subclasses_of(Class super);
|
|||
NSDeallocateZombies = GSPrivateEnvironmentFlag("NSDeallocateZombies", NO);
|
||||
zombieMap = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
|
||||
NSNonOwnedPointerMapValueCallBacks, 0);
|
||||
[[NSObject leakAt: (id*)&zombieMap] release];
|
||||
|
||||
/* We need to cache the zombie class.
|
||||
* We can't call +class because NSZombie doesn't have that method.
|
||||
|
|
|
@ -560,15 +560,9 @@ handle_printf_atsign (FILE *stream,
|
|||
|
||||
+ (void) atExit
|
||||
{
|
||||
NSMapTable *t = placeholderMap;
|
||||
|
||||
if (0 != t)
|
||||
{
|
||||
DESTROY(defaultPlaceholderString);
|
||||
DESTROY(placeholderLock);
|
||||
placeholderMap = 0;
|
||||
NSFreeMapTable(t);
|
||||
}
|
||||
DESTROY(defaultPlaceholderString);
|
||||
DESTROY(placeholderLock);
|
||||
DESTROY(placeholderMap);
|
||||
}
|
||||
|
||||
+ (void) initialize
|
||||
|
@ -626,6 +620,7 @@ handle_printf_atsign (FILE *stream,
|
|||
[NSException raise: NSGenericException
|
||||
format: @"register printf handling of %%@ failed"];
|
||||
#endif
|
||||
[self registerAtExit];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -668,6 +668,7 @@ static NSMapTable *absolutes = 0;
|
|||
{
|
||||
absolutes = NSCreateMapTable(NSIntegerMapKeyCallBacks,
|
||||
NSNonOwnedPointerMapValueCallBacks, 0);
|
||||
[[NSObject leakAt: (id*)&absolutes] release];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1340,18 +1341,23 @@ static NSMapTable *absolutes = 0;
|
|||
NSTimeZoneClass = self;
|
||||
GSPlaceholderTimeZoneClass = [GSPlaceholderTimeZone class];
|
||||
zoneDictionary = [[NSMutableDictionary alloc] init];
|
||||
[[NSObject leakAt: &zoneDictionary] release];
|
||||
|
||||
/*
|
||||
* Set up infrastructure for placeholder timezones.
|
||||
*/
|
||||
defaultPlaceholderTimeZone = (GSPlaceholderTimeZone*)
|
||||
NSAllocateObject(GSPlaceholderTimeZoneClass, 0, NSDefaultMallocZone());
|
||||
[[NSObject leakAt: &defaultPlaceholderTimeZone] release];
|
||||
placeholderMap = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
|
||||
NSNonRetainedObjectMapValueCallBacks, 0);
|
||||
[[NSObject leakAt: (id*)&placeholderMap] release];
|
||||
|
||||
localTimeZone = [[NSLocalTimeZone alloc] init];
|
||||
[[NSObject leakAt: (id*)&localTimeZone] release];
|
||||
|
||||
zone_mutex = [GSLazyRecursiveLock new];
|
||||
[[NSObject leakAt: (id*)&zone_mutex] release];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver: self
|
||||
selector: @selector(_notified:)
|
||||
|
|
|
@ -390,6 +390,7 @@ newLanguages(NSArray *oldNames)
|
|||
NSMutableDictionaryClass = [NSMutableDictionary class];
|
||||
NSStringClass = [NSString class];
|
||||
classLock = [GSLazyRecursiveLock new];
|
||||
[self registerAtExit];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -103,9 +103,12 @@ static NSLock *placeholderLock;
|
|||
*/
|
||||
defaultPlaceholderValue = (GSPlaceholderValue*)
|
||||
NSAllocateObject(GSPlaceholderValueClass, 0, NSDefaultMallocZone());
|
||||
[[NSObject leakAt: (id*)&defaultPlaceholderValue] release];
|
||||
placeholderMap = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
|
||||
NSNonRetainedObjectMapValueCallBacks, 0);
|
||||
[[NSObject leakAt: (id*)&placeholderMap] release];
|
||||
placeholderLock = [NSLock new];
|
||||
[[NSObject leakAt: (id*)&placeholderLock] release];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue