Implemented NSZombie stuff

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@13247 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-Macdonald 2002-03-27 09:55:57 +00:00
parent e64c0a1282
commit c0b0abdbd1
7 changed files with 232 additions and 63 deletions

View file

@ -1,3 +1,11 @@
2002-03-27 Richard Frith-Macdonald <rfm@gnu.org>
* Source/GSPrivate.h: Added function to fetch boolean value from env
* Source/NSException.m: Use new function.
* Source/NSProcesInfo.m: Implement new function.
* Source/NSObject.m: Implement NSZombie functionality.
* Headers/Foundation/NSDebug.h: Document NSZombie functionality.
2002-03-25 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSTask.m: Implement code to watch for child process exit

View file

@ -111,6 +111,43 @@ GS_EXPORT NSString* GSDebugMethodMsg(id obj, SEL sel, const char *file,
int line, NSString *fmt);
#endif
/**
* Enable/disable zombies.
* <p>When an object is deallocated, its isa pointer is normally modified
* to the hexadecimal value 0xdeadface, so that any attempt to send a
* message to the deallocated object will cause a crash, and examination
* of the object within the debugger will show the 0xdeadface value ...
* making it obvious why the program crashed.
* </p>
* <p>Turning on zombies changes this behavior so that the isa pointer
* is modified to be that of the NSZombie class. When messages are
* sent to the object, intead of crashing, NSZombie will use NSLog() to
* produce an error message. By default the memory used by the object
* will not really be freed, so error messages will continue to
* be generated whenever a message is sent to the object, and the object
* instance variables will remain available for examination by the debugger.
* </p>
* The default value of this boolean is NO, but this can be controlled
* by the NSZombieEnabled environment variable.
*/
GS_EXPORT BOOL NSZombieEnabled;
/**
* Enable/disable object deallocation.
* <p>If zombies are enabled, objects are by default <em>not</em>
* deallocated, and memory leaks. The NSDeallocateZombies variable
* lets you say that the the memory used by zombies should be freed.
* </p>
* <p>Doing this makes the behavior of zombies similar to that when zombies
* are not enabled ... the memory occupied by the zombie may be re-used for
* other purposes, at which time the isa pointer may be overwritten and the
* zombie behavior will cease.
* </p>
* The default value of this boolean is NO, but this can be controlled
* by the NSDeallocateZombies environment variable.
*/
GS_EXPORT BOOL NSDeallocateZombies;
/* Debug logging which can be enabled/disabled by defining GSDIAGNOSE

View file

@ -80,7 +80,12 @@ NSDictionary *GSUserDefaultsDictionaryRepresentation();
/*
* Get one of several potentially useful flags.
*/
BOOL GSUserDefaultsFlag(GSUserDefaultFlagType type);
BOOL GSUserDefaultsFlag(GSUserDefaultFlagType type);
/**
* Get a flag from an environment variable - return def if not defined.
*/
BOOL GSEnvironmentFlag(const char *name, BOOL def);
#endif /* __GSPrivate_h_ */

View file

