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:
Richard Frith-MacDonald 2011-06-19 13:34:17 +00:00
parent c6b3b8a097
commit 523366101e
10 changed files with 165 additions and 68 deletions

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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];
}
}

View file

@ -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];
}
}

View file

@ -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.

View file

@ -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];
}
}

View file

@ -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:)

View file

@ -390,6 +390,7 @@ newLanguages(NSArray *oldNames)
NSMutableDictionaryClass = [NSMutableDictionary class];
NSStringClass = [NSString class];
classLock = [GSLazyRecursiveLock new];
[self registerAtExit];
}
}

View file

@ -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];
}
}