mirror of
https://github.com/gnustep/libs-base.git
synced 2025-05-30 16:30:41 +00:00
__weak is only allowed on ivars and globals, so we need to turn the Observation structure into a class.
Richard: I've run the test suite with GC disabled on Linux/x86-64 and nothing broke, but can you please review these changes carefully anyway? We seem to be using a complex custom allocator for a structure that is not allocated or deallocated. In typical programs, it looks like we're actually just wasting memory by using the memory pool here. Looking at the commit log, this hasn't really been touched for about 10 years, so possibly the assumptions are no longer valid. I can only see this being useful if someone is adding and removing hundreds of notification observers every run loop iteration. Do you have code that does this? If not, then can I remove the custom allocator? git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@33165 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
07fa091275
commit
19e71aa612
1 changed files with 86 additions and 43 deletions
|
@ -141,14 +141,24 @@ struct NCTbl; /* Notification Center Table structure */
|
||||||
* list is marked by 'next' being set to 'ENDOBS'.
|
* list is marked by 'next' being set to 'ENDOBS'.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef struct Obs {
|
@interface GSObservation : NSObject
|
||||||
|
{
|
||||||
|
@public
|
||||||
__weak id observer; /* Object to receive message. */
|
__weak id observer; /* Object to receive message. */
|
||||||
SEL selector; /* Method selector. */
|
SEL selector; /* Method selector. */
|
||||||
IMP method; /* Method implementation. */
|
IMP method; /* Method implementation. */
|
||||||
struct Obs *next; /* Next item in linked list. */
|
GSObservation *next; /* Next item in linked list. */
|
||||||
int retained; /* Retain count for structure. */
|
#ifndef __OBJC_GC__
|
||||||
|
/** Retain count. This must be stored internally, because we're
|
||||||
|
* allocating a load of these in an array, so we can't rely on the existence
|
||||||
|
* of the header that NSAllocateObject() adds (in non-GC mode). */
|
||||||
|
int retained;
|
||||||
|
#endif
|
||||||
struct NCTbl *link; /* Pointer back to chunk table */
|
struct NCTbl *link; /* Pointer back to chunk table */
|
||||||
} Observation;
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
typedef GSObservation Observation;
|
||||||
|
|
||||||
#define ENDOBS ((Observation*)-1)
|
#define ENDOBS ((Observation*)-1)
|
||||||
|
|
||||||
|
@ -188,14 +198,12 @@ static inline BOOL doEqual(NSString* key1, NSString* key2)
|
||||||
* Setup for inline operation on arrays of Observers.
|
* Setup for inline operation on arrays of Observers.
|
||||||
*/
|
*/
|
||||||
static void listFree(Observation *list);
|
static void listFree(Observation *list);
|
||||||
static void obsRetain(Observation *o);
|
|
||||||
static void obsFree(Observation *o);
|
|
||||||
|
|
||||||
|
|
||||||
#define GSI_ARRAY_TYPES 0
|
#define GSI_ARRAY_TYPES 0
|
||||||
#define GSI_ARRAY_TYPE Observation*
|
#define GSI_ARRAY_TYPE Observation*
|
||||||
#define GSI_ARRAY_RELEASE(A, X) obsFree(X.ext)
|
#define GSI_ARRAY_RELEASE(A, X) [X.ext release]
|
||||||
#define GSI_ARRAY_RETAIN(A, X) obsRetain(X.ext)
|
#define GSI_ARRAY_RETAIN(A, X) [X.ext retain]
|
||||||
|
|
||||||
#include "GNUstepBase/GSIArray.h"
|
#include "GNUstepBase/GSIArray.h"
|
||||||
|
|
||||||
|
@ -252,12 +260,16 @@ typedef struct NCTbl {
|
||||||
GSIMapTable named; /* Getting named messages only. */
|
GSIMapTable named; /* Getting named messages only. */
|
||||||
unsigned lockCount; /* Count recursive operations. */
|
unsigned lockCount; /* Count recursive operations. */
|
||||||
GSLazyRecursiveLock *_lock; /* Lock out other threads. */
|
GSLazyRecursiveLock *_lock; /* Lock out other threads. */
|
||||||
|
#ifndef __OBJC_GC__
|
||||||
|
// In GC mode, we don't bother with a memory pool for observations, we let
|
||||||
|
// the GC manage all of this
|
||||||
Observation *freeList;
|
Observation *freeList;
|
||||||
Observation **chunks;
|
Observation **chunks;
|
||||||
unsigned numChunks;
|
unsigned numChunks;
|
||||||
GSIMapTable cache[CACHESIZE];
|
GSIMapTable cache[CACHESIZE];
|
||||||
unsigned short chunkIndex;
|
unsigned short chunkIndex;
|
||||||
unsigned short cacheIndex;
|
unsigned short cacheIndex;
|
||||||
|
#endif
|
||||||
} NCTable;
|
} NCTable;
|
||||||
|
|
||||||
#define TABLE ((NCTable*)_table)
|
#define TABLE ((NCTable*)_table)
|
||||||
|
@ -266,8 +278,52 @@ typedef struct NCTbl {
|
||||||
#define NAMED (TABLE->named)
|
#define NAMED (TABLE->named)
|
||||||
#define LOCKCOUNT (TABLE->lockCount)
|
#define LOCKCOUNT (TABLE->lockCount)
|
||||||
|
|
||||||
|
@implementation GSObservation
|
||||||
|
// We don't need the finalize method on ObjC-GC mode, because the collector
|
||||||
|
// will automatically delete the zeroing weak reference, and we can't compile
|
||||||
|
// the dealloc method in this mode because we're also not creating the fields
|
||||||
|
// that it refers to.
|
||||||
|
#ifndef __OBJC_GC__
|
||||||
|
- (id)retain
|
||||||
|
{
|
||||||
|
retained++;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
- (void)release
|
||||||
|
{
|
||||||
|
NSAssert(retained >= 0, NSInternalInconsistencyException);
|
||||||
|
if (retained-- == 0)
|
||||||
|
{
|
||||||
|
#if GS_WITH_GC
|
||||||
|
GSAssignZeroingWeakPointer((void**)&observer, 0);
|
||||||
|
#endif
|
||||||
|
NCTable *t = link;
|
||||||
|
link = (NCTable*)t->freeList;
|
||||||
|
t->freeList = self;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
// Don't actually try to destroy this in response to a -dealloc message
|
||||||
|
// (should not be sent), since memory is allocated in a pool.
|
||||||
|
GSNOSUPERDEALLOC;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
static Observation *obsNew(NCTable* t)
|
static Observation *obsNew(NCTable* t)
|
||||||
{
|
{
|
||||||
|
static Class observationClass;
|
||||||
|
static size_t observationSize;
|
||||||
|
if (0 == observationClass)
|
||||||
|
{
|
||||||
|
observationClass = [GSObservation class];
|
||||||
|
observationSize = class_getInstanceSize(observationClass);
|
||||||
|
}
|
||||||
|
#if __OBJC_GC__
|
||||||
|
return NSAllocateObject(observationClass, 0, _zone);
|
||||||
|
#else
|
||||||
Observation *obs;
|
Observation *obs;
|
||||||
|
|
||||||
if (t->freeList == 0)
|
if (t->freeList == 0)
|
||||||
|
@ -289,7 +345,7 @@ static Observation *obsNew(NCTable* t)
|
||||||
t->chunks, size);
|
t->chunks, size);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
size = CHUNKSIZE * sizeof(Observation);
|
size = CHUNKSIZE * observationSize;
|
||||||
#if GS_WITH_GC
|
#if GS_WITH_GC
|
||||||
t->chunks[t->numChunks - 1]
|
t->chunks[t->numChunks - 1]
|
||||||
= (Observation*)NSAllocateCollectable(size, 0);
|
= (Observation*)NSAllocateCollectable(size, 0);
|
||||||
|
@ -300,18 +356,27 @@ static Observation *obsNew(NCTable* t)
|
||||||
t->chunkIndex = 0;
|
t->chunkIndex = 0;
|
||||||
}
|
}
|
||||||
block = t->chunks[t->numChunks - 1];
|
block = t->chunks[t->numChunks - 1];
|
||||||
t->freeList = &block[t->chunkIndex];
|
|
||||||
|
t->freeList = (Observation*)((char*)block+(observationSize * t->chunkIndex));
|
||||||
t->chunkIndex++;
|
t->chunkIndex++;
|
||||||
t->freeList->link = 0;
|
t->freeList->link = 0;
|
||||||
}
|
}
|
||||||
obs = t->freeList;
|
obs = t->freeList;
|
||||||
t->freeList = (Observation*)obs->link;
|
t->freeList = (Observation*)obs->link;
|
||||||
obs->link = (void*)t;
|
obs->link = (void*)t;
|
||||||
|
object_setClass(obs, observationClass);
|
||||||
return obs;
|
return obs;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static GSIMapTable mapNew(NCTable *t)
|
static GSIMapTable mapNew(NCTable *t)
|
||||||
{
|
{
|
||||||
|
#ifdef __OBJC_GC__
|
||||||
|
GSIMapTable m =
|
||||||
|
NSAllocateCollectable(sizeof(GSIMapTable_t), NSScannedOption);
|
||||||
|
GSIMapInitWithZoneAndCapacity(m, 0, 2);
|
||||||
|
return m;
|
||||||
|
#else
|
||||||
if (t->cacheIndex > 0)
|
if (t->cacheIndex > 0)
|
||||||
return t->cache[--t->cacheIndex];
|
return t->cache[--t->cacheIndex];
|
||||||
else
|
else
|
||||||
|
@ -326,10 +391,12 @@ static GSIMapTable mapNew(NCTable *t)
|
||||||
GSIMapInitWithZoneAndCapacity(m, _zone, 2);
|
GSIMapInitWithZoneAndCapacity(m, _zone, 2);
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mapFree(NCTable *t, GSIMapTable m)
|
static void mapFree(NCTable *t, GSIMapTable m)
|
||||||
{
|
{
|
||||||
|
#ifndef __OBJC_GC__
|
||||||
if (t->cacheIndex < CACHESIZE)
|
if (t->cacheIndex < CACHESIZE)
|
||||||
{
|
{
|
||||||
t->cache[t->cacheIndex++] = m;
|
t->cache[t->cacheIndex++] = m;
|
||||||
|
@ -339,10 +406,12 @@ static void mapFree(NCTable *t, GSIMapTable m)
|
||||||
GSIMapEmptyMap(m);
|
GSIMapEmptyMap(m);
|
||||||
NSZoneFree(NSDefaultMallocZone(), (void*)m);
|
NSZoneFree(NSDefaultMallocZone(), (void*)m);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void endNCTable(NCTable *t)
|
static void endNCTable(NCTable *t)
|
||||||
{
|
{
|
||||||
|
#ifndef __OBJC_GC__
|
||||||
unsigned i;
|
unsigned i;
|
||||||
GSIMapEnumerator_t e0;
|
GSIMapEnumerator_t e0;
|
||||||
GSIMapNode n0;
|
GSIMapNode n0;
|
||||||
|
@ -405,28 +474,21 @@ static void endNCTable(NCTable *t)
|
||||||
NSZoneFree(NSDefaultMallocZone(), t);
|
NSZoneFree(NSDefaultMallocZone(), t);
|
||||||
|
|
||||||
TEST_RELEASE(t->_lock);
|
TEST_RELEASE(t->_lock);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static NCTable *newNCTable(void)
|
static NCTable *newNCTable(void)
|
||||||
{
|
{
|
||||||
NCTable *t;
|
NCTable *t;
|
||||||
|
|
||||||
#if GS_WITH_GC
|
|
||||||
t = (NCTable*)NSAllocateCollectable(sizeof(NCTable), NSScannedOption);
|
t = (NCTable*)NSAllocateCollectable(sizeof(NCTable), NSScannedOption);
|
||||||
#else
|
#ifndef __OBJC_GC__
|
||||||
t = (NCTable*)NSZoneMalloc(_zone, sizeof(NCTable));
|
|
||||||
memset((void*)t, '\0', sizeof(NCTable));
|
|
||||||
#endif
|
|
||||||
t->chunkIndex = CHUNKSIZE;
|
t->chunkIndex = CHUNKSIZE;
|
||||||
|
#endif
|
||||||
t->wildcard = ENDOBS;
|
t->wildcard = ENDOBS;
|
||||||
|
|
||||||
#if GS_WITH_GC
|
|
||||||
t->nameless = NSAllocateCollectable(sizeof(GSIMapTable_t), NSScannedOption);
|
t->nameless = NSAllocateCollectable(sizeof(GSIMapTable_t), NSScannedOption);
|
||||||
t->named = NSAllocateCollectable(sizeof(GSIMapTable_t), NSScannedOption);
|
t->named = NSAllocateCollectable(sizeof(GSIMapTable_t), NSScannedOption);
|
||||||
#else
|
|
||||||
t->nameless = NSZoneMalloc(_zone, sizeof(GSIMapTable_t));
|
|
||||||
t->named = NSZoneMalloc(_zone, sizeof(GSIMapTable_t));
|
|
||||||
#endif
|
|
||||||
GSIMapInitWithZoneAndCapacity(t->nameless, _zone, 16);
|
GSIMapInitWithZoneAndCapacity(t->nameless, _zone, 16);
|
||||||
GSIMapInitWithZoneAndCapacity(t->named, _zone, 128);
|
GSIMapInitWithZoneAndCapacity(t->named, _zone, 128);
|
||||||
|
|
||||||
|
@ -447,21 +509,6 @@ static inline void unlockNCTable(NCTable* t)
|
||||||
[t->_lock unlock];
|
[t->_lock unlock];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void obsFree(Observation *o)
|
|
||||||
{
|
|
||||||
NSCAssert(o->retained >= 0, NSInternalInconsistencyException);
|
|
||||||
if (o->retained-- == 0)
|
|
||||||
{
|
|
||||||
NCTable *t = o->link;
|
|
||||||
|
|
||||||
#if GS_WITH_GC
|
|
||||||
GSAssignZeroingWeakPointer((void**)&o->observer, 0);
|
|
||||||
#endif
|
|
||||||
o->link = (NCTable*)t->freeList;
|
|
||||||
t->freeList = o;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void listFree(Observation *list)
|
static void listFree(Observation *list)
|
||||||
{
|
{
|
||||||
while (list != ENDOBS)
|
while (list != ENDOBS)
|
||||||
|
@ -470,7 +517,7 @@ static void listFree(Observation *list)
|
||||||
|
|
||||||
list = o->next;
|
list = o->next;
|
||||||
o->next = 0;
|
o->next = 0;
|
||||||
obsFree(o);
|
[o release];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,7 +537,7 @@ static Observation *listPurge(Observation *list, id observer)
|
||||||
{
|
{
|
||||||
tmp = list->next;
|
tmp = list->next;
|
||||||
list->next = 0;
|
list->next = 0;
|
||||||
obsFree(list);
|
[list release];
|
||||||
list = tmp;
|
list = tmp;
|
||||||
}
|
}
|
||||||
if (list != ENDOBS)
|
if (list != ENDOBS)
|
||||||
|
@ -504,7 +551,7 @@ static Observation *listPurge(Observation *list, id observer)
|
||||||
|
|
||||||
tmp->next = next->next;
|
tmp->next = next->next;
|
||||||
next->next = 0;
|
next->next = 0;
|
||||||
obsFree(next);
|
[next release];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -515,10 +562,6 @@ static Observation *listPurge(Observation *list, id observer)
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void obsRetain(Observation *o)
|
|
||||||
{
|
|
||||||
o->retained++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Utility function to remove all the observations from a particular
|
* Utility function to remove all the observations from a particular
|
||||||
|
@ -565,7 +608,7 @@ purgeMapNode(GSIMapTable map, GSIMapNode node, id observer)
|
||||||
* purgeCollectedFromMapNode() does the same thing but also handles cleanup
|
* purgeCollectedFromMapNode() does the same thing but also handles cleanup
|
||||||
* of the map node containing the list if necessary.
|
* of the map node containing the list if necessary.
|
||||||
*/
|
*/
|
||||||
#if GS_WITH_GC
|
#if GS_WITH_GC || __OBJC_GC__
|
||||||
#define purgeCollected(X) listPurge(X, nil)
|
#define purgeCollected(X) listPurge(X, nil)
|
||||||
static Observation*
|
static Observation*
|
||||||
purgeCollectedFromMapNode(GSIMapTable map, GSIMapNode node)
|
purgeCollectedFromMapNode(GSIMapTable map, GSIMapNode node)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue