Added implementations of the hooks provided by the new runtime. This brings

NSObject up to feature-parity with the OS X 10.5 implementation when using the
new runtime and up to feature-parity with the 10.6 implementation if you are
using the new runtime and compiling with clang.

Also removes the objc_mutex_wibble stuff from NSObject in favour of just using
NSLocks (which, with the new implementation, are now faster than using
objc_mutex_stuff).



git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@28657 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
David Chisnall 2009-09-10 20:14:42 +00:00
parent 0836fd9bd7
commit 47eab60ed8
4 changed files with 206 additions and 36 deletions

View file

@ -1,3 +1,17 @@
2009-09-10 David Chisnall <csdavec@swan.ac.uk>
* Source/GSFFIInvocation.m:
* Source/NSObject.m:
* Headers/Foundation/NSObject.h:
Added implementations of the hooks provided by the new runtime. This
brings NSObject up to feature-parity with the OS X 10.5 implementation
when using the new runtime and up to feature-parity with the 10.6
implementation if you are using the new runtime and compiling with clang.
Also removes the objc_mutex_wibble stuff from NSObject in favour of just
using NSLocks (which, with the new implementation, are now faster than
using objc_mutex_stuff).
2009-09-10 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSRunLoop.m: ([-limitDateForMode:]) on OSX it seems that only

View file

@ -234,6 +234,58 @@ extern "C" {
- (id) self;
- (Class) superclass;
- (NSZone*) zone;
#if OS_API_VERSION(MAC_OS_X_VERSION_10_5, GS_API_LATEST)
/**
* This method will be called when attempting to send a message a class that
* does not understand it. The class may install a new method for the given
* selector and return YES, otherwise it should return NO.
*
* Note: This method is only reliable when using the GNUstep runtime. If you
* require compatibility with the GCC runtime, you must also implement
* -forwardInvocation: with equivalent semantics. This will be considerably
* slower, but more portable.
*/
+ (BOOL)resolveClassMethod:(SEL)name;
/**
* This method will be called when attempting to send a message an instance
* that does not understand it. The class may install a new method for the
* given selector and return YES, otherwise it should return NO.
*
* Note: This method is only reliable when using the GNUstep runtime. If you
* require compatibility with the GCC runtime, you must also implement
* -forwardInvocation: with equivalent semantics. This will be considerably
* slower, but more portable.
*/
+ (BOOL)resolveInstanceMethod:(SEL)name;
#endif
#if OS_API_VERSION(MAC_OS_X_VERSION_10_6, GS_API_LATEST)
/**
* If an object does not understand a message, it may delegate it to another
* object. Returning nil indicates that forwarding should not take place. The
* default implementation of this returns nil, but care should be taken when
* subclassing NSObject subclasses and overriding this method that
* the superclass implementation is called if returning nil.
*
* Note: This method is only reliable when using the GNUstep runtime and code
* compiled with clang. If you require compatibility with GCC and the GCC
* runtime, you must also implement -forwardInvocation: with equivalent
* semantics. This will be considerably slower, but more portable.
*/
- (id)forwardingTargetForSelector:(SEL)aSelector;
/**
* Returns an auto-accessing proxy for the given object. This proxy sends a
* -beginContentAccess message to the receiver when it is created and an
* -endContentAccess message when it is destroyed. This prevents an object
* that implements the NSDiscardableContent protocol from having its contents
* discarded for as long as the proxy exists.
*
* On systems using the GNUstep runtime, messages send to the proxy will be
* slightly slower than direct messages. With the GCC runtime, they will be
* approximately two orders of magnitude slower. The GNUstep runtime,
* therefore, is strongly recommended for code calling this method.
*/
- (id)autoContentAccessingProxy;
#endif
@end
/**

View file

@ -29,6 +29,10 @@
#import "GSInvocation.h"
#import <config.h>
#import <objc/objc-api.h>
// FIXME: We should be using the new interfaces, not exposing the old ones.
#define __OBJC_LEGACY_GNU_MODE__
#import <objc/runtime.h>
#import <pthread.h>
#import "cifframe.h"
#import "mframe.h"
#import "GSPrivate.h"
@ -209,6 +213,66 @@ IMP gs_objc_msg_forward (SEL sel)
{
return gs_objc_msg_forward2 (nil, sel);
}
#ifdef __GNUSTEP_RUNTIME__
pthread_key_t thread_slot_key;
static struct objc_slot_t gs_objc_msg_forward3(id receiver, SEL op)
{
/* The slot has its version set to 0, so it can not be cached. This makes it
* safe to free it when the thread exits. */
Slot_t slot = pthread_getspecific(thread_slot_key);
if (NULL == slot)
{
slot = calloc(sizeof(struct objc_slot), 1);
pthread_setspecific(thread_slot_key, slot);
}
slot->method = gs_objc_msg_forward2(receiver, op);
return slot;
}
/** Hidden by legacy API define. Declare it locally */
BOOL class_isMetaClass(Class cls);
BOOL class_respondsToSelector(Class cls, SEL sel);
/**
* Runtime hook used to provide message redirections. If lookup fails but this
* function returns non-nil then the lookup will be retried with the returned
* value.
*
* Note: Every message sent by this function MUST be understood by the
* receiver. If this is not the case then there is a potential for infinite
* recursion.
*/
static id gs_objc_proxy_lookup(id receiver, SEL op)
{
/* FIXME: Should be isa, but legacy-compat mode makes it class_pointer */
Class cls = receiver->class_pointer;
BOOL resolved = NO;
/* Let the class try to add a method for this thing. */
if (class_isMetaClass(cls))
{
if (class_respondsToSelector(cls, @selector(resolveClassMethod:)))
{
resolved = [receiver resolveClassMethod: op];
}
}
else
{
if (class_respondsToSelector(cls->class_pointer, @selector(resolveInstanceMethod:)))
{
resolved = [class resolveInstanceMethod: op];
}
}
if (resolved)
{
return receiver;
}
if (class_respondsToSelector(cls, @selector(forwardingTargetForSelector:)))
{
return [receiver forwardingTargetForSelector: op]
}
return nil;
}
#endif
+ (void) load
{
@ -217,6 +281,11 @@ IMP gs_objc_msg_forward (SEL sel)
#else
__objc_msg_forward = gs_objc_msg_forward;
#endif
#ifdef __GNUSTEP_RUNTIME__
pthread_key_create(&thread_slot_key, free);
objc_msg_forward3 = gs_objc_msg_forward3;
objc_proxy_lookup = gs_objc_proxy_lookup;
#endif
}
- (id) initWithArgframe: (arglist_t)frame selector: (SEL)aSelector

View file

@ -35,6 +35,7 @@
#include "config.h"
#include "GNUstepBase/preface.h"
#include "GNUstepBase/GNUstep.h"
#include <stdarg.h>
#include "Foundation/NSObject.h"
#include <objc/Protocol.h>
@ -114,11 +115,18 @@ static Class NSConstantStringClass;
- (NSMethodSignature*) methodSignatureForSelector: (SEL)aSelector;
@end
@interface GSContentAccessingProxy : NSProxy
{
NSObject<NSDiscardableContent> *object;
}
- (id)initWithObject: (id)anObject;
@end
/*
* allocationLock is needed when running multi-threaded for
* protecting the map table of zombie information.
*/
static objc_mutex_t allocationLock = NULL;
static NSLock *allocationLock;
BOOL NSZombieEnabled = NO;
BOOL NSDeallocateZombies = NO;
@ -135,15 +143,9 @@ static void GSMakeZombie(NSObject *o)
((id)o)->class_pointer = zombieClass;
if (NSDeallocateZombies == NO)
{
if (allocationLock != 0)
{
objc_mutex_lock(allocationLock);
}
[allocationLock lock];
NSMapInsert(zombieMap, (void*)o, (void*)c);
if (allocationLock != 0)
{
objc_mutex_unlock(allocationLock);
}
[allocationLock unlock];
}
}
#endif
@ -154,15 +156,9 @@ static void GSLogZombie(id o, SEL sel)
if (NSDeallocateZombies == NO)
{
if (allocationLock != 0)
{
objc_mutex_lock(allocationLock);
}
[allocationLock lock];
c = NSMapGet(zombieMap, (void*)o);
if (allocationLock != 0)
{
objc_mutex_unlock(allocationLock);
}
[allocationLock unlock];
}
if (c == 0)
{
@ -339,9 +335,9 @@ GSAtomicDecrement(gsatomic_t X)
#define LOCKMASK (LOCKCOUNT-1)
#define ALIGNBITS 3
static objc_mutex_t allocationLocks[LOCKCOUNT] = { 0 };
static NSLock *allocationLocks[LOCKCOUNT] = { 0 };
static inline objc_mutex_t GSAllocationLockForObject(id p)
static inline NSLock *GSAllocationLockForObject(id p)
{
NSUInteger i = ((((NSUInteger)(uintptr_t)p) >> ALIGNBITS) & LOCKMASK);
return allocationLocks[i];
@ -422,18 +418,18 @@ NSDecrementExtraRefCountWasZero(id anObject)
return YES;
}
#else /* GSATOMICREAD */
objc_mutex_t theLock = GSAllocationLockForObject(anObject);
NSLock *theLock = GSAllocationLockForObject(anObject);
objc_mutex_lock(theLock);
[theLock lock];
if (((obj)anObject)[-1].retained == 0)
{
objc_mutex_unlock(theLock);
[theLock unlock];
return YES;
}
else
{
((obj)anObject)[-1].retained--;
objc_mutex_unlock(theLock);
[theLock unlock];
return NO;
}
#endif /* GSATOMICREAD */
@ -495,9 +491,9 @@ NSIncrementExtraRefCount(id anObject)
format: @"NSIncrementExtraRefCount() asked to increment too far"];
}
#else /* GSATOMICREAD */
objc_mutex_t theLock = GSAllocationLockForObject(anObject);
NSLock *theLock = GSAllocationLockForObject(anObject);
objc_mutex_lock(theLock);
[theLock lock];
if (((obj)anObject)[-1].retained == UINT_MAX - 1)
{
objc_mutex_unlock (theLock);
@ -505,7 +501,7 @@ NSIncrementExtraRefCount(id anObject)
format: @"NSIncrementExtraRefCount() asked to increment too far"];
}
((obj)anObject)[-1].retained++;
objc_mutex_unlock (theLock);
[theLock lock];
#endif /* GSATOMICREAD */
}
else
@ -875,10 +871,10 @@ GSDescriptionForClassMethod(pcl self, SEL aSel)
for (i = 0; i < LOCKCOUNT; i++)
{
allocationLocks[i] = objc_mutex_allocate();
allocationLocks[i] = [NSLock new];
}
#endif
allocationLock = objc_mutex_allocate();
allocationLock = [NSLock new];
}
}
@ -2018,6 +2014,22 @@ GSGarbageCollectorLog(char *msg, GC_word arg)
return self;
}
+ (BOOL)resolveClassMethod:(SEL)name
{
return NO;
}
+ (BOOL)resolveInstanceMethod:(SEL)name;
{
return NO;
}
- (id)forwardingTargetForSelector:(SEL)aSelector;
{
return nil;
}
- (id)autoContentAccessingProxy
{
return AUTORELEASE([[GSContentAccessingProxy alloc] initWithObject: self]);
}
@end
@ -2455,16 +2467,39 @@ GSGarbageCollectorLog(char *msg, GC_word arg)
{
Class c;
if (allocationLock != 0)
{
objc_mutex_lock(allocationLock);
}
[allocationLock lock];
c = NSMapGet(zombieMap, (void*)self);
if (allocationLock != 0)
{
objc_mutex_unlock(allocationLock);
}
[allocationLock unlock];
return [c instanceMethodSignatureForSelector: aSelector];
}
@end
@implementation GSContentAccessingProxy
- (id)initWithObject: (id)anObject
{
ASSIGN(object, anObject);
[object beginContentAccess];
return self;
}
- (void)finalize
{
[object endContentAccess];
}
- (void)dealloc
{
[object endContentAccess];
}
- (id)forwardingTargetForSelector: (SEL)aSelector
{
return object;
}
/* Support for legacy runtimes... */
- (void) forwardInvocation: (NSInvocation*)anInvocation
{
[anInvocation invokeWithTarget: object];
}
- (NSMethodSignature*) methodSignatureForSelector: (SEL)aSelector
{
return [object methodSignatureForSelector: aSelector];
}
@end