@ -33,7 +33,8 @@
#include <Foundation/NSThread.h>
#include <Foundation/NSDictionary.h>
#include <stdio.h>
#include <stdlib.h> // for getenv()
#include "GSPrivate.h"
static void
_preventRecursion (NSException *exception)
@ -43,7 +44,6 @@ _preventRecursion (NSException *exception)
static void
_NSFoundationUncaughtExceptionHandler (NSException *exception)
{
const char *c = getenv("CRASH_ON_ABORT");
BOOL a;
_NSUncaughtExceptionHandler = _preventRecursion;
@ -60,33 +60,7 @@ _NSFoundationUncaughtExceptionHandler (NSException *exception)
#else
a = NO; // exit() by default.
#endif
if (c != 0)
{
/*
* Use the CRASH_ON_ABORT environment variable ... if it's defined
* then we use abort(), unless it's 'no', 'false', or '0, in which
* case we use exit()
*/
if (c[0] == '0' && c[1] == 0)
{
a = NO;
}
else if ((c[0] == 'n' || c[0] == 'N') && (c[1] == 'o' || c[1] == 'O')
&& c[2] == 0)
{
a = NO;
}
else if ((c[0] == 'f' || c[0] == 'F') && (c[1] == 'a' || c[1] == 'A')
&& (c[2] == 'l' || c[2] == 'L') && (c[3] == 's' || c[3] == 'S')
&& (c[4] == 'e' || c[4] == 'E') && c[5] == 0)
{
a = NO;
}
else
{
a = YES;
}
}
a = GSEnvironmentFlag("CRASH_ON_ABORT", a);
if (a == YES)
{
abort();

View file

@ -42,8 +42,10 @@
#include <Foundation/NSThread.h>
#include <Foundation/NSNotification.h>
#include <Foundation/NSObjCRuntime.h>
#include <Foundation/NSMapTable.h>
#include <limits.h>
#include "GSPrivate.h"
#ifndef NeXT_RUNTIME
@ -61,6 +63,68 @@ static Class NSConstantStringClass;
static BOOL deallocNotifications = NO;
/*
* allocationLock is needed when running multi-threaded for retain/release
* to work reliably.
* We also use it for protecting the map table of zombie information.
*/
static objc_mutex_t allocationLock = NULL;
BOOL NSZombieEnabled = NO;
BOOL NSDeallocateZombies = NO;
@class NSZombie;
static Class zombieClass;
static NSMapTable zombieMap;
static void GSMakeZombie(NSObject *o)
{
Class c = ((id)o)->class_pointer;
((id)o)->class_pointer = zombieClass;
if (NSDeallocateZombies == NO)
{
if (allocationLock == 0)
{
objc_mutex_lock(allocationLock);
}
NSMapInsert(zombieMap, (void*)o, (void*)c);
if (allocationLock == 0)
{
objc_mutex_unlock(allocationLock);
}
}
}
static void GSLogZombie(id o, SEL sel)
{
Class c = 0;
if (NSDeallocateZombies == NO)
{
if (allocationLock == 0)
{
objc_mutex_lock(allocationLock);
}
c = NSMapGet(zombieMap, (void*)o);
if (allocationLock == 0)
{
objc_mutex_unlock(allocationLock);
}
}
if (c == 0)
{
NSLog(@"Deallocated object (0x%x) sent %@",
o, NSStringFromSelector(sel));
}
else
{
NSLog(@"Deallocated %@ (0x%x) sent %@",
NSStringFromClass(c), o, NSStringFromSelector(sel));
}
}
/*
* Reference count and memory management
@ -76,12 +140,6 @@ static BOOL deallocNotifications = NO;
*/
/*
* retain_counts_gate is needed when running multi-threaded for retain/release
* to work reliably.
*/
static objc_mutex_t retain_counts_gate = NULL;
#if GS_WITH_GC == 0 && !defined(NeXT_RUNTIME)
#define REFCNT_LOCAL 1
#define CACHE_ZONE 1
@ -165,11 +223,11 @@ NSExtraRefCount(id anObject)
void
NSIncrementExtraRefCount(id anObject)
{
if (retain_counts_gate != 0)
if (allocationLock != 0)
{
objc_mutex_lock(retain_counts_gate);
objc_mutex_lock(allocationLock);
((obj)anObject)[-1].retained++;
objc_mutex_unlock (retain_counts_gate);
objc_mutex_unlock (allocationLock);
}
else
{
@ -178,11 +236,11 @@ NSIncrementExtraRefCount(id anObject)
}
#define NSIncrementExtraRefCount(X) ({ \
if (retain_counts_gate != 0) \
if (allocationLock != 0) \
{ \
objc_mutex_lock(retain_counts_gate); \
objc_mutex_lock(allocationLock); \
((obj)(X))[-1].retained++; \
objc_mutex_unlock(retain_counts_gate); \
objc_mutex_unlock(allocationLock); \
} \
else \
{ \
@ -193,17 +251,17 @@ NSIncrementExtraRefCount(id anObject)
BOOL
NSDecrementExtraRefCountWasZero(id anObject)
{
if (retain_counts_gate != 0)
if (allocationLock != 0)
{
objc_mutex_lock(retain_counts_gate);
objc_mutex_lock(allocationLock);
if (((obj)anObject)[-1].retained-- == 0)
{
objc_mutex_unlock(retain_counts_gate);
objc_mutex_unlock(allocationLock);
return YES;
}
else
{
objc_mutex_unlock(retain_counts_gate);
objc_mutex_unlock(allocationLock);
return NO;
}
}
@ -245,9 +303,9 @@ NSIncrementExtraRefCount (id anObject)
{
GSIMapNode node;
if (retain_counts_gate != 0)
if (allocationLock != 0)
{
objc_mutex_lock(retain_counts_gate);
objc_mutex_lock(allocationLock);
node = GSIMapNodeForKey(&retain_counts, (GSIMapKey)anObject);
if (node != 0)
{
@ -257,7 +315,7 @@ NSIncrementExtraRefCount (id anObject)
{
GSIMapAddPair(&retain_counts, (GSIMapKey)anObject, (GSIMapVal)1);
}
objc_mutex_unlock(retain_counts_gate);
objc_mutex_unlock(allocationLock);
}
else
{
@ -278,13 +336,13 @@ NSDecrementExtraRefCountWasZero (id anObject)
{
GSIMapNode node;
if (retain_counts_gate != 0)
if (allocationLock != 0)
{
objc_mutex_lock(retain_counts_gate);
objc_mutex_lock(allocationLock);
node = GSIMapNodeForKey(&retain_counts, (GSIMapKey)anObject);
if (node == 0)
{
objc_mutex_unlock(retain_counts_gate);
objc_mutex_unlock(allocationLock);
return YES;
}
NSCAssert(node->value.uint > 0, NSInternalInconsistencyException);
@ -292,7 +350,7 @@ NSDecrementExtraRefCountWasZero (id anObject)
{
GSIMapRemoveKey((GSIMapTable)&retain_counts, (GSIMapKey)anObject);
}
objc_mutex_unlock(retain_counts_gate);
objc_mutex_unlock(allocationLock);
}
else
{
@ -316,9 +374,9 @@ NSExtraRefCount (id anObject)
GSIMapNode node;
unsigned ret;
if (retain_counts_gate != 0)
if (allocationLock != 0)
{
objc_mutex_lock(retain_counts_gate);
objc_mutex_lock(allocationLock);
node = GSIMapNodeForKey(&retain_counts, (GSIMapKey)anObject);
if (node == 0)
{
@ -328,7 +386,7 @@ NSExtraRefCount (id anObject)
{
ret = node->value.uint;
}
objc_mutex_unlock(retain_counts_gate);
objc_mutex_unlock(allocationLock);
}
else
{
@ -501,8 +559,19 @@ NSDeallocateObject(NSObject *anObject)
#ifndef NDEBUG
GSDebugAllocationRemove(((id)anObject)->class_pointer, (id)anObject);
#endif
((id)anObject)->class_pointer = (void*) 0xdeadface;
NSZoneFree(z, o);
if (NSZombieEnabled == YES)
{
GSMakeZombie(anObject);
if (NSDeallocateZombies == YES)
{
NSZoneFree(z, o);
}
}
else
{
((id)anObject)->class_pointer = (void*) 0xdeadface;
NSZoneFree(z, o);
}
}
return;
}
@ -545,8 +614,19 @@ NSDeallocateObject(NSObject *anObject)
#ifndef NDEBUG
GSDebugAllocationRemove(((id)anObject)->class_pointer, (id)anObject);
#endif
((id)anObject)->class_pointer = (void*) 0xdeadface;
NSZoneFree(z, anObject);
if (NSZombieEnabled == YES)
{
GSMakeZombie(anObject);
if (NSDeallocateZombies == YES)
{
NSZoneFree(z, anObject);
}
}
else
{
((id)anObject)->class_pointer = (void*) 0xdeadface;
NSZoneFree(z, anObject);
}
}
return;
}
@ -562,7 +642,7 @@ NSShouldRetainWithZone (NSObject *anObject, NSZone *requestedZone)
return YES;
#else
return (!requestedZone || requestedZone == NSDefaultMallocZone()
|| GSObjCZone(anObject) == requestedZone);
|| GSObjCZone(anObject) == requestedZone);
#endif
}
@ -587,9 +667,9 @@ static BOOL double_release_check_enabled = NO;
+ (void) _becomeMultiThreaded: (NSNotification)aNotification
{
if (retain_counts_gate == 0)
if (allocationLock == 0)
{
retain_counts_gate = objc_mutex_allocate();
allocationLock = objc_mutex_allocate();
}
}
@ -630,6 +710,14 @@ static BOOL double_release_check_enabled = NO;
// Create the global lock
gnustep_global_lock = [[NSRecursiveLock alloc] init];
// Zombie management stuff.
zombieClass = [NSZombie class];
zombieMap = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
NSNonOwnedPointerMapValueCallBacks, 0);
NSZombieEnabled = GSEnvironmentFlag("NSZombieEnabled", NO);
NSDeallocateZombies = GSEnvironmentFlag("NSDeallocateZombies", NO);
autorelease_class = [NSAutoreleasePool class];
autorelease_sel = @selector(addObject:);
autorelease_imp = [autorelease_class methodForSelector: autorelease_sel];
@ -1542,3 +1630,28 @@ _fastMallocBuffer(unsigned size)
}
@end
@interface NSZombie
- (retval_t) forward:(SEL)aSel :(arglist_t)argFrame;
- (void) forwardInvocation: (NSInvocation*)anInvocation;
@end
@implementation NSZombie
- (retval_t) forward:(SEL)aSel :(arglist_t)argFrame
{
GSLogZombie(self, aSel);
return 0;
}
- (void) forwardInvocation: (NSInvocation*)anInvocation
{
unsigned size = [[anInvocation methodSignature] methodReturnLength];
unsigned char v[size];
memset(v, '\0', size);
GSLogZombie(self, [anInvocation selector]);
[anInvocation setReturnValue: (void*)v];
return;
}
@end

View file

@ -78,6 +78,8 @@
#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSHost.h>
#include "GSPrivate.h"
/* This error message should be called only if the private main function
* was not executed successfully. This may happen ONLY if another library
* or kit defines its own main function (as gnustep-base does).
@ -679,3 +681,32 @@ BOOL GSDebugSet(NSString *level)
return YES;
}
BOOL
GSEnvironmentFlag(const char *name, BOOL def)
{
const char *c = getenv(name);
BOOL a = def;
if (c != 0)
{
a = NO;
if ((c[0] == 'y' || c[0] == 'Y') && (c[1] == 'e' || c[1] == 'E')
&& (c[2] == 's' || c[2] == 'S') && c[3] == 0)
{
a = YES;
}
else if ((c[0] == 't' || c[0] == 'T') && (c[1] == 'r' || c[1] == 'R')
&& (c[2] == 'u' || c[2] == 'U') && (c[3] == 'e' || c[3] == 'E')
&& c[4] == 0)
{
a = YES;
}
else if (isdigit(c[0]) && c[0] != '0')
{
a = YES;
}
}
return a;
}

View file

@ -9,6 +9,7 @@ int main ()
id o = [NSObject new];
printf ("Hello from object at 0x%x\n", (unsigned)[o self]);
[o release];
o = [NSString stringWithFormat: @"/proc/%d/status", getpid()];
NSLog(@"'%@'", o);
o = [NSString stringWithContentsOfFile: o];