mirror of
https://github.com/gnustep/libs-base.git
synced 2025-05-30 16:30:41 +00:00
Merge branch 'weakref' for wek reference support improvements.
This commit is contained in:
commit
ae8367e401
19 changed files with 840 additions and 192 deletions
|
@ -637,5 +637,14 @@ void
|
|||
GSPrivateEncodeBase64(const uint8_t *src, NSUInteger length, uint8_t *dst)
|
||||
GS_ATTRIB_PRIVATE;
|
||||
|
||||
#ifndef OBJC_CAP_ARC
|
||||
/* When we don't have a runtime with ARC to support weak references, we
|
||||
* use our own version.
|
||||
*/
|
||||
BOOL GSPrivateMarkedWeak(id obj, BOOL mark) GS_ATTRIB_PRIVATE;
|
||||
void GSWeakInit() GS_ATTRIB_PRIVATE;
|
||||
BOOL objc_delete_weak_refs(id obj);
|
||||
#endif
|
||||
|
||||
#endif /* _GSPrivate_h_ */
|
||||
|
||||
|
|
|
@ -105,8 +105,8 @@ typedef GSIMapNode_t *GSIMapNode;
|
|||
pointerFunctionsReplace(&M->cb.pf, (void**)addr, (x).obj);
|
||||
|
||||
#define GSI_MAP_READ_KEY(M,addr) \
|
||||
(M->legacy ? *(addr) :\
|
||||
(__typeof__(*addr))pointerFunctionsRead(&M->cb.pf, (void**)addr))
|
||||
(M->legacy ? *(addr) :\
|
||||
(__typeof__(*addr))pointerFunctionsRead(&M->cb.pf, (void**)addr))
|
||||
|
||||
#define GSI_MAP_ENUMERATOR NSHashEnumerator
|
||||
|
||||
|
|
|
@ -28,15 +28,15 @@
|
|||
# include <objc/capabilities.h>
|
||||
#endif
|
||||
|
||||
#import "GNUstepBase/GSObjCRuntime.h"
|
||||
|
||||
#define WEAK_READ(x) objc_loadWeak((id*)x)
|
||||
#define WEAK_WRITE(addr, x) objc_storeWeak((id*)addr, (id)x)
|
||||
|
||||
#if defined(OBJC_CAP_ARC)
|
||||
# include <objc/objc-arc.h>
|
||||
# define WEAK_READ(x) objc_loadWeak((id*)x)
|
||||
# define WEAK_WRITE(addr, x) objc_storeWeak((id*)addr, (id)x)
|
||||
# define STRONG_WRITE(addr, x) objc_storeStrong((id*)addr, (id)x)
|
||||
# define STRONG_ACQUIRE(x) objc_retain(x)
|
||||
# define STRONG_WRITE(addr, x) objc_storeStrong((id*)addr, (id)x)
|
||||
# define STRONG_ACQUIRE(x) objc_retain(x)
|
||||
#else
|
||||
# define WEAK_READ(x) (*x)
|
||||
# define WEAK_WRITE(addr, x) (*(addr) = x)
|
||||
# define STRONG_WRITE(addr, x) ASSIGN(*((id*)addr), ((id)x))
|
||||
# define STRONG_ACQUIRE(x) RETAIN(((id)x))
|
||||
#endif
|
||||
|
|
|
@ -1705,8 +1705,9 @@ typedef struct {
|
|||
{
|
||||
NSMapRemove(messagePortMap, (void*)name);
|
||||
}
|
||||
[self retain];
|
||||
M_UNLOCK(messagePortLock);
|
||||
[self dealloc];
|
||||
[super release];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -132,17 +132,24 @@ struct NCTbl; /* Notification Center Table structure */
|
|||
* removed from, or not yet added to any list). The end of a
|
||||
* list is marked by 'next' being set to 'ENDOBS'.
|
||||
*
|
||||
* This is normally a structure which handles memory management using a fast
|
||||
* reference count mechanism, but when built with clang for GC, a structure
|
||||
* can't hold a zeroing weak pointer to an observer so it's implemented as a
|
||||
* trivial class instead ... and gets managed by the garbage collector.
|
||||
* The observer is a weak reference to the observing object.
|
||||
* The receiver is a strong reference to the observing object but
|
||||
* exists only while we are in the process of posting a notification
|
||||
* to that object (in which case the value of posting is the number
|
||||
* of times the observation has been added to arrays for posting).
|
||||
*
|
||||
* The posting count records the number of times the Observation has
|
||||
* been added to arrays for posting notification to observers, it is
|
||||
* needed to track the situation where multiple threads are posting
|
||||
* notifications to the same server at the same time.
|
||||
*/
|
||||
|
||||
typedef struct Obs {
|
||||
id observer; /* Object to receive message. */
|
||||
id observer; /* Reference to object. */
|
||||
id receiver; /* Retained object (temporary). */
|
||||
SEL selector; /* Method selector. */
|
||||
BOOL owner; /* If we own the observer. */
|
||||
int32_t retained; /* Retain count for structure. */
|
||||
int32_t posting; /* Retain count for structure. */
|
||||
struct Obs *next; /* Next item in linked list. */
|
||||
struct NCTbl *link; /* Pointer back to chunk table */
|
||||
} Observation;
|
||||
|
@ -186,17 +193,19 @@ static inline BOOL doEqual(BOOL shouldHash, NSString* key1, NSString* key2)
|
|||
*/
|
||||
static void listFree(Observation *list);
|
||||
|
||||
/* Observations have retain/release counts managed explicitly by fast
|
||||
* function calls.
|
||||
*/
|
||||
static void obsRetain(Observation *o);
|
||||
static void obsFree(Observation *o);
|
||||
|
||||
/* Observations have their 'posting' counts managed explicitly by fast
|
||||
* function calls when thye are added to or removed from arrays.
|
||||
*/
|
||||
static void obsPost(Observation *o);
|
||||
static void obsDone(Observation *o);
|
||||
|
||||
|
||||
#define GSI_ARRAY_TYPES 0
|
||||
#define GSI_ARRAY_TYPE Observation*
|
||||
#define GSI_ARRAY_RELEASE(A, X) obsFree(X.ext)
|
||||
#define GSI_ARRAY_RETAIN(A, X) obsRetain(X.ext)
|
||||
#define GSI_ARRAY_RELEASE(A, X) obsDone(X.ext)
|
||||
#define GSI_ARRAY_RETAIN(A, X) obsPost(X.ext)
|
||||
|
||||
#include "GNUstepBase/GSIArray.h"
|
||||
|
||||
|
@ -217,7 +226,7 @@ static void obsFree(Observation *o);
|
|||
/*
|
||||
* An NC table is used to keep track of memory allocated to store
|
||||
* Observation structures. When an Observation is removed from the
|
||||
* notification center, it's memory is returned to the free list of
|
||||
* notification center, its memory is returned to the free list of
|
||||
* the chunk table, rather than being released to the general
|
||||
* memory allocation system. This means that, once a large number
|
||||
* of observers have been registered, memory usage will never shrink
|
||||
|
@ -291,17 +300,17 @@ obsNew(NCTable *t, SEL s, id o)
|
|||
obs = t->freeList;
|
||||
t->freeList = (Observation*)obs->link;
|
||||
obs->link = (void*)t;
|
||||
obs->retained = 0;
|
||||
obs->posting = 0;
|
||||
obs->next = 0;
|
||||
|
||||
obs->receiver = nil;
|
||||
obs->selector = s;
|
||||
obs->observer = o;
|
||||
objc_initWeak(&obs->observer, o);
|
||||
|
||||
/* Instances of GSNotificationObserverClass are owned by the Observation
|
||||
* and must be explicitly released when the observation is removed.
|
||||
*/
|
||||
obs->owner = (GSNotificationObserverClass == object_getClass(o)) ? YES : NO;
|
||||
|
||||
return obs;
|
||||
}
|
||||
|
||||
|
@ -343,8 +352,7 @@ static void endNCTable(NCTable *t)
|
|||
|
||||
TEST_RELEASE(t->_lock);
|
||||
|
||||
/*
|
||||
* Free observations without notification names or numbers.
|
||||
/* Free observations without notification names or numbers.
|
||||
*/
|
||||
listFree(t->wildcard);
|
||||
|
||||
|
@ -433,24 +441,59 @@ static inline void unlockNCTable(NCTable* t)
|
|||
|
||||
static void obsFree(Observation *o)
|
||||
{
|
||||
NSCAssert(o->retained >= 0, NSInternalInconsistencyException);
|
||||
if (o->retained-- == 0)
|
||||
{
|
||||
NCTable *t = o->link;
|
||||
NCTable *t;
|
||||
|
||||
if (o->owner)
|
||||
{
|
||||
DESTROY(o->observer);
|
||||
o->owner = NO;
|
||||
}
|
||||
o->next = 0; // no longer in any active list
|
||||
if (o->posting)
|
||||
{
|
||||
return; // Defer until posting is done
|
||||
}
|
||||
|
||||
/* If we own the observer, we must release it as well as destroying
|
||||
* the weak reference to it.
|
||||
*/
|
||||
if (o->owner)
|
||||
{
|
||||
id obj = objc_loadWeak(&o->observer);
|
||||
|
||||
[obj autorelease]; // Release to balance the fact we own it.
|
||||
o->owner = NO;
|
||||
}
|
||||
|
||||
objc_destroyWeak(&o->observer);
|
||||
|
||||
/* This observation can now be placed in the free list if there is one.
|
||||
*/
|
||||
if ((t = o->link) != 0)
|
||||
{
|
||||
o->link = (NCTable*)t->freeList;
|
||||
t->freeList = o;
|
||||
}
|
||||
}
|
||||
|
||||
static void obsRetain(Observation *o)
|
||||
static void obsDone(Observation *o)
|
||||
{
|
||||
o->retained++;
|
||||
if (0 == --o->posting)
|
||||
{
|
||||
/* Posting to this observer is over, so we null-out the receiver
|
||||
* and let it be deallocated if nobody else has retained it.
|
||||
*/
|
||||
[o->receiver autorelease];
|
||||
o->receiver = nil;
|
||||
|
||||
/* If the observation was removed from its linked list, it needs
|
||||
* to be freed now it's no longer being p[osted to.
|
||||
*/
|
||||
if (0 == o->next)
|
||||
{
|
||||
obsFree(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void obsPost(Observation *o)
|
||||
{
|
||||
o->posting++;
|
||||
}
|
||||
|
||||
static void listFree(Observation *list)
|
||||
|
@ -477,7 +520,7 @@ static Observation *listPurge(Observation *list, id observer)
|
|||
{
|
||||
Observation *tmp;
|
||||
|
||||
while (list != ENDOBS && list->observer == observer)
|
||||
while (list != ENDOBS && objc_loadWeak(&list->observer) == observer)
|
||||
{
|
||||
tmp = list->next;
|
||||
list->next = 0;
|
||||
|
@ -489,7 +532,7 @@ static Observation *listPurge(Observation *list, id observer)
|
|||
tmp = list;
|
||||
while (tmp->next != ENDOBS)
|
||||
{
|
||||
if (tmp->next->observer == observer)
|
||||
if (objc_loadWeak(&tmp->next->observer) == observer)
|
||||
{
|
||||
Observation *next = tmp->next;
|
||||
|
||||
|
@ -546,14 +589,6 @@ purgeMapNode(GSIMapTable map, GSIMapNode node, id observer)
|
|||
}
|
||||
}
|
||||
|
||||
/* purgeCollected() returns a list of observations with any observations for
|
||||
* a collected observer removed.
|
||||
* purgeCollectedFromMapNode() does the same thing but also handles cleanup
|
||||
* of the map node containing the list if necessary.
|
||||
*/
|
||||
#define purgeCollected(X) (X)
|
||||
#define purgeCollectedFromMapNode(X, Y) ((Observation*)Y->value.ext)
|
||||
|
||||
|
||||
@interface GSNotificationBlockOperation : NSOperation
|
||||
{
|
||||
|
@ -673,14 +708,6 @@ purgeMapNode(GSIMapTable map, GSIMapNode node, id observer)
|
|||
|
||||
static NSNotificationCenter *default_center = nil;
|
||||
|
||||
+ (void) atExit
|
||||
{
|
||||
id tmp = default_center;
|
||||
|
||||
default_center = nil;
|
||||
[tmp release];
|
||||
}
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
if (self == [NSNotificationCenter class])
|
||||
|
@ -704,7 +731,6 @@ static NSNotificationCenter *default_center = nil;
|
|||
*/
|
||||
default_center = [self alloc];
|
||||
[default_center init];
|
||||
[self registerAtExit];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -819,7 +845,6 @@ static NSNotificationCenter *default_center = nil;
|
|||
* once - in which case, the observer will receive multiple messages when
|
||||
* the notification is posted... odd, but the MacOS-X docs specify this.
|
||||
*/
|
||||
|
||||
if (name)
|
||||
{
|
||||
/*
|
||||
|
@ -937,7 +962,7 @@ static NSNotificationCenter *default_center = nil;
|
|||
* of our map tables - while enumerating a table, it is safe to remove
|
||||
* the entry returned by the enumerator.
|
||||
*/
|
||||
|
||||
ENTER_POOL
|
||||
lockNCTable(TABLE);
|
||||
|
||||
if (name == nil && object == nil)
|
||||
|
@ -1075,6 +1100,7 @@ static NSNotificationCenter *default_center = nil;
|
|||
}
|
||||
|
||||
unlockNCTable(TABLE);
|
||||
LEAVE_POOL
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1089,6 +1115,52 @@ static NSNotificationCenter *default_center = nil;
|
|||
[self removeObserver: observer name: nil object: nil];
|
||||
}
|
||||
|
||||
static Observation*
|
||||
addPost(Observation *head, GSIArray a)
|
||||
{
|
||||
Observation *p = 0;
|
||||
Observation *o = head;
|
||||
Observation *t;
|
||||
|
||||
while (o != ENDOBS)
|
||||
{
|
||||
t = o->next;
|
||||
|
||||
if (o->receiver)
|
||||
{
|
||||
/* Posting already in progress, so we post to the same receiver.
|
||||
*/
|
||||
GSIArrayAddItem(a, (GSIArrayItem)o);
|
||||
p = o;
|
||||
}
|
||||
else if ((o->receiver = objc_loadWeakRetained(&o->observer)) != nil)
|
||||
{
|
||||
/* We will start posting using a newly retained object as receiver.
|
||||
*/
|
||||
GSIArrayAddItem(a, (GSIArrayItem)o);
|
||||
p = o;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The object has been deallocated ... remove the observation from
|
||||
* the list.
|
||||
*/
|
||||
if (p)
|
||||
{
|
||||
p->next = t;
|
||||
}
|
||||
else
|
||||
{
|
||||
head = t;
|
||||
}
|
||||
o->next = 0;
|
||||
obsFree(o);
|
||||
}
|
||||
o = t;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Private method to perform the actual posting of a notification.
|
||||
|
@ -1099,7 +1171,7 @@ static NSNotificationCenter *default_center = nil;
|
|||
{
|
||||
Observation *o;
|
||||
unsigned count;
|
||||
NSString *name = [notification name];
|
||||
NSString *name;
|
||||
id object;
|
||||
GSIMapNode n;
|
||||
GSIMapTable m;
|
||||
|
@ -1107,52 +1179,48 @@ static NSNotificationCenter *default_center = nil;
|
|||
GSIArray_t b;
|
||||
GSIArray a = &b;
|
||||
|
||||
if (name == nil)
|
||||
if ((name = [notification name]) == nil)
|
||||
{
|
||||
RELEASE(notification);
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"Tried to post a notification with no name."];
|
||||
}
|
||||
|
||||
/* Do the rest in an autorelease pool so that the observers can be
|
||||
* safely released (when the pool ends) outside our locked regions.
|
||||
*/
|
||||
ENTER_POOL
|
||||
|
||||
object = [notification object];
|
||||
|
||||
/*
|
||||
* Lock the table of observations while we traverse it.
|
||||
*
|
||||
* The table of observations contains weak pointers which are zeroed when
|
||||
* the observers get destroyed. So to avoid consistency problems
|
||||
* we use scanned memory in the array in the case where there are more
|
||||
* than the 64 observers we allowed room for on the stack.
|
||||
/* Lock the table of observations while we traverse it.
|
||||
*/
|
||||
GSIArrayInitWithZoneAndStaticCapacity(a, _zone, 64, i);
|
||||
|
||||
lockNCTable(TABLE);
|
||||
|
||||
/*
|
||||
* Find all the observers that specified neither NAME nor OBJECT.
|
||||
/* Find all the observers that specified neither NAME nor OBJECT.
|
||||
*/
|
||||
for (o = WILDCARD = purgeCollected(WILDCARD); o != ENDOBS; o = o->next)
|
||||
{
|
||||
GSIArrayAddItem(a, (GSIArrayItem)o);
|
||||
}
|
||||
WILDCARD = addPost(WILDCARD, a);
|
||||
|
||||
/*
|
||||
* Find the observers that specified OBJECT, but didn't specify NAME.
|
||||
/* Find the observers that specified OBJECT, but didn't specify NAME.
|
||||
*/
|
||||
if (object)
|
||||
{
|
||||
n = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)object);
|
||||
if (n != 0)
|
||||
if (n)
|
||||
{
|
||||
o = purgeCollectedFromMapNode(NAMELESS, n);
|
||||
while (o != ENDOBS)
|
||||
if (ENDOBS == (n->value.ext = addPost(n->value.ext, a)))
|
||||
{
|
||||
GSIArrayAddItem(a, (GSIArrayItem)o);
|
||||
o = o->next;
|
||||
GSIMapBucket bucket = GSIMapBucketForKey(NAMELESS, n->key);
|
||||
|
||||
GSIMapRemoveNodeFromMap(NAMELESS, bucket, n);
|
||||
GSIMapFreeNode(NAMELESS, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the observers of NAME, except those observers with a non-nil OBJECT
|
||||
/** Find the observers of NAME, except those observers with a non-nil OBJECT
|
||||
* that doesn't match the notification's OBJECT).
|
||||
*/
|
||||
if (name)
|
||||
|
@ -1168,33 +1236,33 @@ static NSNotificationCenter *default_center = nil;
|
|||
}
|
||||
if (m != 0)
|
||||
{
|
||||
/*
|
||||
* First, observers with a matching object.
|
||||
/* First, observers with a matching object.
|
||||
*/
|
||||
n = GSIMapNodeForSimpleKey(m, (GSIMapKey)object);
|
||||
if (n != 0)
|
||||
{
|
||||
o = purgeCollectedFromMapNode(m, n);
|
||||
while (o != ENDOBS)
|
||||
if (ENDOBS == (n->value.ext = addPost(n->value.ext, a)))
|
||||
{
|
||||
GSIArrayAddItem(a, (GSIArrayItem)o);
|
||||
o = o->next;
|
||||
GSIMapBucket bucket = GSIMapBucketForKey(m, n->key);
|
||||
|
||||
GSIMapRemoveNodeFromMap(m, bucket, n);
|
||||
GSIMapFreeNode(m, n);
|
||||
}
|
||||
}
|
||||
|
||||
if (object != nil)
|
||||
{
|
||||
/*
|
||||
* Now observers with a nil object.
|
||||
/* Now observers with a nil object.
|
||||
*/
|
||||
n = GSIMapNodeForSimpleKey(m, (GSIMapKey)(id)nil);
|
||||
if (n != 0)
|
||||
{
|
||||
o = purgeCollectedFromMapNode(m, n);
|
||||
while (o != ENDOBS)
|
||||
if (ENDOBS == (n->value.ext = addPost(n->value.ext, a)))
|
||||
{
|
||||
GSIArrayAddItem(a, (GSIArrayItem)o);
|
||||
o = o->next;
|
||||
GSIMapBucket bucket = GSIMapBucketForKey(m, n->key);
|
||||
|
||||
GSIMapRemoveNodeFromMap(m, bucket, n);
|
||||
GSIMapFreeNode(m, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1205,8 +1273,7 @@ static NSNotificationCenter *default_center = nil;
|
|||
*/
|
||||
unlockNCTable(TABLE);
|
||||
|
||||
/*
|
||||
* Now send all the notifications.
|
||||
/* Now send all the notifications.
|
||||
*/
|
||||
count = GSIArrayCount(a);
|
||||
while (count-- > 0)
|
||||
|
@ -1216,7 +1283,7 @@ static NSNotificationCenter *default_center = nil;
|
|||
{
|
||||
NS_DURING
|
||||
{
|
||||
[o->observer performSelector: o->selector
|
||||
[o->receiver performSelector: o->selector
|
||||
withObject: notification];
|
||||
}
|
||||
NS_HANDLER
|
||||
|
@ -1241,11 +1308,17 @@ static NSNotificationCenter *default_center = nil;
|
|||
NS_ENDHANDLER
|
||||
}
|
||||
}
|
||||
|
||||
/* Cleanup of the array of observations must be lock protected.
|
||||
*/
|
||||
lockNCTable(TABLE);
|
||||
GSIArrayEmpty(a);
|
||||
unlockNCTable(TABLE);
|
||||
|
||||
/* Release the notification and any objects we autoreleased during posting
|
||||
*/
|
||||
RELEASE(notification);
|
||||
LEAVE_POOL
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -208,8 +208,7 @@ extern void GSLogZombie(id o, SEL sel)
|
|||
#undef GSATOMICREAD
|
||||
#endif
|
||||
|
||||
#if defined(__llvm__) || (defined(USE_ATOMIC_BUILTINS) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)))
|
||||
/* Use the GCC atomic operations with recent GCC versions */
|
||||
#ifdef OBJC_CAP_ARC
|
||||
|
||||
typedef intptr_t volatile *gsatomic_t;
|
||||
typedef intptr_t gsrefcount_t;
|
||||
|
@ -217,6 +216,15 @@ typedef intptr_t gsrefcount_t;
|
|||
#define GSAtomicIncrement(X) __sync_add_and_fetch(X, 1)
|
||||
#define GSAtomicDecrement(X) __sync_sub_and_fetch(X, 1)
|
||||
|
||||
#elif (defined(USE_ATOMIC_BUILTINS) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)))
|
||||
/* Use the GCC atomic operations with recent GCC versions */
|
||||
|
||||
typedef int32_t volatile *gsatomic_t;
|
||||
typedef int32_t gsrefcount_t;
|
||||
#define GSATOMICREAD(X) (*(X))
|
||||
#define GSAtomicIncrement(X) __sync_add_and_fetch(X, 1)
|
||||
#define GSAtomicDecrement(X) __sync_sub_and_fetch(X, 1)
|
||||
|
||||
#elif defined(_WIN32)
|
||||
|
||||
/* Set up atomic read, increment and decrement for mswindows
|
||||
|
@ -382,7 +390,7 @@ GSAtomicDecrement(gsatomic_t X)
|
|||
|
||||
#include <pthread.h>
|
||||
|
||||
typedef int gsrefcount_t; // No atomics, use a simple integer
|
||||
typedef int32_t gsrefcount_t; // No atomics, use a simple integer
|
||||
|
||||
/* Having just one allocationLock for all leads to lock contention
|
||||
* if there are lots of threads doing lots of retain/release calls.
|
||||
|
@ -415,12 +423,21 @@ static inline pthread_mutex_t *GSAllocationLockForObject(id p)
|
|||
#endif
|
||||
#define alignof(type) __builtin_offsetof(struct { const char c; type member; }, member)
|
||||
|
||||
#ifndef OBJC_CAP_ARC
|
||||
typedef struct {
|
||||
BOOL hadWeakReference: 1; // set if the instance ever had a weak reference
|
||||
} gsinstinfo_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Define a structure to hold information that is held locally
|
||||
* (before the start) in each object.
|
||||
*/
|
||||
typedef struct obj_layout_unpadded {
|
||||
gsrefcount_t retained;
|
||||
#ifndef OBJC_CAP_ARC
|
||||
gsinstinfo_t extra;
|
||||
#endif
|
||||
} unp;
|
||||
#define UNP sizeof(unp)
|
||||
|
||||
|
@ -441,9 +458,26 @@ struct obj_layout {
|
|||
char padding[__BIGGEST_ALIGNMENT__ - ((UNP % __BIGGEST_ALIGNMENT__)
|
||||
? (UNP % __BIGGEST_ALIGNMENT__) : __BIGGEST_ALIGNMENT__)];
|
||||
gsrefcount_t retained;
|
||||
#ifndef OBJC_CAP_ARC
|
||||
gsinstinfo_t extra;
|
||||
#endif
|
||||
};
|
||||
typedef struct obj_layout *obj;
|
||||
|
||||
#ifndef OBJC_CAP_ARC
|
||||
BOOL
|
||||
GSPrivateMarkedWeak(id anObject, BOOL mark)
|
||||
{
|
||||
BOOL wasMarked = ((obj)anObject)[-1].extra.hadWeakReference;
|
||||
|
||||
if (mark)
|
||||
{
|
||||
((obj)anObject)[-1].extra.hadWeakReference = YES;
|
||||
}
|
||||
return wasMarked;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* These symbols are provided by newer versions of the GNUstep Objective-C
|
||||
* runtime. When linked against an older version, we will use our internal
|
||||
|
@ -493,9 +527,7 @@ static BOOL objc_release_fast_no_destroy_internal(id anObject)
|
|||
* have been greater than zero)
|
||||
*/
|
||||
(((obj)anObject)[-1].retained) = 0;
|
||||
# ifdef OBJC_CAP_ARC
|
||||
objc_delete_weak_refs(anObject);
|
||||
# endif
|
||||
return YES;
|
||||
}
|
||||
#else /* GSATOMICREAD */
|
||||
|
@ -504,9 +536,7 @@ static BOOL objc_release_fast_no_destroy_internal(id anObject)
|
|||
pthread_mutex_lock(theLock);
|
||||
if (((obj)anObject)[-1].retained == 0)
|
||||
{
|
||||
# ifdef OBJC_CAP_ARC
|
||||
objc_delete_weak_refs(anObject);
|
||||
# endif
|
||||
pthread_mutex_unlock(theLock);
|
||||
return YES;
|
||||
}
|
||||
|
@ -947,6 +977,8 @@ static id gs_weak_load(id obj)
|
|||
{
|
||||
#ifdef OBJC_CAP_ARC
|
||||
_objc_weak_load = gs_weak_load;
|
||||
#else
|
||||
GSWeakInit();
|
||||
#endif
|
||||
objc_create_block_classes_as_subclasses_of(self);
|
||||
}
|
||||
|
@ -2454,6 +2486,10 @@ static id gs_weak_load(id obj)
|
|||
}
|
||||
return c;
|
||||
}
|
||||
- (NSUInteger) retainCount
|
||||
{
|
||||
return 0; // So that gs_weak_load() knows the object was deallocated
|
||||
}
|
||||
- (void) logZombie: (SEL)selector
|
||||
{
|
||||
GSLogZombie(self, selector);
|
||||
|
|
|
@ -2474,8 +2474,9 @@ static Class tcpPortClass;
|
|||
{
|
||||
NSMapRemove(thePorts, host);
|
||||
}
|
||||
[self retain];
|
||||
M_UNLOCK(tcpPortLock);
|
||||
[self dealloc];
|
||||
[super release];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -56,7 +56,11 @@ ObjectiveC2_OBJC_FILES += \
|
|||
endif
|
||||
|
||||
ifeq ($(CC), clang)
|
||||
# We need the flag for blocks support and we have ARC built in.
|
||||
ADDITIONAL_OBJCFLAGS = -fblocks
|
||||
else
|
||||
# We need to emulated the weak reference API from the ARC runtime.
|
||||
ObjectiveC2_OBJC_FILES += weak.m
|
||||
endif
|
||||
|
||||
-include Makefile.preamble
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
ObjectiveC2 framework implements the new Apple runtime APIs, introduced with
|
||||
OS X 10.5 on top of the GCC Objective-C runtime. Its use is now discouraged.
|
||||
This code has been merged into the GNUstep Objective-C-2.0 runtime, which can
|
||||
act as a drop-in replacement for GCC libobjc. You can find the code here:
|
||||
|
||||
svn://svn.gna.org/svn/gnustep/libs/libobjc2/trunk
|
||||
OS X 10.5 (and later) on top of the GCC Objective-C runtime. Much code has
|
||||
been merged into the GNUstep Objective-C-2.0 runtime, which is used when
|
||||
compiling with clang (svn://svn.gna.org/svn/gnustep/libs/libobjc2/trunk).
|
||||
Somewhat recent versions of the GNU runtime implement much of the newer Apple
|
||||
APIs.
|
||||
|
|
451
Source/ObjectiveC2/weak.m
Normal file
451
Source/ObjectiveC2/weak.m
Normal file
|
@ -0,0 +1,451 @@
|
|||
|
||||
/* Emulation of ARC runtime support for weak references based on the gnustep
|
||||
* runtime implementation.
|
||||
*/
|
||||
|
||||
#import "common.h"
|
||||
#import "Foundation/Foundation.h"
|
||||
#import "../GSPrivate.h"
|
||||
#import "../GSPThread.h"
|
||||
|
||||
static Class persistentClasses[1];
|
||||
static int persistentClassCount = sizeof(persistentClasses)/sizeof(Class);
|
||||
|
||||
/* This function needs to identify objects which should NOT be handled by
|
||||
* weak references.
|
||||
* Nil is a special case which can not be stored as a weak reference because
|
||||
* it indicates the absence of an object etc.
|
||||
* Persistent objects do not need any sort of weak (or strong) reference and
|
||||
* if they are immutable then trying to mark them as referenced would crash.
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline BOOL
|
||||
isPersistentObject(id obj)
|
||||
{
|
||||
Class c;
|
||||
int i;
|
||||
|
||||
if (nil == obj)
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
/* If the alignment of the object does not match that needed for a
|
||||
* pointer (to the class of the object) then the object must be a
|
||||
* special one of some sort and we assume it's persistent.
|
||||
*/
|
||||
#if GS_SIZEOF_VOIDP == 8
|
||||
if ((intptr_t)obj & 15)
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
#else
|
||||
if ((intptr_t)obj & 7)
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
#endif
|
||||
|
||||
c = object_getClass(obj);
|
||||
if (class_isMetaClass(c))
|
||||
{
|
||||
return YES; // obj was a class rather than an instance
|
||||
}
|
||||
|
||||
for (i = 0; i < persistentClassCount; i++)
|
||||
{
|
||||
if (persistentClasses[i] == c)
|
||||
{
|
||||
return YES; // the object is a persistent instance
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
static int WeakRefClass = 0;
|
||||
|
||||
#define GSI_MAP_NODE_CLASS (&WeakRefClass)
|
||||
#define GSI_MAP_CLEAR_KEY(M, X) ((*X).obj = nil)
|
||||
#define GSI_MAP_HASH(M, X) ((NSUInteger)(X.obj) >> 2)
|
||||
#define GSI_MAP_EQUAL(M, X, Y) (X.obj == Y.obj)
|
||||
#define GSI_MAP_RETAIN_KEY(M, X)
|
||||
#define GSI_MAP_RELEASE_KEY(M, X)
|
||||
#define GSI_MAP_RETAIN_VAL(M, X)
|
||||
#define GSI_MAP_RELEASE_VAL(M, X)
|
||||
#define GSI_MAP_KTYPES GSUNION_OBJ
|
||||
#define GSI_MAP_VTYPES GSUNION_NSINT
|
||||
|
||||
#include "GNUstepBase/GSIMap.h"
|
||||
|
||||
typedef GSIMapNode_t WeakRef;
|
||||
|
||||
static gs_mutex_t weakLock = GS_MUTEX_INIT_STATIC;
|
||||
|
||||
/* The weakRefs table contains weak references (nodes) for weak references
|
||||
* to any active objects.
|
||||
*/
|
||||
static GSIMapTable_t weakRefs = { 0 };
|
||||
|
||||
/* The deallocated list contains the weak references (nodes) for objects
|
||||
* which have already been deallocated (so the references are now to nil).
|
||||
*/
|
||||
static WeakRef *deallocated = NULL;
|
||||
|
||||
|
||||
/* This must be called on startup before any weak references are taken.
|
||||
*/
|
||||
void
|
||||
GSWeakInit()
|
||||
{
|
||||
GS_MUTEX_LOCK(weakLock);
|
||||
if (0 == weakRefs.increment)
|
||||
{
|
||||
GSIMapInitWithZoneAndCapacity(
|
||||
&weakRefs, NSDefaultMallocZone(), 1024);
|
||||
persistentClasses[0] = [NXConstantString class];
|
||||
}
|
||||
GS_MUTEX_UNLOCK(weakLock);
|
||||
}
|
||||
|
||||
/* Load from a weak pointer and return whether this really was a weak
|
||||
* reference or a strong (not deallocatable) object in a weak pointer.
|
||||
* The object will be stored in 'obj' and the weak reference in 'ref',
|
||||
* if one exists.
|
||||
*/
|
||||
inline static BOOL
|
||||
loadWeakPointer(id *addr, id *obj, WeakRef **ref)
|
||||
{
|
||||
id oldObj = *addr;
|
||||
|
||||
if (nil == oldObj)
|
||||
{
|
||||
*ref = NULL;
|
||||
*obj = nil;
|
||||
return NO;
|
||||
}
|
||||
if (*(void**)oldObj == (void*)&WeakRefClass)
|
||||
{
|
||||
*ref = (WeakRef*)oldObj;
|
||||
*obj = (*ref)->key.obj;
|
||||
return YES;
|
||||
}
|
||||
*ref = NULL;
|
||||
*obj = oldObj;
|
||||
return NO;
|
||||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline BOOL
|
||||
weakRefRelease(WeakRef *ref)
|
||||
{
|
||||
ref->value.nsi--;
|
||||
if (ref->value.nsi == 0)
|
||||
{
|
||||
if (nil == ref->key.obj)
|
||||
{
|
||||
/* The object was already deallocated so we must remove this
|
||||
* reference from the deallocated list.
|
||||
*/
|
||||
if (deallocated == ref)
|
||||
{
|
||||
deallocated = ref->nextInBucket;
|
||||
}
|
||||
else
|
||||
{
|
||||
WeakRef *tmp = deallocated;
|
||||
|
||||
while (tmp->nextInBucket != 0)
|
||||
{
|
||||
if (tmp->nextInBucket == ref)
|
||||
{
|
||||
tmp->nextInBucket = ref->nextInBucket;
|
||||
break;
|
||||
}
|
||||
tmp = tmp->nextInBucket;
|
||||
}
|
||||
}
|
||||
ref->nextInBucket = weakRefs.freeNodes;
|
||||
weakRefs.freeNodes = ref;
|
||||
}
|
||||
else
|
||||
{
|
||||
GSIMapBucket bucket = GSIMapBucketForKey(&weakRefs, ref->key);
|
||||
|
||||
GSIMapRemoveNodeFromMap(&weakRefs, bucket, ref);
|
||||
GSIMapFreeNode(&weakRefs, ref);
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
/* We should record the fact that the object has weak references (unless
|
||||
* it is a persistent one).
|
||||
* Return YES if the object is persistent and should not have weak references,
|
||||
* NO otherwise.
|
||||
*/
|
||||
static BOOL
|
||||
setObjectHasWeakRefs(id obj)
|
||||
{
|
||||
BOOL isPersistent = isPersistentObject(obj);
|
||||
|
||||
if (NO == isPersistent)
|
||||
{
|
||||
/* FIXME ... for performance we should mark the object as having
|
||||
* weak references and we should check that in objc_delete_weak_refs()
|
||||
*/
|
||||
}
|
||||
return isPersistent;
|
||||
}
|
||||
|
||||
static WeakRef *
|
||||
incrementWeakRefCount(id obj)
|
||||
{
|
||||
GSIMapKey key;
|
||||
GSIMapBucket bucket;
|
||||
WeakRef *ref;
|
||||
|
||||
/* Mark the instance as having had a weak reference at some point.
|
||||
* This information is used when the instance is deallocated.
|
||||
*/
|
||||
GSPrivateMarkedWeak(obj, YES);
|
||||
|
||||
key.obj = obj;
|
||||
bucket = GSIMapBucketForKey(&weakRefs, key);
|
||||
ref = GSIMapNodeForKeyInBucket(&weakRefs, bucket, key);
|
||||
if (NULL == ref)
|
||||
{
|
||||
ref = GSIMapGetNode(&weakRefs);
|
||||
|
||||
ref->key.obj = obj;
|
||||
ref->value.nsi = 1;
|
||||
GSIMapAddNodeToBucket(bucket, ref);
|
||||
weakRefs.nodeCount++;
|
||||
return ref;
|
||||
}
|
||||
ref->value.nsi++;
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
id
|
||||
objc_storeWeak(id *addr, id obj)
|
||||
{
|
||||
WeakRef *oldRef;
|
||||
id old;
|
||||
BOOL isGlobalObject;
|
||||
|
||||
GS_MUTEX_LOCK(weakLock);
|
||||
loadWeakPointer(addr, &old, &oldRef);
|
||||
/* If the old and new values are the same (and we are not setting a nil
|
||||
* value to destroy an existing weak reference), then we don't need to
|
||||
* do anything.
|
||||
*/
|
||||
if ((obj != nil || oldRef == NULL) && old == obj)
|
||||
{
|
||||
GS_MUTEX_UNLOCK(weakLock);
|
||||
return obj;
|
||||
}
|
||||
isGlobalObject = setObjectHasWeakRefs(obj);
|
||||
|
||||
/* If we old ref exists, decrement its reference count. This may also
|
||||
* delete the weak reference from the map.
|
||||
*/
|
||||
if (oldRef != NULL)
|
||||
{
|
||||
weakRefRelease(oldRef);
|
||||
}
|
||||
|
||||
/* If we're storing nil, then just write a null pointer.
|
||||
*/
|
||||
if (nil == obj)
|
||||
{
|
||||
*addr = obj;
|
||||
}
|
||||
else if (isGlobalObject)
|
||||
{
|
||||
/* If this is a global object, it's never deallocated,
|
||||
* so we don't make this a weak reference.
|
||||
*/
|
||||
*addr = obj;
|
||||
}
|
||||
else if ([obj retainCount] == 0)
|
||||
{
|
||||
/* The object is being deallocated ... we must store nil.
|
||||
*/
|
||||
*addr = obj = nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
*addr = (id)incrementWeakRefCount(obj);
|
||||
}
|
||||
GS_MUTEX_UNLOCK(weakLock);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* Function called when objects are deallocated
|
||||
*/
|
||||
BOOL
|
||||
objc_delete_weak_refs(id obj)
|
||||
{
|
||||
GSIMapKey key;
|
||||
GSIMapBucket bucket;
|
||||
WeakRef *ref;
|
||||
|
||||
/* For performance we should have marked the object as having
|
||||
* weak references and we check that in order to avoid the cost
|
||||
* of the map table lookup when it's not needed.
|
||||
*/
|
||||
if (NO == GSPrivateMarkedWeak(obj, NO))
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
key.obj = obj;
|
||||
GS_MUTEX_LOCK(weakLock);
|
||||
bucket = GSIMapBucketForKey(&weakRefs, key);
|
||||
ref = GSIMapNodeForKeyInBucket(&weakRefs, bucket, key);
|
||||
if (ref)
|
||||
{
|
||||
GSIMapRemoveNodeFromBucket(bucket, ref);
|
||||
ref->key.obj = nil;
|
||||
weakRefs.nodeCount--;
|
||||
/* The object is deallocated but there are still weak references
|
||||
* to it so we put the weak reference node in the deallocated list.
|
||||
*/
|
||||
ref->nextInBucket = deallocated;
|
||||
deallocated = ref;
|
||||
}
|
||||
GS_MUTEX_UNLOCK(weakLock);
|
||||
return YES;
|
||||
}
|
||||
|
||||
id
|
||||
objc_loadWeakRetained(id *addr)
|
||||
{
|
||||
id obj;
|
||||
WeakRef *ref;
|
||||
|
||||
GS_MUTEX_LOCK(weakLock);
|
||||
|
||||
/* If this is not actually a weak pointer, return the object directly.
|
||||
*/
|
||||
if (!loadWeakPointer(addr, &obj, &ref))
|
||||
{
|
||||
GS_MUTEX_UNLOCK(weakLock);
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (nil == obj)
|
||||
{
|
||||
/* The object has been destroed so we should remove the weak
|
||||
* reference to it.
|
||||
*/
|
||||
if (ref != NULL)
|
||||
{
|
||||
weakRefRelease(ref);
|
||||
*addr = nil;
|
||||
}
|
||||
GS_MUTEX_UNLOCK(weakLock);
|
||||
return nil;
|
||||
}
|
||||
|
||||
obj = [obj retain];
|
||||
GS_MUTEX_UNLOCK(weakLock);
|
||||
return obj;
|
||||
}
|
||||
|
||||
id
|
||||
objc_loadWeak(id *object)
|
||||
{
|
||||
return [objc_loadWeakRetained(object) autorelease];
|
||||
}
|
||||
|
||||
void
|
||||
objc_copyWeak(id *dest, id *src)
|
||||
{
|
||||
/* Don't retain or release.
|
||||
* 'src' is a valid pointer to a __weak pointer or nil.
|
||||
* 'dest' is a valid pointer to uninitialised memory.
|
||||
* After this operation, 'dest' should contain whatever 'src' contained.
|
||||
*/
|
||||
id obj;
|
||||
WeakRef *srcRef;
|
||||
|
||||
GS_MUTEX_LOCK(weakLock);
|
||||
loadWeakPointer(src, &obj, &srcRef);
|
||||
*dest = *src;
|
||||
if (srcRef)
|
||||
{
|
||||
srcRef->value.nsi++;
|
||||
}
|
||||
GS_MUTEX_UNLOCK(weakLock);
|
||||
}
|
||||
|
||||
void
|
||||
objc_moveWeak(id *dest, id *src)
|
||||
{
|
||||
/* Don't retain or release.
|
||||
* 'dest' is a valid pointer to uninitialized memory.
|
||||
* 'src' is a valid pointer to a __weak pointer.
|
||||
* This operation moves from *src to *dest and must be atomic with respect
|
||||
* to other stores to *src via 'objc_storeWeak'.
|
||||
*/
|
||||
GS_MUTEX_LOCK(weakLock);
|
||||
*dest = *src;
|
||||
*src = nil;
|
||||
GS_MUTEX_UNLOCK(weakLock);
|
||||
}
|
||||
|
||||
void
|
||||
objc_destroyWeak(id *obj)
|
||||
{
|
||||
WeakRef *oldRef;
|
||||
id old;
|
||||
|
||||
GS_MUTEX_LOCK(weakLock);
|
||||
loadWeakPointer(obj, &old, &oldRef);
|
||||
/* If the old ref exists, decrement its reference count.
|
||||
* This may also remove the weak reference from the map.
|
||||
*/
|
||||
if (oldRef != NULL)
|
||||
{
|
||||
weakRefRelease(oldRef);
|
||||
}
|
||||
GS_MUTEX_UNLOCK(weakLock);
|
||||
}
|
||||
|
||||
id
|
||||
objc_initWeak(id *addr, id obj)
|
||||
{
|
||||
BOOL isGlobalObject;
|
||||
|
||||
if (nil == obj)
|
||||
{
|
||||
*addr = nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
GS_MUTEX_LOCK(weakLock);
|
||||
isGlobalObject = setObjectHasWeakRefs(obj);
|
||||
if (isGlobalObject)
|
||||
{
|
||||
*addr = obj;
|
||||
GS_MUTEX_UNLOCK(weakLock);
|
||||
return obj;
|
||||
}
|
||||
|
||||
if ([obj retainCount] == 0)
|
||||
{
|
||||
*addr = nil;
|
||||
GS_MUTEX_UNLOCK(weakLock);
|
||||
return nil;
|
||||
}
|
||||
if (nil != obj)
|
||||
{
|
||||
*(WeakRef**)addr = incrementWeakRefCount(obj);
|
||||
}
|
||||
GS_MUTEX_UNLOCK(weakLock);
|
||||
return obj;
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue