Initial pass at implementing fully Apple-compatible GC. This requires the code to be built with -fobjc-gc or -fobjc-gc-only, and requires a runtime that implements all of the support functions (GNUstep runtime trunk or 1.5 when it's release).

Currently, there are a few places where we should be calling NSAllocateCollectable() without NSScannedOption, but are actually calling NSZoneMalloc() unless we're in GC mode.  We should not need separate code paths for this anywhere outside NSZone, since NSAllocateCollectable() will work in non-GC mode as well.

A few of the changes should be tweaked slightly so that they do run-time tests.  For example, when compiling with -fobjc-gc, we may be linked against non-GC code, which will use -retain and -release but won't use the memory barriers.  Supporting this nicely is a lot of effort, and I'm not fully convinced it's a good idea.



git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@33104 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
David Chisnall 2011-05-24 14:43:27 +00:00
parent e32626abe7
commit 6068e9484c
7 changed files with 241 additions and 14 deletions

View file

@ -1,3 +1,17 @@
2011-05-24 David Chisnall <theraven@gna.org>
* Source/NSBundle.m
* Source/NSGarbageCollector.m
* Source/NSKeyValueObserving.m
* Source/NSZone.m
* Source/NSObject.m
* Source/NSAutoreleasePool.m
First pass at supporting fully Apple-compatible GC in -base. Code sitting
on top of -base should not require modifying (in theory, at least, and
unless it does the sort of evil tricks that LanguageKit does for
performance).
2011-05-24 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSAutoreleasePool.m:

View file

@ -34,7 +34,7 @@
#import "Foundation/NSException.h"
#import "Foundation/NSThread.h"
#if GS_WITH_GC
#if GS_WITH_GC || __OBJC_GC__
@implementation NSAutoreleasePool
@ -120,6 +120,8 @@ static NSAutoreleasePool *pool = nil;
- (void) dealloc
{
[NSException raise: NSGenericException
format: @"dealloc should not be called in garbage collected mode"];
GSNOSUPERDEALLOC;
return;
}

View file

@ -1305,13 +1305,21 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
{
bundle = (NSBundle *)NSMapGet(_byIdentifier, identifier);
IF_NO_GC(
if (bundle != nil)
{
[bundle retain]; /* retain - look as if we were alloc'ed */
}
[bundle retain]; /* retain - look as if we were alloc'ed */
)
}
[load_lock unlock];
// Some OS X apps try to get the foundation bundle by looking it up by
// identifier. This is expected to be faster than looking it up by class, so
// we lazily insert it into the table if it's requested.
if (nil == bundle && [@"com.apple.Foundation" isEqualToString: identifier])
{
NSBundle *foundation = [self bundleForClass: self];
[load_lock lock];
NSMapInsert(_byIdentifier, @"com.apple.Foundation", foundation);
[load_lock unlock];
return foundation;
}
return AUTORELEASE(bundle);
}

View file

@ -27,8 +27,91 @@
#import "Foundation/NSGarbageCollector.h"
static NSGarbageCollector *collector = nil;
static unsigned disabled = 0;
#if __OBJC_GC__
#include <objc/objc-auto.h>
id CFRetain(id obj)
{
return objc_gc_retain(obj);
}
void CFRelease(id obj)
{
objc_gc_release(obj);
}
@implementation NSGarbageCollector
+ (id) defaultCollector
{
return collector;
}
+ (void) initialize
{
collector = [self alloc];
}
- (void) collectIfNeeded
{
objc_collect(OBJC_COLLECT_IF_NEEDED | OBJC_FULL_COLLECTION);
}
- (void) collectExhaustively
{
objc_collect(OBJC_EXHAUSTIVE_COLLECTION);
}
- (void) disable
{
objc_gc_disable();
}
- (void) disableCollectorForPointer: (void *)ptr
{
CFRetain(ptr);
}
- (void) enable
{
objc_gc_enable();
}
- (void) enableCollectorForPointer: (void *)ptr
{
CFRelease(ptr);
}
- (id) init
{
if (self != collector)
{
[self dealloc];
self = nil;
}
return self;
}
- (BOOL) isCollecting
{
return NO;
}
- (BOOL) isEnabled
{
return objc_collectingEnabled();
}
- (NSZone*) zone
{
return NSDefaultMallocZone();
}
@end
#else
static unsigned disabled = 0;
#if GS_WITH_GC
#include <gc/gc.h>
@ -152,3 +235,4 @@ static NSHashTable *uncollectable = 0;
}
@end
#endif // __OBJC_GC__

View file

@ -1705,6 +1705,7 @@ replacementForClass(Class c)
}
pathInfo = [info lockReturningPathInfoForKey: aKey];
//[pathInfo retain];
if (pathInfo != nil)
{
if (pathInfo->recursion == 1)
@ -1730,6 +1731,7 @@ replacementForClass(Class c)
}
[self didChangeValueForDependentsOfKey: aKey];
//[pathInfo release];
}
- (void) didChange: (NSKeyValueChange)changeKind

View file

