mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 16:33:29 +00:00
Start stuff for handling cleanup on process exit.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@33344 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
3d1656a7bd
commit
8b2ffba3d9
12 changed files with 246 additions and 9 deletions
25
ChangeLog
25
ChangeLog
|
@ -1,3 +1,28 @@
|
|||
2011-06-19 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Headers/GNUstepBase/NSObject+GNUstepBase.h:
|
||||
* Source/Additions/NSObject+GNUstepBase.m:
|
||||
Add new category with methods for cleanup of long-lived data structures
|
||||
on process exit.
|
||||
* Source/NSObject.m: Check GNUSTEP_SHOULD_CLEAN_UP environment variable
|
||||
and call +shouldCleanUp: if it is set.
|
||||
* Documentation/Base.gsdoc: Document new environment variable.
|
||||
* Source/GSLocale.m:
|
||||
* Source/NSDistributedNotificationCenter.m:
|
||||
* Source/NSUserDefaults.m:
|
||||
* Source/NSNotificationCenter.m:
|
||||
* Source/GSPrivate.h:
|
||||
* Source/externs.m:
|
||||
* Source/NSString.m:
|
||||
Add some cleanup support.
|
||||
|
||||
Classes may perform cleanup in a +atExit method which is automatically
|
||||
called on process exit.
|
||||
Classes may use [NSObject+leak:] to 'leak' an object so that it is
|
||||
retained for the life of the process, but released immediately before
|
||||
the +atExit methods are called if GNUSTEP_SHOULD_CLEAN_UP is YES.
|
||||
See the NSObject(AtExit) category documentation for details.
|
||||
|
||||
2011-06-18 Sebastian Reitenbach <sebastia@l00-bugdead-prods.de>
|
||||
|
||||
* Source/NSObject.m: fix for clang
|
||||
|
|
|
@ -269,6 +269,22 @@ notice and this notice are preserved.
|
|||
core dump on systems where that is possible.
|
||||
</p>
|
||||
</desc>
|
||||
<term>GNUSTEP_SHOULD_CLEAN_UP</term>
|
||||
<desc>
|
||||
<p>
|
||||
When this is set to YES, the GNUstep extension method
|
||||
+setShouldCleanUp: is called when the NSObject class is
|
||||
initialised, this turns on recording of some intentionally
|
||||
leaked memory (data structures intended to persist for the
|
||||
whole life of the process), and activates cleanup of that
|
||||
memory on process exit so that external tools such as
|
||||
valgrind will not report the memory as possibly lost.
|
||||
</p>
|
||||
<p>
|
||||
Use of this facility is a work in progress ... many classes
|
||||
do not yet clean up after themselves when this is enabled.
|
||||
</p>
|
||||
</desc>
|
||||
<term>GNUSTEP_STACK_TRACE</term>
|
||||
<desc>
|
||||
<p>
|
||||
|
|
|
@ -96,6 +96,67 @@ extern "C" {
|
|||
|
||||
@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
|
||||
* by gnustep-base if the GNUSTEP_SHOULD_CLEAN_UP environment variable is
|
||||
* set to YES).<br />
|
||||
* Your class then has two options for performing cleanup when the process
|
||||
* ends:
|
||||
* <p>1. Use the +leak: method to register objects which are simply to be
|
||||
* retained until the process ends, and then either ignored or released
|
||||
* depending on the cleanup setting in force. This mechanism is simple
|
||||
* 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.
|
||||
* </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
|
||||
* 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;
|
||||
|
||||
/** 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
|
||||
* calls to +atExit methods are performed) if cleanup is enabled.<br />
|
||||
* Returns its argument.
|
||||
*/
|
||||
+ (id) leak: (id)anObject;
|
||||
|
||||
/** 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
|
||||
* method as well.<br />
|
||||
* The GNUstep Base library calls this method with the value obtained from
|
||||
* the GNUSTEP_SHOULD_CLEAN_UP environment variable when NSObject is
|
||||
* initialised.
|
||||
*/
|
||||
+ (void) setShouldCleanUp: (BOOL)aFlag;
|
||||
|
||||
/** Returns a flag indicating whether the receiver should clean up
|
||||
* its data structures etc at process exit.<br />
|
||||
* The NSObject implementation returns the value set by the +setShouldCleanUp:
|
||||
* method but subclasses may override this.
|
||||
*/
|
||||
+ (BOOL) shouldCleanUp;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* OS_API_VERSION */
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
|
|
@ -23,7 +23,9 @@
|
|||
|
||||
*/
|
||||
#import "common.h"
|
||||
#import "Foundation/NSArray.h"
|
||||
#import "Foundation/NSException.h"
|
||||
#import "Foundation/NSLock.h"
|
||||
#import "GNUstepBase/NSObject+GNUstepBase.h"
|
||||
#import "GNUstepBase/NSDebug+GNUstepBase.h"
|
||||
|
||||
|
@ -125,3 +127,92 @@
|
|||
|
||||
@end
|
||||
|
||||
static NSMutableArray *leaked = nil;
|
||||
static NSLock *leakLock = nil;
|
||||
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++)
|
||||
{
|
||||
Class c = classes[index];
|
||||
Method m = class_getClassMethod(c, @selector(atExit));
|
||||
|
||||
if (m != 0)
|
||||
{
|
||||
Class s = class_getSuperclass(c);
|
||||
|
||||
if (0 == s || class_getClassMethod(s, @selector(atExit)) != m)
|
||||
{
|
||||
[c atExit];
|
||||
}
|
||||
}
|
||||
}
|
||||
free(classes);
|
||||
}
|
||||
}
|
||||
|
||||
@implementation NSObject(atExit)
|
||||
|
||||
+ (void) enableAtExit
|
||||
{
|
||||
if (nil == leakLock)
|
||||
{
|
||||
leakLock = [NSLock new];
|
||||
atexit(handleExit);
|
||||
}
|
||||
}
|
||||
|
||||
+ (void) atExit
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
+ (id) leak: (id)anObject
|
||||
{
|
||||
[leakLock lock];
|
||||
if (nil == leaked)
|
||||
{
|
||||
leaked = [NSMutableArray new];
|
||||
}
|
||||
[leaked addObject: anObject];
|
||||
[leakLock unlock];
|
||||
return anObject;
|
||||
}
|
||||
|
||||
+ (void) setShouldCleanUp: (BOOL)aFlag
|
||||
{
|
||||
if (YES == aFlag)
|
||||
{
|
||||
[self enableAtExit];
|
||||
shouldCleanUp = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldCleanUp = NO;
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL) shouldCleanUp
|
||||
{
|
||||
return shouldCleanUp;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
*/
|
||||
#import "common.h"
|
||||
#import "GNUstepBase/GSLocale.h"
|
||||
#import "GNUstepBase/NSObject+GNUstepBase.h"
|
||||
#import "Foundation/NSDictionary.h"
|
||||
#import "Foundation/NSArray.h"
|
||||
#import "Foundation/NSLock.h"
|
||||
|
@ -218,7 +219,7 @@ GSDomainFromDefaultLocale(void)
|
|||
*/
|
||||
if (saved == nil)
|
||||
{
|
||||
saved = [dict mutableCopy];
|
||||
saved = [NSObject leak: dict];
|
||||
}
|
||||
[gnustep_global_lock unlock];
|
||||
return saved;
|
||||
|
|
|
@ -161,8 +161,8 @@ __attribute__((unused)) static void GSFreeTempBuffer(void **b)
|
|||
* Yet the optimization of the stored hash value is currently deemed
|
||||
* more important.
|
||||
*/
|
||||
#define GS_REPLACE_CONSTANT_STRING(ID) \
|
||||
ID = [[NSStringClass alloc] initWithCString: [ID cString]]
|
||||
#define GS_REPLACE_CONSTANT_STRING(ID) [(ID = [NSObject \
|
||||
leak: [[NSString alloc] initWithCString: [ID cString]]]) release]
|
||||
/* Using cString here is OK here
|
||||
because NXConstantString returns a pointer
|
||||
to it's internal pointer. */
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#import "Foundation/NSRunLoop.h"
|
||||
#import "Foundation/NSTask.h"
|
||||
#import "GNUstepBase/NSTask+GNUstepBase.h"
|
||||
#import "GNUstepBase/NSObject+GNUstepBase.h"
|
||||
#import "Foundation/NSDistributedNotificationCenter.h"
|
||||
#import "Foundation/NSUserDefaults.h"
|
||||
#import "Foundation/NSHost.h"
|
||||
|
@ -151,7 +152,8 @@ static NSDistributedNotificationCenter *netCenter = nil;
|
|||
NSAllocateObject(self, 0, NSDefaultMallocZone());
|
||||
tmp->_centerLock = [NSRecursiveLock new];
|
||||
tmp->_type = RETAIN(NSLocalNotificationCenterType);
|
||||
locCenter = tmp;
|
||||
locCenter = [NSObject leak: tmp];
|
||||
[tmp release];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
|
@ -179,7 +181,8 @@ static NSDistributedNotificationCenter *netCenter = nil;
|
|||
NSAllocateObject(self, 0, NSDefaultMallocZone());
|
||||
tmp->_centerLock = [NSRecursiveLock new];
|
||||
tmp->_type = RETAIN(GSPublicNotificationCenterType);
|
||||
pubCenter = tmp;
|
||||
pubCenter = [NSObject leak: tmp];
|
||||
[tmp release];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
|
@ -207,7 +210,8 @@ static NSDistributedNotificationCenter *netCenter = nil;
|
|||
NSAllocateObject(self, 0, NSDefaultMallocZone());
|
||||
tmp->_centerLock = [NSRecursiveLock new];
|
||||
tmp->_type = RETAIN(GSNetworkNotificationCenterType);
|
||||
netCenter = tmp;
|
||||
netCenter = [NSObject leak: tmp];
|
||||
[tmp release];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
|
|
|
@ -417,6 +417,8 @@ static void endNCTable(NCTable *t)
|
|||
GSIMapNode n0;
|
||||
Observation *l;
|
||||
|
||||
TEST_RELEASE(t->_lock);
|
||||
|
||||
/*
|
||||
* Free observations without notification names or numbers.
|
||||
*/
|
||||
|
@ -472,8 +474,6 @@ static void endNCTable(NCTable *t)
|
|||
}
|
||||
NSZoneFree(NSDefaultMallocZone(), t->chunks);
|
||||
NSZoneFree(NSDefaultMallocZone(), t);
|
||||
|
||||
TEST_RELEASE(t->_lock);
|
||||
}
|
||||
|
||||
static NCTable *newNCTable(void)
|
||||
|
@ -684,6 +684,14 @@ purgeCollectedFromMapNode(GSIMapTable map, GSIMapNode node)
|
|||
|
||||
static NSNotificationCenter *default_center = nil;
|
||||
|
||||
+ (void) atExit
|
||||
{
|
||||
id tmp = default_center;
|
||||
|
||||
default_center = nil;
|
||||
[tmp release];
|
||||
}
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
if (self == [NSNotificationCenter class])
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#import "Foundation/NSNotification.h"
|
||||
#import "Foundation/NSMapTable.h"
|
||||
#import "GNUstepBase/GSLocale.h"
|
||||
#import "GNUstepBase/NSObject+GNUstepBase.h"
|
||||
#ifdef HAVE_LOCALE_H
|
||||
#include <locale.h>
|
||||
#endif
|
||||
|
@ -71,7 +72,6 @@
|
|||
#define IN_NSOBJECT_M 1
|
||||
#import "GSPrivate.h"
|
||||
|
||||
|
||||
/* When this is `YES', every call to release/autorelease, checks to
|
||||
make sure isn't being set up to release itself too many times.
|
||||
This does not need mutex protection. */
|
||||
|
@ -1112,6 +1112,14 @@ objc_create_block_classes_as_subclasses_of(Class super);
|
|||
* string constants etc.
|
||||
*/
|
||||
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
|
||||
|
|
|
@ -558,6 +558,19 @@ handle_printf_atsign (FILE *stream,
|
|||
}
|
||||
#endif /* HAVE_REGISTER_PRINTF_FUNCTION */
|
||||
|
||||
+ (void) atExit
|
||||
{
|
||||
NSMapTable *t = placeholderMap;
|
||||
|
||||
if (0 != t)
|
||||
{
|
||||
DESTROY(defaultPlaceholderString);
|
||||
DESTROY(placeholderLock);
|
||||
placeholderMap = 0;
|
||||
NSFreeMapTable(t);
|
||||
}
|
||||
}
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
/*
|
||||
|
|
|
@ -363,6 +363,15 @@ newLanguages(NSArray *oldNames)
|
|||
*/
|
||||
@implementation NSUserDefaults: NSObject
|
||||
|
||||
+ (void) atExit
|
||||
{
|
||||
id tmp;
|
||||
|
||||
tmp = sharedDefaults;
|
||||
sharedDefaults = nil;
|
||||
[tmp release];
|
||||
}
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
if (self == [NSUserDefaults class])
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#import "Foundation/NSArray.h"
|
||||
#import "Foundation/NSException.h"
|
||||
#import "GNUstepBase/NSObject+GNUstepBase.h"
|
||||
|
||||
#import "GSPrivate.h"
|
||||
|
||||
|
|
Loading…
Reference in a new issue