garbage collecting fixes

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@27638 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 2009-01-20 10:15:52 +00:00
parent d8cf9c2d35
commit e82bd33ab1
6 changed files with 275 additions and 156 deletions

View file

@ -1,3 +1,17 @@
2009-01-20 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSKeyValueObserving.m: Automtically remove collected observers
In a garbage collecting environment.
* Headers/Foundation/NSZone.h:
* Source/NSZone.m:
Add new functions for handling weak pointers. This allows all code
which uses the boehm garbage collector header to be localised in
NSZone.m and NSGarbageCollector.m
* Source/NSNotificationCenter.m:
Update to use new functions.
* Source/Additions/GSInsensitiveDictionary.m:
Fix to use scanned memory in a garbage collecting environment.
2009-01-19 Richard Frith-Macdonald <rfm@gnu.org>
* configure.ac: Add options from pathconfig

View file

@ -62,37 +62,6 @@ typedef struct _NSZone NSZone;
extern "C" {
#endif
/**
* <code>NSZoneStats</code> is the structure returned by the NSZoneStats()
* function that summarizes the current usage of a zone. It is similar to
* the structure <em>mstats</em> in the GNU C library. It has 5 fields of
* type <code>size_t</code>-
* <deflist>
* <term><code>bytes_total</code></term>
* <desc>
* This is the total size of memory managed by the zone, in bytes.</desc>
* <term><code>chunks_used</code></term>
* <desc>This is the number of memory chunks in use in the zone.</desc>
* <term><code>bytes_used</code></term>
* <desc>This is the number of bytes in use.</desc>
* <term><code>chunks_free</code></term>
* <desc>This is the number of memory chunks that are not in use.</desc>
* <term><code>bytes_free</code></term>
* <desc>
* This is the number of bytes managed by the zone that are not in use.
* </desc>
* </deflist>
*/
struct NSZoneStats
{
size_t bytes_total;
size_t chunks_used;
size_t bytes_used;
size_t chunks_free;
size_t bytes_free;
};
/**
* Primary structure representing an <code>NSZone</code>. Technically it
* consists of a set of function pointers for zone upkeep functions plus some
@ -131,35 +100,13 @@ struct _NSZone
NSZone *next;
};
/**
* Try to get more memory - the normal process has failed.
* If we can't do anything, just return a null pointer.
* Try to do some logging if possible.
*/
void*
GSOutOfMemory(size_t size, BOOL retry);
NSZone*
GS_EXPORT NSZone*
NSCreateZone (size_t start, size_t gran, BOOL canFree);
NSZone*
GS_EXPORT NSZone*
NSDefaultMallocZone (void);
/**
* Returns the default zone used for memory allocation, created at startup.
* This zone cannot be recycled.
*/
NSZone*
GSAtomicMallocZone (void);
/**
* Returns the default zone used for scanned memory allocation ... a
* garbage collectable chunk of memory which is scanned for pointers.
*/
NSZone*
GSScannedMallocZone (void);
NSZone*
GS_EXPORT NSZone*
NSZoneFromPointer (void *ptr);
/**
@ -169,7 +116,7 @@ NSZoneFromPointer (void *ptr);
* allocate and no more can be obtained from system, unless using the
* default zone, in which case NULL is returned.
*/
void*
GS_EXPORT void*
NSZoneMalloc (NSZone *zone, size_t size);
/**
@ -179,7 +126,7 @@ NSZoneMalloc (NSZone *zone, size_t size);
* allocate and no more can be obtained from system, unless using the
* default zone, in which case NULL is returned.
*/
void*
GS_EXPORT void*
NSZoneCalloc (NSZone *zone, size_t elems, size_t bytes);
/**
@ -189,7 +136,7 @@ NSZoneCalloc (NSZone *zone, size_t elems, size_t bytes);
* zone and no more memory can be obtained from the system, unless using the
* default zone, in which case NULL is returned.
*/
void*
GS_EXPORT void*
NSZoneRealloc (NSZone *zone, void *ptr, size_t size);
/**
@ -199,7 +146,7 @@ NSZoneRealloc (NSZone *zone, void *ptr, size_t size);
* must simply equal the number of allocation calls. The default zone, on the
* other hand, cannot be recycled.
*/
void
GS_EXPORT void
NSRecycleZone (NSZone *zone);
/**
@ -208,36 +155,121 @@ NSRecycleZone (NSZone *zone);
* returns it to zone. Note, if this is a nonfreeable zone, the memory is
* not actually freed, but the count of number of free()s is updated.
*/
void
GS_EXPORT void
NSZoneFree (NSZone *zone, void *ptr);
/**
* Sets name of the given zone (useful for debugging and logging).
*/
void
GS_EXPORT void
NSSetZoneName (NSZone *zone, NSString *name);
/**
* Sets name of the given zone (useful for debugging and logging).
*/
NSString*
GS_EXPORT NSString*
NSZoneName (NSZone *zone);
#if OS_API_VERSION(GS_API_NONE, GS_API_NONE)
/**
/** Deprecated ...<br />
* Checks integrity of a zone. Not defined by OpenStep or OS X.
*/
BOOL
NSZoneCheck (NSZone *zone);
/**
* <code>NSZoneStats</code> is the structure returned by the NSZoneStats()
* function that summarizes the current usage of a zone. It is similar to
* the structure <em>mstats</em> in the GNU C library. It has 5 fields of
* type <code>size_t</code>-
* <deflist>
* <term><code>bytes_total</code></term>
* <desc>
* This is the total size of memory managed by the zone, in bytes.</desc>
* <term><code>chunks_used</code></term>
* <desc>This is the number of memory chunks in use in the zone.</desc>
* <term><code>bytes_used</code></term>
* <desc>This is the number of bytes in use.</desc>
* <term><code>chunks_free</code></term>
* <desc>This is the number of memory chunks that are not in use.</desc>
* <term><code>bytes_free</code></term>
* <desc>
* This is the number of bytes managed by the zone that are not in use.
* </desc>
* </deflist>
*/
struct NSZoneStats
{
size_t bytes_total;
size_t chunks_used;
size_t bytes_used;
size_t chunks_free;
size_t bytes_free;
};
/** Deprecated ...<br />
* Obtain statistics about the zone. Implementation emphasis is on
* correctness, not speed. Not defined by OpenStep or OS X.
*/
struct NSZoneStats
NSZoneStats (NSZone *zone);
/**
* Try to get more memory - the normal process has failed.
* If we can't do anything, just return a null pointer.
* Try to do some logging if possible.
*/
void*
GSOutOfMemory(size_t size, BOOL retry);
/**
* Returns the default zone used for memory allocation, created at startup.
* This zone cannot be recycled.
*/
GS_EXPORT NSZone*
GSAtomicMallocZone (void);
/**
* Returns the default zone used for scanned memory allocation ... a
* garbage collectable chunk of memory which is scanned for pointers.
*/
GS_EXPORT NSZone*
GSScannedMallocZone (void);
/**
* Called during +initialize to tell the class that instances created
* in future should have the specified instance variable as a weak
* pointer for garbage collection.<br />
* NB. making a pointer weak does not mean that it is automatically
* zeroed when the object it points to is garbage collected. To get that
* behavior you must asign values to the pointer using the
* GSAssignZeroingWeakPointer() function.<rb />
* This function has no effect if the system is
* not built for garbage collection.
*/
GS_EXPORT void
GSMakeWeakPointer(Class class, const char *iVarName);
/**
* This function must be used to assign a value to a zeroing weak pointer.<br />
* A zeroing weak pointer is one where, when the garbage collector collects
* the object pointed to, it also clears the weak pointer.<br />
* Assigning zero (nil) will always succeed and has the effect of telling the
* garbage collector that it no longer needs to track the previously assigned
* object. Apart from that case, a source needs to be garbage collectable for
* this function to work, and using a non-garbage collectable value will
* cause the function to return NO.<br />
* The destination object (watching the source object) must also be memory
* allocated by the garbage colleector, and if it is not the function will
* return NO.<br />
* If garbage collection is not in use, this function performs a simple
* assignment returning YES, unless destination is null in which case it
* returns NO.
*/
GS_EXPORT BOOL
GSAssignZeroingWeakPointer(void **destination, void *source);
#endif
GS_EXPORT NSUInteger

View file

@ -159,7 +159,11 @@ static SEL objSel;
[aCoder decodeValueOfObjCType: @encode(unsigned)
at: &count];
#if GS_WITH_GC
GSIMapInitWithZoneAndCapacity(&map, GSScannedMallocZone(), count);
#else
GSIMapInitWithZoneAndCapacity(&map, GSObjCZone(self), count);
#endif
while (count-- > 0)
{
(*imp)(aCoder, sel, type, &key);
@ -175,7 +179,12 @@ static SEL objSel;
{
unsigned int i;
#if GS_WITH_GC
GSIMapInitWithZoneAndCapacity(&map, GSScannedMallocZone(), c);
#else
GSIMapInitWithZoneAndCapacity(&map, GSObjCZone(self), c);
#endif
for (i = 0; i < c; i++)
{
GSIMapNode node;
@ -214,9 +223,14 @@ static SEL objSel;
- (id) initWithDictionary: (NSDictionary*)other
copyItems: (BOOL)shouldCopy
{
NSZone *z = GSObjCZone(self);
NSZone *z;
unsigned c = [other count];
#if GS_WITH_GC
z = GSScannedMallocZone();
#else
z = GSObjCZone(self);
#endif
GSIMapInitWithZoneAndCapacity(&map, z, c);
if (c > 0)
@ -369,7 +383,11 @@ static SEL objSel;
/* Designated initialiser */
- (id) initWithCapacity: (unsigned)cap
{
#if GS_WITH_GC
GSIMapInitWithZoneAndCapacity(&map, GSScannedMallocZone(), cap);
#else
GSIMapInitWithZoneAndCapacity(&map, GSObjCZone(self), cap);
#endif
return self;
}

View file

@ -138,7 +138,7 @@ static inline void setup()
@interface GSKVOObservation : NSObject
{
@public
NSObject *observer; // Not retained
NSObject *observer; // Not retained (zeroing weak pointer)
void *context;
int options;
}
@ -586,7 +586,8 @@ replacementForClass(Class c)
if (!found)
{
NSDebugLLog(@"KVC", @"class %@ not KVC complient for %@", original, aKey);
NSDebugLLog(@"KVC", @"class %@ not KVC complient for %@",
original, aKey);
/*
[NSException raise: NSInvalidArgumentException
format: @"class not KVC complient for %@", aKey];
@ -804,6 +805,12 @@ replacementForClass(Class c)
@implementation GSKVOObservation
#if GS_WITH_GC
+ (void) initialize
{
GSMakeWeakPointer(self, "observer");
}
#endif
@end
@implementation GSKVOPathInfo
@ -956,17 +963,26 @@ replacementForClass(Class c)
o = [pathInfo->observations objectAtIndex: count];
if (o->observer == anObserver)
{
o->observer = anObserver;
o->context = aContext;
o->options = options;
observation = o;
}
#if GS_WITH_GC
else if (o->observer == nil)
{
/* The observer for thsi observation must have been collected.
*/
[pathInfo->observations removeObjectAtIndex: count];
continue;
}
#endif
pathInfo->allOptions |= o->options;
}
if (observation == nil)
{
observation = [GSKVOObservation new];
observation->observer = anObserver;
GSAssignZeroingWeakPointer((void**)&observation->observer,
(void*)anObserver);
observation->context = aContext;
observation->options = options;
[pathInfo->observations addObject: observation];
@ -1050,7 +1066,7 @@ replacementForClass(Class c)
GSKVOObservation *o;
o = [pathInfo->observations objectAtIndex: count];
if (o->observer == anObserver)
if (o->observer == anObserver || o->observer == nil)
{
[pathInfo->observations removeObjectAtIndex: count];
if ([pathInfo->observations count] == 0)
@ -1088,6 +1104,14 @@ replacementForClass(Class c)
context = o->context;
break;
}
#if GS_WITH_GC
else if (o->observer == nil)
{
/* The observer for thsi observation must have been collected.
*/
[pathInfo->observations removeObjectAtIndex: count];
}
#endif
}
}
[iLock unlock];

View file

@ -41,7 +41,6 @@
* a collected observer removed.
*/
#if GS_WITH_GC
#include <gc.h>
#define purgeCollected(X) (X = listPurge(X, nil))
#else
#define purgeCollected(X) (X)
@ -437,7 +436,7 @@ static void obsFree(Observation *o)
NCTable *t = o->link;
#if GS_WITH_GC
GC_unregister_disappearing_link((GC_PTR*)&o->observer);
GSAssignZeroingWeakPointer((void**)&o->observer, 0);
#endif
o->link = (NCTable*)t->freeList;
t->freeList = o;
@ -705,23 +704,14 @@ static NSNotificationCenter *default_center = nil;
o = obsNew(TABLE);
o->selector = selector;
o->method = method;
#if GS_WITH_GC
GSAssignZeroingWeakPointer((void**)&o->observer, (void*)observer);
#else
o->observer = observer;
#endif
o->retained = 0;
o->next = 0;
#if GS_WITH_GC
/* Ensure that if the observer is garbage collected, we clear the
* oservation so that we don't end up sending notifications to the
* deallocated object.
* The observer must be a real GC-allocated object or this mechanism
* can't be used.
*/
if (GC_base(observer) != 0)
{
GC_general_register_disappearing_link((GC_PTR*)&o->observer, observer);
}
#endif
if (object != nil)
{
object = CHEATGC(object);

View file

@ -1837,14 +1837,8 @@ NSReallocateCollectable(void *ptr, NSUInteger size, NSUInteger options)
#define GS_ZONE_ATTR __attribute__((unused))
#endif
#ifndef GS_WITH_GC
#define GS_WITH_GC 0
#endif
#if GS_WITH_GC
#include <gc.h>
NSZone*
NSCreateZone (size_t start, size_t gran, BOOL canFree)
{
@ -1875,76 +1869,11 @@ NSZoneFromPointer (void *ptr)
return &default_zone;
}
void*
NSZoneMalloc (NSZone *zone, size_t size)
{
void *ptr;
if (zone == GSAtomicMallocZone())
ptr = (void*)GC_MALLOC_ATOMIC(size);
else if (zone == GSScannedMallocZone())
ptr = (void*)GC_MALLOC(size);
else
ptr = (void*)malloc(size);
if (ptr == 0)
ptr = GSOutOfMemory(size, YES);
return ptr;
}
void*
NSZoneCalloc (NSZone *zone, size_t elems, size_t bytes)
{
size_t size = elems * bytes;
void *ptr;
if (zone == &atomic_zone)
ptr = (void*)GC_MALLOC_ATOMIC(size);
else if (zone == &scanned_zone)
ptr = (void*)GC_MALLOC(size);
else
ptr = (void*)malloc(size);
if (ptr == 0)
ptr = GSOutOfMemory(size, NO);
memset(ptr, '\0', size);
return ptr;
}
void*
NSZoneRealloc (NSZone *zone, void *ptr, size_t size)
{
if (GC_base(ptr) != 0)
{
ptr = GC_REALLOC(ptr, size);
}
else
{
ptr = realloc(ptr, size);
}
if (ptr == 0)
GSOutOfMemory(size, NO);
return ptr;
}
void
NSRecycleZone (NSZone *zone)
{
}
void
NSZoneFree (NSZone *zone, void *ptr)
{
if (GC_base(ptr) != 0)
{
GC_FREE(ptr);
}
else
{
free(ptr);
}
}
void
NSSetZoneName (NSZone *zone, NSString *name)
{
@ -1969,6 +1898,101 @@ NSZoneStats NSZoneStats (NSZone *zone)
return stats;
}
void
GSMakeWeakPointer(Class class, const char *iVarName)
{
class_ivar_set_gcinvisible(class, iVarName, YES);
}
#include <gc.h>
BOOL
GSAssignZeroingWeakPointer(void **destination, void *source)
{
if (GC_base(destination) == 0)
{
return NO; // Destination is not in garbage collection system.
}
if (*destination == source)
{
return YES; // Already assigned.
}
if (source != 0 && GC_base(source) == 0)
{
return NO; // Source is not garbage collectable.
}
if (*destination != 0)
{
GC_unregister_disappearing_link((GC_PTR*)destination);
}
*destination = source;
GC_general_register_disappearing_link((GC_PTR*)destination, source);
return YES;
}
void*
NSZoneMalloc (NSZone *zone, size_t size)
{
return NSZoneCalloc(zone, 1, size);
}
void*
NSZoneCalloc (NSZone *zone, size_t elems, size_t bytes)
{
size_t size = elems * bytes;
void *ptr;
if (zone == &atomic_zone)
{
ptr = (void*)GC_MALLOC_ATOMIC(size);
}
else if (zone == &scanned_zone)
{
ptr = (void*)GC_MALLOC(size);
}
else
{
ptr = (void*)malloc(size);
if (ptr == 0)
{
ptr = GSOutOfMemory(size, NO);
}
if (ptr != 0)
{
memset(ptr, '\0', size);
}
}
return ptr;
}
void*
NSZoneRealloc (NSZone *zone, void *ptr, size_t size)
{
if (GC_base(ptr) != 0)
{
ptr = GC_REALLOC(ptr, size);
}
else
{
ptr = realloc(ptr, size);
}
return ptr;
}
void
NSZoneFree (NSZone *zone, void *ptr)
{
if (GC_base(ptr) != 0)
{
GC_FREE(ptr);
}
else
{
free(ptr);
}
}
#else /* GS_WITH_GC */
NSZone*
@ -1989,6 +2013,23 @@ GSScannedMallocZone (void)
return &default_zone;
}
void
GSMakeWeakPointer(Class class, const char *iVarName)
{
return;
}
BOOL
GSAssignZeroingWeakPointer(void **destination, void *source)
{
if (destination == 0)
{
return NO;
}
*destination = source;
return YES;
}
void*
NSZoneMalloc (NSZone *zone, size_t size)
{