mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 16:33:29 +00:00
Add some flexibility
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@33355 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
53b5f5405c
commit
33843644e1
4 changed files with 125 additions and 84 deletions
|
@ -3,6 +3,10 @@
|
|||
* Source/NSObject.m: Keep zombie working until after atext stuff has
|
||||
run.
|
||||
* Source/NSUserDefaults.m: fix possible deref of nul pointer.
|
||||
* Source/Additions/NSObject+GNUstepBase.m:
|
||||
* Headers/GNUstepBase/NSObject+GNUstepBase.h:
|
||||
Make clanup code a bit more flexible by allowin different cleanup
|
||||
methods to be registered.
|
||||
|
||||
2011-06-19 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
|
|
|
@ -96,6 +96,23 @@ extern "C" {
|
|||
|
||||
@end
|
||||
|
||||
/** This is an informal protocol ... classes may implement the method and
|
||||
* register themselves to have it called on process exit.
|
||||
*/
|
||||
@interface NSObject(GSAtExit)
|
||||
/** 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
|
||||
* been leaked.
|
||||
*/
|
||||
+ (void) atExit;
|
||||
@end
|
||||
|
||||
/** Category for methods handling leaked memory cleanup on exit of process
|
||||
* (for use when debugging memory leaks).<br />
|
||||
* You enable this by calling the +setShouldCleanUp: method (done implicitly
|
||||
|
@ -114,25 +131,17 @@ extern "C" {
|
|||
* +registerAtExit to have your +atExit method called when the process
|
||||
* terminates.
|
||||
* </p>
|
||||
* <p>The order in which 'leaked' objects are released and +atExit methods
|
||||
* are called on process exist is the reverse of the order in which they
|
||||
* werse set up suing this API.
|
||||
* </p>
|
||||
*/
|
||||
@interface NSObject(atExit)
|
||||
@interface NSObject(GSCleanup)
|
||||
|
||||
/** 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
|
||||
* been leaked.
|
||||
*/
|
||||
+ (void) atExit;
|
||||
|
||||
/** 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
|
||||
* calls to +atExit methods are performed) if cleanup is enabled.<br />
|
||||
* it is released during process exit if cleanup is enabled.<br />
|
||||
* Returns its argument.
|
||||
*/
|
||||
+ (id) leak: (id)anObject;
|
||||
|
@ -140,16 +149,26 @@ extern "C" {
|
|||
/** 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 />
|
||||
* exit 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.
|
||||
* the process terminates.<br />
|
||||
* Returns YES on success and NO on failure (if the class does not implement
|
||||
* the method or if it is already registered to call it).<br />
|
||||
* Implemented as a call to +registerAtExit: with the selector for the +atExit
|
||||
* method as its argument.
|
||||
*/
|
||||
+ (void) registerAtExit;
|
||||
+ (BOOL) registerAtExit;
|
||||
|
||||
/** Sets the receiver to have the specified method called at the point when
|
||||
* the process terminates.<br />
|
||||
* Returns YES on success and NO on failure (if the class does not implement
|
||||
* the method ir if it is already registered to call it).
|
||||
*/
|
||||
+ (BOOL) registerAtExit: (SEL)aSelector;
|
||||
|
||||
/** Specifies the default cleanup behavior on process exit ... the value
|
||||
* returned by the NSObject implementation of the +shouldClanUp method.<br />
|
||||
|
|
|
@ -129,120 +129,132 @@
|
|||
|
||||
struct exitLink {
|
||||
struct exitLink *next;
|
||||
Class cls;
|
||||
};
|
||||
|
||||
struct leakLink {
|
||||
struct leakLink *next;
|
||||
id obj;
|
||||
id *at;
|
||||
id obj; // Object to release or class for atExit
|
||||
SEL sel; // Selector for atExit or 0 if releasing
|
||||
id *at; // Address of static variable or NULL
|
||||
};
|
||||
|
||||
static struct exitLink *exited = 0;
|
||||
static struct leakLink *leaked = 0;
|
||||
static BOOL enabled = NO;
|
||||
static BOOL shouldCleanUp = NO;
|
||||
|
||||
static void
|
||||
handleExit()
|
||||
{
|
||||
if (YES == shouldCleanUp)
|
||||
while (exited != 0)
|
||||
{
|
||||
while (leaked != 0)
|
||||
{
|
||||
struct leakLink *tmp = leaked;
|
||||
struct exitLink *tmp = exited;
|
||||
|
||||
leaked = tmp->next;
|
||||
exited = tmp->next;
|
||||
if (0 != tmp->sel)
|
||||
{
|
||||
Method method;
|
||||
IMP msg;
|
||||
|
||||
method = class_getClassMethod(tmp->obj, tmp->sel);
|
||||
msg = method_getImplementation(method);
|
||||
if (0 != msg)
|
||||
{
|
||||
(*msg)(tmp->obj, tmp->sel);
|
||||
}
|
||||
}
|
||||
else if (YES == shouldCleanUp)
|
||||
{
|
||||
if (0 != tmp->at)
|
||||
{
|
||||
tmp->obj = *(tmp->at);
|
||||
*(tmp->at) = nil;
|
||||
}
|
||||
[tmp->obj release];
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
while (exited != 0)
|
||||
{
|
||||
struct exitLink *tmp = exited;
|
||||
|
||||
exited = tmp->next;
|
||||
[tmp->cls atExit];
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
[gnustep_global_lock release];
|
||||
}
|
||||
|
||||
@implementation NSObject(atExit)
|
||||
|
||||
+ (void) atExit
|
||||
{
|
||||
return;
|
||||
}
|
||||
@implementation NSObject(GSCleanup)
|
||||
|
||||
+ (id) leakAt: (id*)anAddress
|
||||
{
|
||||
struct leakLink *l;
|
||||
struct exitLink *l;
|
||||
|
||||
l = (struct leakLink*)malloc(sizeof(struct leakLink));
|
||||
l = (struct exitLink*)malloc(sizeof(struct exitLink));
|
||||
l->at = anAddress;
|
||||
l->obj = [*anAddress retain];
|
||||
l->sel = 0;
|
||||
[gnustep_global_lock lock];
|
||||
l->next = leaked;
|
||||
leaked = l;
|
||||
l->next = exited;
|
||||
exited = l;
|
||||
[gnustep_global_lock unlock];
|
||||
return l->obj;
|
||||
}
|
||||
|
||||
+ (id) leak: (id)anObject
|
||||
{
|
||||
struct leakLink *l;
|
||||
struct exitLink *l;
|
||||
|
||||
l = (struct leakLink*)malloc(sizeof(struct leakLink));
|
||||
l = (struct exitLink*)malloc(sizeof(struct exitLink));
|
||||
l->at = 0;
|
||||
l->obj = [anObject retain];
|
||||
l->sel = 0;
|
||||
[gnustep_global_lock lock];
|
||||
l->next = leaked;
|
||||
leaked = l;
|
||||
l->next = exited;
|
||||
exited = l;
|
||||
[gnustep_global_lock unlock];
|
||||
return l->obj;
|
||||
}
|
||||
|
||||
+ (void) registerAtExit
|
||||
+ (BOOL) registerAtExit
|
||||
{
|
||||
Method m = class_getClassMethod(self, @selector(atExit));
|
||||
return [self registerAtExit: @selector(atExit)];
|
||||
}
|
||||
|
||||
if (m != 0)
|
||||
+ (BOOL) registerAtExit: (SEL)sel
|
||||
{
|
||||
Method m;
|
||||
Class s;
|
||||
struct exitLink *l;
|
||||
|
||||
if (0 == sel)
|
||||
{
|
||||
Class s = class_getSuperclass(self);
|
||||
sel = @selector(atExit);
|
||||
}
|
||||
|
||||
if (0 == s || class_getClassMethod(s, @selector(atExit)) != m)
|
||||
m = class_getClassMethod(self, sel);
|
||||
if (0 == m)
|
||||
{
|
||||
return NO; // method not implemented.
|
||||
}
|
||||
|
||||
s = class_getSuperclass(self);
|
||||
if (0 != s && class_getClassMethod(s, sel) == m)
|
||||
{
|
||||
return NO; // method not implemented in this class
|
||||
}
|
||||
|
||||
[gnustep_global_lock lock];
|
||||
for (l = exited; l != 0; l = l->next)
|
||||
{
|
||||
if (l->obj == self && sel_isEqual(l->sel, sel))
|
||||
{
|
||||
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];
|
||||
[gnustep_global_lock unlock];
|
||||
return NO; // Already registered
|
||||
}
|
||||
}
|
||||
l = (struct exitLink*)malloc(sizeof(struct exitLink));
|
||||
l->obj = self;
|
||||
l->sel = sel;
|
||||
l->at = 0;
|
||||
l->next = exited;
|
||||
exited = l;
|
||||
if (NO == enabled)
|
||||
{
|
||||
atexit(handleExit);
|
||||
enabled = YES;
|
||||
}
|
||||
[gnustep_global_lock lock];
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (void) setShouldCleanUp: (BOOL)aFlag
|
||||
|
|
|
@ -136,7 +136,7 @@ static void GSMakeZombie(NSObject *o)
|
|||
|
||||
c = object_getClass(o);
|
||||
object_setClass(o, zombieClass);
|
||||
if (NSDeallocateZombies == NO)
|
||||
if (0 != zombieMap)
|
||||
{
|
||||
[allocationLock lock];
|
||||
NSMapInsert(zombieMap, (void*)o, (void*)c);
|
||||
|
@ -149,7 +149,7 @@ static void GSLogZombie(id o, SEL sel)
|
|||
{
|
||||
Class c = 0;
|
||||
|
||||
if (NSDeallocateZombies == NO)
|
||||
if (0 != zombieMap)
|
||||
{
|
||||
[allocationLock lock];
|
||||
c = NSMapGet(zombieMap, (void*)o);
|
||||
|
@ -1106,6 +1106,7 @@ objc_create_block_classes_as_subclasses_of(Class super);
|
|||
if (YES == GSPrivateEnvironmentFlag("GNUSTEP_SHOULD_CLEAN_UP", NO))
|
||||
{
|
||||
[self setShouldCleanUp: YES];
|
||||
[self registerAtExit: @selector(_atExit)];
|
||||
}
|
||||
|
||||
/* Set up the autorelease system ... we must do this before using any
|
||||
|
@ -1149,6 +1150,11 @@ objc_create_block_classes_as_subclasses_of(Class super);
|
|||
return;
|
||||
}
|
||||
|
||||
+ (void) _atExit
|
||||
{
|
||||
DESTROY(zombieMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates a new instance of the receiver from the default
|
||||
* zone, by invoking +allocWithZone: with
|
||||
|
@ -2460,7 +2466,7 @@ objc_create_block_classes_as_subclasses_of(Class super);
|
|||
}
|
||||
- (Class) originalClass
|
||||
{
|
||||
return NSMapGet(zombieMap, (void*)self);
|
||||
return zombieMap ? NSMapGet(zombieMap, (void*)self) : Nil;
|
||||
}
|
||||
- (void) forwardInvocation: (NSInvocation*)anInvocation
|
||||
{
|
||||
|
@ -2481,7 +2487,7 @@ objc_create_block_classes_as_subclasses_of(Class super);
|
|||
return nil;
|
||||
}
|
||||
[allocationLock lock];
|
||||
c = NSMapGet(zombieMap, (void*)self);
|
||||
c = zombieMap ? NSMapGet(zombieMap, (void*)self) : Nil;
|
||||
[allocationLock unlock];
|
||||
return [c instanceMethodSignatureForSelector: aSelector];
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue