atexit improvements

This commit is contained in:
rfm 2024-11-14 12:52:43 +00:00
parent e64b7dc6b3
commit dd3367de3b
4 changed files with 86 additions and 42 deletions

View file

@ -212,46 +212,49 @@ extern "C" {
+ (void) atExit; + (void) atExit;
@end @end
/** Category for methods handling leaked memory cleanup on exit of process /** Category for methods handling leaked memory clean-up on exit of process
* (for use when debugging memory leaks).<br /> * (for use when debugging memory leaks).<br />
* You enable this by calling the +setShouldCleanUp: method (done implicitly * You enable this by calling the +setShouldCleanUp: method (done implicitly
* by gnustep-base if the GNUSTEP_SHOULD_CLEAN_UP environment variable is * by gnustep-base if the GNUSTEP_SHOULD_CLEAN_UP environment variable is
* set to YES).<br /> * set to YES).<br />
* Your class then has two options for performing cleanup when the process * Your class then has two options for performing clean-up when the process
* ends: * ends:
* <p>1. Use the +leak: method to register objects which are simply to be * <p>1. Use the +leaked: method to register objects which are simply to be
* retained until the process ends, and then either ignored or released * retained until the process ends, and then either ignored or released
* depending on the cleanup setting in force. This mechanism is simple * depending on the clean-up setting in force. This mechanism is simple
* and should be sufficient for many classes. * and should be sufficient for many classes.
* </p> * </p>
* <p>2. Implement a +atExit method to be run when the process ends and, * <p>2. Implement a +atExit method to be run when the process ends and,
* within your +initialize implementation, call +shouldCleanUp to determine * within your +initialize implementation, call +shouldCleanUp to determine
* whether cleanup should be done, and if it returns YES then call * whether clean-up should be done, and if it returns YES then call
* +registerAtExit to have your +atExit method called when the process * +registerAtExit to have your +atExit method called when the process
* terminates. * terminates.
* </p> * </p>
* <p>The order in which 'leaked' objects are released and +atExit methods * <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 * are called on process exist is the reverse of the order in which they
* werse set up suing this API. * werse set up using this API.
* </p> * </p>
*/ */
@interface NSObject(GSCleanup) @interface NSObject(GSCleanUp)
/** Returns YES if the process is exiting (and perhaps performing clean-up).
/** 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 if cleanup is enabled.<br />
* Returns its argument.
*/ */
+ (id) NS_RETURNS_RETAINED leak: (id)anObject; + (BOOL) isExiting;
/** This method retains the object at *anAddress so that it will never be /** This method informs the system that the object at anAddress has been
* deallocated during normal operation, but keeps track of the address * intentionally leaked (will not be deallocated by higher level code)
* so that the object is released and the address is zeroed during process * and should be cleaned up at process exit (and the address content
* exit if cleanup is enabled.<br /> * zeroed out) if clean-up is enabled.
* Returns the object at *anAddress.
*/ */
+ (id) NS_RETURNS_RETAINED leakAt: (id*)anAddress; + (void) leaked: (id*)anAddress;
/** Deprecated: use +leaked: instead.
*/
+ (id) NS_RETURNS_RETAINED leak: (id)anObject ;//GS_DEPRECATED_FUNC;
/** Deprecated: use +leaked: instead.
*/
+ (id) NS_RETURNS_RETAINED leakAt: (id*)anAddress ;//GS_DEPRECATED_FUNC;
/** Sets the receiver to have its +atExit method called at the point when /** Sets the receiver to have its +atExit method called at the point when
* the process terminates.<br /> * the process terminates.<br />
@ -269,10 +272,10 @@ extern "C" {
*/ */
+ (BOOL) registerAtExit: (SEL)aSelector; + (BOOL) registerAtExit: (SEL)aSelector;
/** Specifies the default cleanup behavior on process exit ... the value /** Specifies the default clean-up behavior on process exit ... the value
* returned by the NSObject implementation of the +shouldCleanUp method.<br /> * returned by the NSObject implementation of the +shouldCleanUp method.<br />
* Calling this method with a YES argument implicitly enables the support for * Calling this method with a YES argument implicitly enables the support for
* cleanup at exit.<br /> * clean-up at exit.<br />
* The GNUstep Base library calls this method with the value obtained from * The GNUstep Base library calls this method with the value obtained from
* the GNUSTEP_SHOULD_CLEAN_UP environment variable when NSObject is * the GNUSTEP_SHOULD_CLEAN_UP environment variable when NSObject is
* initialised. * initialised.

View file

@ -156,6 +156,7 @@ struct exitLink {
static struct exitLink *exited = 0; static struct exitLink *exited = 0;
static BOOL enabled = NO; static BOOL enabled = NO;
static BOOL shouldCleanUp = NO; static BOOL shouldCleanUp = NO;
static BOOL isExiting = NO;
static NSLock *exitLock = nil; static NSLock *exitLock = nil;
static inline void setup() static inline void setup()
@ -176,7 +177,10 @@ static inline void setup()
static void static void
handleExit() handleExit()
{ {
BOOL unknownThread = GSRegisterCurrentThread(); BOOL unknownThread;
isExiting = YES;
unknownThread = GSRegisterCurrentThread();
CREATE_AUTORELEASE_POOL(arp); CREATE_AUTORELEASE_POOL(arp);
while (exited != 0) while (exited != 0)
@ -212,9 +216,32 @@ handleExit()
{ {
GSUnregisterCurrentThread(); GSUnregisterCurrentThread();
} }
isExiting = NO;
} }
@implementation NSObject(GSCleanup) @implementation NSObject(GSCleanUp)
+ (BOOL) isExiting
{
return isExiting;
}
+ (void) leaked: (id*)anAddress
{
struct exitLink *l;
NSAssert(*anAddress && [*anAddress isKindOfClass: [NSObject class]],
NSInvalidArgumentException);
l = (struct exitLink*)malloc(sizeof(struct exitLink));
l->at = anAddress;
l->obj = *anAddress;
l->sel = 0;
setup();
[exitLock lock];
l->next = exited;
exited = l;
[exitLock unlock];
}
+ (id) leakAt: (id*)anAddress + (id) leakAt: (id*)anAddress
{ {

View file

@ -105,7 +105,15 @@ static SEL rlSel;
+ (void) atExit + (void) atExit
{ {
DESTROY(defaultPlaceholderArray); id o;
/* The default placeholder array overrides -dealloc so we must get rid of
* it directly.
*/
o = defaultPlaceholderArray;
defaultPlaceholderArray = nil;
NSDeallocateObject(o);
DESTROY(placeholderMap); DESTROY(placeholderMap);
} }

View file

@ -166,9 +166,13 @@ static NSMapTable *placeholderMap;
static gs_mutex_t placeholderLock = GS_MUTEX_INIT_STATIC; static gs_mutex_t placeholderLock = GS_MUTEX_INIT_STATIC;
static SEL cMemberSel = 0; static SEL cMemberSel = 0;
static NSCharacterSet *nonBase = nil; static NSCharacterSet *nonBase = nil;
static BOOL (*nonBaseImp)(id, SEL, unichar) = 0; static BOOL (*nonBaseImp)(id, SEL, unichar) = 0;
static NSCharacterSet *wPathSeps = nil;
static NSCharacterSet *uPathSeps = nil;
static NSCharacterSet *rPathSeps = nil;
/* Macro to return the receiver if it is already immutable, but an /* Macro to return the receiver if it is already immutable, but an
* autoreleased copy otherwise. Used where we have to return an * autoreleased copy otherwise. Used where we have to return an
@ -308,9 +312,6 @@ GSPathHandling(const char *mode)
static NSCharacterSet* static NSCharacterSet*
pathSeps(void) pathSeps(void)
{ {
static NSCharacterSet *wPathSeps = nil;
static NSCharacterSet *uPathSeps = nil;
static NSCharacterSet *rPathSeps = nil;
if (GSPathHandlingRight()) if (GSPathHandlingRight())
{ {
if (rPathSeps == nil) if (rPathSeps == nil)
@ -318,9 +319,8 @@ pathSeps(void)
GS_MUTEX_LOCK(placeholderLock); GS_MUTEX_LOCK(placeholderLock);
if (rPathSeps == nil) if (rPathSeps == nil)
{ {
rPathSeps rPathSeps = RETAIN([NSCharacterSet
= [NSCharacterSet characterSetWithCharactersInString: @"/\\"]; characterSetWithCharactersInString: @"/\\"]);
rPathSeps = [NSObject leakAt: &rPathSeps];
} }
GS_MUTEX_UNLOCK(placeholderLock); GS_MUTEX_UNLOCK(placeholderLock);
} }
@ -333,9 +333,8 @@ pathSeps(void)
GS_MUTEX_LOCK(placeholderLock); GS_MUTEX_LOCK(placeholderLock);
if (uPathSeps == nil) if (uPathSeps == nil)
{ {
uPathSeps uPathSeps = RETAIN([NSCharacterSet
= [NSCharacterSet characterSetWithCharactersInString: @"/"]; characterSetWithCharactersInString: @"/"]);
uPathSeps = [NSObject leakAt: &uPathSeps];
} }
GS_MUTEX_UNLOCK(placeholderLock); GS_MUTEX_UNLOCK(placeholderLock);
} }
@ -348,9 +347,8 @@ pathSeps(void)
GS_MUTEX_LOCK(placeholderLock); GS_MUTEX_LOCK(placeholderLock);
if (wPathSeps == nil) if (wPathSeps == nil)
{ {
wPathSeps wPathSeps = RETAIN([NSCharacterSet
= [NSCharacterSet characterSetWithCharactersInString: @"\\"]; characterSetWithCharactersInString: @"\\"]);
wPathSeps = [NSObject leakAt: &wPathSeps];
} }
GS_MUTEX_UNLOCK(placeholderLock); GS_MUTEX_UNLOCK(placeholderLock);
} }
@ -881,6 +879,10 @@ register_printf_atsign ()
+ (void) atExit + (void) atExit
{ {
DESTROY(placeholderMap); DESTROY(placeholderMap);
DESTROY(nonBase);
DESTROY(rPathSeps);
DESTROY(uPathSeps);
DESTROY(wPathSeps);
} }
+ (void) initialize + (void) initialize
@ -900,7 +902,6 @@ register_printf_atsign ()
ranSel = @selector(rangeOfComposedCharacterSequenceAtIndex:); ranSel = @selector(rangeOfComposedCharacterSequenceAtIndex:);
nonBase = [NSCharacterSet nonBaseCharacterSet]; nonBase = [NSCharacterSet nonBaseCharacterSet];
nonBase = [NSObject leakAt: &nonBase];
nonBaseImp nonBaseImp
= (BOOL(*)(id,SEL,unichar))[nonBase methodForSelector: cMemberSel]; = (BOOL(*)(id,SEL,unichar))[nonBase methodForSelector: cMemberSel];
@ -3951,9 +3952,13 @@ register_printf_atsign ()
} }
else else
{ {
BOOL result;
ENTER_POOL
NSData *d = [self dataUsingEncoding: encoding]; NSData *d = [self dataUsingEncoding: encoding];
unsigned length = [d length]; unsigned length = [d length];
BOOL result = (length < maxLength) ? YES : NO;
result = (length < maxLength) ? YES : NO;
if (d == nil) if (d == nil)
{ {
@ -3966,6 +3971,7 @@ register_printf_atsign ()
} }
memcpy(buffer, [d bytes], length); memcpy(buffer, [d bytes], length);
buffer[length] = '\0'; buffer[length] = '\0';
LEAVE_POOL
return result; return result;
} }
} }
@ -4279,7 +4285,7 @@ register_printf_atsign ()
if (GSFromUnicode(&b, &l, u, len, encoding, NSDefaultMallocZone(), if (GSFromUnicode(&b, &l, u, len, encoding, NSDefaultMallocZone(),
options) == YES) options) == YES)
{ {
d = [NSDataClass dataWithBytesNoCopy: b length: l]; d = [NSDataClass dataWithBytesNoCopy: b length: l freeWhenDone: YES];
} }
else else
{ {