@ -125,7 +125,7 @@ BOOL NSDeallocateZombies = NO;
static Class zombieClass = Nil;
static NSMapTable *zombieMap = 0;
#if !GS_WITH_GC
#if !GS_WITH_GC && !__OBJC_GC__
static void GSMakeZombie(NSObject *o)
{
Class c;
@ -414,7 +414,7 @@ typedef struct obj_layout *obj;
BOOL
NSDecrementExtraRefCountWasZero(id anObject)
{
#if !GS_WITH_GC
#if !GS_WITH_GC && !__OBJC_GC__
if (double_release_check_enabled)
{
NSUInteger release_count;
@ -504,7 +504,7 @@ NSExtraRefCount(id anObject)
inline void
NSIncrementExtraRefCount(id anObject)
{
#if GS_WITH_GC
#if GS_WITH_GC || __OBJC_GC__
return;
#else /* GS_WITH_GC */
if (allocationLock != 0)
@ -594,7 +594,32 @@ callCXXConstructors(Class aClass, id anObject)
* depending on what information (if any) we are storing before
* the start of each object.
*/
#if GS_WITH_GC
#if __OBJC_GC__
inline NSZone *
GSObjCZone(NSObject *object)
{
return NSDefaultMallocZone();
}
inline id
NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone)
{
id new = class_createInstance(aClass, extraBytes);
if (0 == cxx_construct)
{
cxx_construct = sel_registerName(".cxx_construct");
cxx_destruct = sel_registerName(".cxx_destruct");
}
callCXXConstructors(aClass, new);
return new;
}
inline void
NSDeallocateObject(id anObject)
{
}
#elif GS_WITH_GC
inline NSZone *
GSObjCZone(NSObject *object)
@ -777,7 +802,7 @@ NSDeallocateObject(id anObject)
BOOL
NSShouldRetainWithZone (NSObject *anObject, NSZone *requestedZone)
{
#if GS_WITH_GC
#if GS_WITH_GC || __OBJC_GC__
return YES;
#else
return (!requestedZone || requestedZone == NSDefaultMallocZone()
@ -1904,7 +1929,7 @@ objc_create_block_classes_as_subclasses_of(Class super);
*/
- (oneway void) release
{
#if GS_WITH_GC == 0
#if GS_WITH_GC == 0 && !__OBJC_GC__
if (NSDecrementExtraRefCountWasZero(self))
{
[self dealloc];
@ -1954,7 +1979,7 @@ objc_create_block_classes_as_subclasses_of(Class super);
*/
- (id) retain
{
#if GS_WITH_GC == 0
#if GS_WITH_GC == 0 && !__OBJC_GC__
NSIncrementExtraRefCount(self);
#endif
return self;

View file

@ -227,8 +227,100 @@ NSZoneName (NSZone *zone)
zone = NSDefaultMallocZone();
return zone->name;
}
#if __OBJC_GC__
#if GS_WITH_GC
#include <objc/objc-auto.h>
void *
NSAllocateCollectable(NSUInteger size, NSUInteger options)
{
return objc_gc_allocate_collectable(size,
((options & NSScannedOption) == NSScannedOption));
}
void *
NSReallocateCollectable(void *ptr, NSUInteger size, NSUInteger options)
{
return objc_gc_reallocate_collectable(ptr, size,
((options & NSScannedOption) == NSScannedOption));
}
NSZone*
NSCreateZone (NSUInteger start, NSUInteger gran, BOOL canFree)
{
NSLog(@" *** Creating a zone while running GC is ignored.");
return &default_zone;
}
NSZone*
NSDefaultMallocZone (void)
{
return &default_zone;
}
// This is an ugly hack. We should be using NSAllocateCollectable() without
// NSScannedOption, not trying to fudge this with stuff that's totally
// incompatible with Apple's design.
NSZone*
GSAtomicMallocZone (void)
{
return &default_zone;
}
NSZone*
NSZoneFromPointer (void *ptr)
{
return &default_zone;
}
void
NSRecycleZone (NSZone *zone) { }
BOOL
NSZoneCheck (NSZone *zone)
{
return YES;
}
struct
NSZoneStats NSZoneStats (NSZone *zone)
{
struct NSZoneStats stats = { 0 };
return stats;
}
void
GSMakeWeakPointer(Class theClass, const char *iVarName) { }
BOOL
GSAssignZeroingWeakPointer(void **destination, void *source)
{
objc_assign_weak(source, (id*)destination);
return YES;
}
void*
NSZoneMalloc (NSZone *zone, NSUInteger size)
{
return NSZoneCalloc(zone, 1, size);
}
void*
NSZoneCalloc (NSZone *zone, NSUInteger elems, NSUInteger bytes)
{
// FIXME: Overflow checking
size_t size = elems * bytes;
return objc_gc_allocate_collectable(size, YES);
}
void*
NSZoneRealloc (NSZone *zone, void *ptr, NSUInteger size)
{
return objc_gc_reallocate_collectable(ptr, size, YES);
}
void NSZoneFree (NSZone *zone, void *ptr) { }
#elif GS_WITH_GC
#if defined(DEBUG)
#define GC_DEBUG 1