1996-03-03 01:41:20 +00:00
|
|
|
|
/* Implementation of NSNotificationCenter for GNUstep
|
1999-06-17 13:17:28 +00:00
|
|
|
|
Copyright (C) 1999 Free Software Foundation, Inc.
|
1996-02-13 16:09:50 +00:00
|
|
|
|
|
1999-06-17 13:17:28 +00:00
|
|
|
|
Written by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
|
|
|
|
Created: June 1999
|
|
|
|
|
|
|
|
|
|
Many thanks for the earlier version, (from which this is loosely
|
|
|
|
|
derived) by Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
|
1996-03-03 01:41:20 +00:00
|
|
|
|
Created: March 1996
|
|
|
|
|
|
1996-05-12 00:56:10 +00:00
|
|
|
|
This file is part of the GNUstep Base Library.
|
1996-02-13 16:09:50 +00:00
|
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1996-02-13 16:09:50 +00:00
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
Library General Public License for more details.
|
1996-03-03 01:41:20 +00:00
|
|
|
|
|
1996-02-13 16:09:50 +00:00
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
|
|
|
License along with this library; if not, write to the Free
|
1999-09-09 02:56:20 +00:00
|
|
|
|
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
|
1996-03-03 01:41:20 +00:00
|
|
|
|
*/
|
1996-02-13 16:09:50 +00:00
|
|
|
|
|
|
|
|
|
#include <Foundation/NSNotification.h>
|
1999-06-17 13:17:28 +00:00
|
|
|
|
#include <Foundation/NSException.h>
|
|
|
|
|
#include <Foundation/NSLock.h>
|
1999-06-24 19:30:29 +00:00
|
|
|
|
#include <Foundation/NSThread.h>
|
|
|
|
|
#include <base/fast.x>
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
typedef struct {
|
|
|
|
|
@defs(NSNotification)
|
|
|
|
|
} NotificationStruct;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Garbage collection considerations -
|
|
|
|
|
* The notification center is not supposed to retain any notification
|
|
|
|
|
* observers or notification objects. To achieve this when using garbage
|
|
|
|
|
* collection, we must hide all references to observers and objects.
|
|
|
|
|
* Within an Observation structure, this is not a problem, we simply
|
|
|
|
|
* allocate the structure using 'atomic' allocation to tell the gc
|
|
|
|
|
* system to ignore pointers inside it.
|
|
|
|
|
* Elsewhere, we store the pointers with a bit added, to hide them from
|
|
|
|
|
* the garbage collector.
|
|
|
|
|
*/
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
struct NCTbl; /* Notification Center Table structure */
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Observation structure - One of these objects is created for
|
|
|
|
|
* each -addObserver... request. It holds the requested selector,
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* name and object. Each struct is placed in one LinkedList,
|
|
|
|
|
* as keyed by the NAME/OBJECT parameters.
|
1999-06-17 13:17:28 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
typedef struct Obs {
|
1999-06-24 19:30:29 +00:00
|
|
|
|
id observer; /* Object to receive message. */
|
|
|
|
|
SEL selector; /* Method selector. */
|
|
|
|
|
IMP method; /* Method implementation. */
|
|
|
|
|
struct Obs *next; /* Next item in linked list. */
|
|
|
|
|
int retained; /* Retain count for structure. */
|
|
|
|
|
struct NCTbl *link; /* Pointer back to chunk table */
|
1999-06-17 13:17:28 +00:00
|
|
|
|
} Observation;
|
|
|
|
|
|
|
|
|
|
#define ENDOBS ((Observation*)-1)
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
static SEL hSel = @selector(hash);
|
|
|
|
|
static SEL eqSel = @selector(isEqualToString:);
|
|
|
|
|
|
|
|
|
|
static unsigned (*cHash)(id, SEL);
|
|
|
|
|
static unsigned (*uHash)(id, SEL);
|
|
|
|
|
static BOOL (*cEqual)(id, SEL, id);
|
|
|
|
|
static BOOL (*uEqual)(id, SEL, id);
|
|
|
|
|
|
|
|
|
|
static inline unsigned doHash(NSString* key)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (key == nil)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
else if (((gsaddr)key) & 1)
|
|
|
|
|
{
|
|
|
|
|
return (unsigned)(gsaddr)key;
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
else
|
1999-06-24 19:30:29 +00:00
|
|
|
|
{
|
|
|
|
|
Class c = fastClassOfInstance(key);
|
|
|
|
|
|
|
|
|
|
if (c == _fastCls._NSGCString || c == _fastCls._NSGMutableCString
|
|
|
|
|
|| c == _fastCls._NXConstantString)
|
|
|
|
|
return (*cHash)(key, hSel);
|
|
|
|
|
if (c == _fastCls._NSGString || c == _fastCls._NSGMutableString)
|
|
|
|
|
return (*uHash)(key, hSel);
|
|
|
|
|
return [key hash];
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
static inline BOOL doEqual(NSString* key1, NSString* key2)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (key1 == key2)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
else if ((((gsaddr)key1) & 1) || key1 == nil)
|
|
|
|
|
{
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Class c = fastClassOfInstance(key1);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (c == _fastCls._NSGString)
|
|
|
|
|
return (*uEqual)(key1, eqSel, key2);
|
|
|
|
|
else
|
|
|
|
|
return (*cEqual)(key1, eqSel, key2);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
/*
|
|
|
|
|
* Setup for inline operation on arrays of Observers.
|
|
|
|
|
*/
|
|
|
|
|
static void listFree(Observation *list);
|
|
|
|
|
static void obsRetain(Observation *o);
|
|
|
|
|
static void obsFree(Observation *o);
|
|
|
|
|
|
|
|
|
|
#define GSI_ARRAY_TYPES 0
|
|
|
|
|
#define GSI_ARRAY_EXTRA Observation*
|
|
|
|
|
|
|
|
|
|
#define GSI_ARRAY_RELEASE(X) obsFree(X.ext)
|
|
|
|
|
#define GSI_ARRAY_RETAIN(X) obsRetain(X.ext)
|
|
|
|
|
|
|
|
|
|
#include <base/GSIArray.h>
|
|
|
|
|
|
1999-06-24 19:33:13 +00:00
|
|
|
|
#define GSI_MAP_RETAIN_KEY(X)
|
1999-09-28 19:35:09 +00:00
|
|
|
|
#define GSI_MAP_RELEASE_KEY(X) ({if ((((gsaddr)X.obj) & 1) == 0) \
|
|
|
|
|
RELEASE(X.obj);})
|
1999-06-24 19:30:29 +00:00
|
|
|
|
#define GSI_MAP_HASH(X) doHash(X.obj)
|
|
|
|
|
#define GSI_MAP_EQUAL(X,Y) doEqual(X.obj, Y.obj)
|
|
|
|
|
|
1999-06-24 19:33:13 +00:00
|
|
|
|
#define GSI_MAP_RETAIN_VAL(X)
|
1999-06-24 19:30:29 +00:00
|
|
|
|
#define GSI_MAP_RELEASE_VAL(X)
|
|
|
|
|
#define GSI_MAP_KTYPES GSUNION_OBJ|GSUNION_INT
|
|
|
|
|
#define GSI_MAP_VTYPES GSUNION_PTR
|
|
|
|
|
#define GSI_MAP_VEXTRA Observation*
|
|
|
|
|
#define GSI_MAP_EXTRA 1
|
|
|
|
|
|
|
|
|
|
#include <base/GSIMap.h>
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
|
* the chunk table, rather than being released to the general
|
|
|
|
|
* memory allocation system. This means that, once a large numbner
|
|
|
|
|
* of observers have been registered, memory usage will never shrink
|
|
|
|
|
* even if the observers are removed. On the other hand, the process
|
|
|
|
|
* of adding and removing observers is speeded up.
|
|
|
|
|
*
|
|
|
|
|
* As another minor aid to performance, we also maintain a cache of
|
|
|
|
|
* the map tables used to keep mappings of notification objects to
|
|
|
|
|
* lists of Observations. This lets us avoid the overhead of creating
|
|
|
|
|
* and destroying map tables when we are frequently adding and removing
|
|
|
|
|
* notification observations.
|
|
|
|
|
*
|
|
|
|
|
* Performance is however, not the primary reason for using this
|
|
|
|
|
* structure - it provides a neat way to ensure that observers pointed
|
|
|
|
|
* to by the Observation structures are not seen as being in use by
|
|
|
|
|
* the garbage collection mechanism.
|
|
|
|
|
*/
|
|
|
|
|
#define CHUNKSIZE 128
|
|
|
|
|
#define CACHESIZE 16
|
|
|
|
|
typedef struct NCTbl {
|
|
|
|
|
Observation *wildcard; /* Get ALL messages. */
|
|
|
|
|
GSIMapTable nameless; /* Get messages for any name. */
|
|
|
|
|
GSIMapTable named; /* Getting named messages only. */
|
|
|
|
|
GSIArray array; /* Temp store during posting. */
|
|
|
|
|
unsigned lockCount; /* Count recursive operations. */
|
|
|
|
|
NSRecursiveLock *_lock; /* Lock out other threads. */
|
|
|
|
|
IMP lImp;
|
|
|
|
|
IMP uImp;
|
|
|
|
|
BOOL lockingDisabled;
|
|
|
|
|
BOOL immutableInPost;
|
|
|
|
|
|
|
|
|
|
Observation *freeList;
|
|
|
|
|
Observation **chunks;
|
|
|
|
|
unsigned numChunks;
|
|
|
|
|
GSIMapTable cache[CACHESIZE];
|
|
|
|
|
short chunkIndex;
|
|
|
|
|
short cacheIndex;
|
|
|
|
|
} NCTable;
|
|
|
|
|
|
1999-09-16 07:21:34 +00:00
|
|
|
|
#define TABLE ((NCTable*)_table)
|
1999-06-24 19:30:29 +00:00
|
|
|
|
#define WILDCARD (TABLE->wildcard)
|
|
|
|
|
#define NAMELESS (TABLE->nameless)
|
|
|
|
|
#define NAMED (TABLE->named)
|
|
|
|
|
#define ARRAY (TABLE->array)
|
|
|
|
|
#define LOCKCOUNT (TABLE->lockCount)
|
|
|
|
|
|
|
|
|
|
static Observation *obsNew(NCTable* t)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
Observation *obs;
|
|
|
|
|
|
|
|
|
|
if (t->freeList == 0)
|
|
|
|
|
{
|
|
|
|
|
Observation *block;
|
|
|
|
|
|
|
|
|
|
if (t->chunkIndex == CHUNKSIZE)
|
|
|
|
|
{
|
|
|
|
|
unsigned size;
|
|
|
|
|
|
|
|
|
|
t->numChunks++;
|
|
|
|
|
size = t->numChunks * sizeof(Observation*);
|
|
|
|
|
t->chunks = (Observation**)NSZoneRealloc(NSDefaultMallocZone(),
|
|
|
|
|
t->chunks, size);
|
|
|
|
|
size = CHUNKSIZE * sizeof(Observation);
|
|
|
|
|
#if GS_WITH_GC
|
|
|
|
|
t->chunks[t->numChunks - 1]
|
|
|
|
|
= (Observation*)NSZoneMallocAtomic(NSDefaultMallocZone(), size);
|
|
|
|
|
#else
|
|
|
|
|
t->chunks[t->numChunks - 1]
|
|
|
|
|
= (Observation*)NSZoneMalloc(NSDefaultMallocZone(), size);
|
|
|
|
|
#endif
|
|
|
|
|
t->chunkIndex = 0;
|
|
|
|
|
}
|
|
|
|
|
block = t->chunks[t->numChunks - 1];
|
|
|
|
|
t->freeList = &block[t->chunkIndex];
|
|
|
|
|
t->chunkIndex++;
|
|
|
|
|
t->freeList->link = 0;
|
|
|
|
|
}
|
|
|
|
|
obs = t->freeList;
|
|
|
|
|
t->freeList = (Observation*)obs->link;
|
|
|
|
|
obs->link = (void*)t;
|
|
|
|
|
return obs;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
static GSIMapTable mapNew(NCTable *t)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (t->cacheIndex > 0)
|
|
|
|
|
return t->cache[--t->cacheIndex];
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GSIMapTable m;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
m = NSZoneMalloc(NSDefaultMallocZone(), sizeof(GSIMapTable_t));
|
|
|
|
|
GSIMapInitWithZoneAndCapacity(m, NSDefaultMallocZone(), 2);
|
|
|
|
|
return m;
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
static void mapFree(NCTable *t, GSIMapTable m)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (t->cacheIndex < CACHESIZE)
|
|
|
|
|
t->cache[t->cacheIndex++] = m;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GSIMapEmptyMap(m);
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), (void*)m);
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
static void endNCTable(NCTable *t)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
unsigned i;
|
|
|
|
|
GSIMapNode n0;
|
|
|
|
|
Observation *l;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* free the temporary storage area for observations about to receive msgs.
|
|
|
|
|
*/
|
|
|
|
|
GSIArrayEmpty(t->array);
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), (void*)t->array);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Free observations without notification names or numbers.
|
|
|
|
|
*/
|
|
|
|
|
listFree(t->wildcard);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Free lists of observations without notification names.
|
|
|
|
|
*/
|
|
|
|
|
n0 = t->nameless->firstNode;
|
|
|
|
|
while (n0 != 0)
|
|
|
|
|
{
|
|
|
|
|
l = (Observation*)n0->value.ptr;
|
|
|
|
|
n0 = n0->nextInMap;
|
|
|
|
|
listFree(l);
|
|
|
|
|
}
|
|
|
|
|
GSIMapEmptyMap(t->nameless);
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), (void*)t->nameless);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Free lists of observations keyed by name and observer.
|
|
|
|
|
*/
|
|
|
|
|
n0 = t->named->firstNode;
|
|
|
|
|
while (n0 != 0)
|
|
|
|
|
{
|
|
|
|
|
GSIMapTable m = (GSIMapTable)n0->value.ptr;
|
|
|
|
|
GSIMapNode n1 = m->firstNode;
|
|
|
|
|
|
|
|
|
|
n0 = n0->nextInMap;
|
|
|
|
|
|
|
|
|
|
while (n1 != 0)
|
|
|
|
|
{
|
|
|
|
|
l = (Observation*)n1->value.ptr;
|
|
|
|
|
n1 = n1->nextInMap;
|
|
|
|
|
listFree(l);
|
|
|
|
|
}
|
|
|
|
|
GSIMapEmptyMap(m);
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), (void*)m);
|
|
|
|
|
}
|
|
|
|
|
GSIMapEmptyMap(t->named);
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), (void*)t->named);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < t->numChunks; i++)
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), t->chunks[i]);
|
|
|
|
|
for (i = 0; i < t->cacheIndex; i++)
|
|
|
|
|
{
|
|
|
|
|
GSIMapEmptyMap(t->cache[i]);
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), (void*)t->cache[i]);
|
|
|
|
|
}
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), t->chunks);
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), t);
|
|
|
|
|
|
|
|
|
|
TEST_RELEASE(t->_lock);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
static NCTable *newNCTable()
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
NCTable *t;
|
|
|
|
|
|
|
|
|
|
t = (NCTable*)NSZoneMalloc(NSDefaultMallocZone(), sizeof(NCTable));
|
|
|
|
|
memset((void*)t, '\0', sizeof(NCTable));
|
|
|
|
|
t->chunkIndex = CHUNKSIZE;
|
|
|
|
|
t->wildcard = ENDOBS;
|
|
|
|
|
|
|
|
|
|
t->nameless = NSZoneMalloc(NSDefaultMallocZone(), sizeof(GSIMapTable_t));
|
|
|
|
|
GSIMapInitWithZoneAndCapacity(t->nameless, NSDefaultMallocZone(), 16);
|
|
|
|
|
|
|
|
|
|
t->named = NSZoneMalloc(NSDefaultMallocZone(), sizeof(GSIMapTable_t));
|
|
|
|
|
GSIMapInitWithZoneAndCapacity(t->named, NSDefaultMallocZone(), 128);
|
|
|
|
|
|
|
|
|
|
t->array = NSZoneMalloc(NSDefaultMallocZone(), sizeof(GSIArray_t));
|
|
|
|
|
GSIArrayInitWithZoneAndCapacity(t->array, NSDefaultMallocZone(), 16);
|
|
|
|
|
|
|
|
|
|
return t;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
static inline void lockNCTable(NCTable* t)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (t->_lock != nil && t->lockingDisabled == NO)
|
|
|
|
|
(*t->lImp)(t->_lock, @selector(lock));
|
|
|
|
|
t->lockCount++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void unlockNCTable(NCTable* t)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
t->lockCount--;
|
|
|
|
|
if (t->_lock != nil && t->lockingDisabled == NO)
|
|
|
|
|
(*t->uImp)(t->_lock, @selector(unlock));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void obsFree(Observation *o)
|
|
|
|
|
{
|
|
|
|
|
NSCAssert(o->retained >= 0, NSInternalInconsistencyException);
|
|
|
|
|
if (o->retained-- == 0)
|
|
|
|
|
{
|
|
|
|
|
NCTable *t = o->link;
|
|
|
|
|
|
|
|
|
|
o->link = (NCTable*)t->freeList;
|
|
|
|
|
t->freeList = o;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void listFree(Observation *list)
|
|
|
|
|
{
|
|
|
|
|
while (list != ENDOBS)
|
|
|
|
|
{
|
|
|
|
|
Observation *o = list;
|
|
|
|
|
|
|
|
|
|
list = o->next;
|
|
|
|
|
o->next = 0;
|
|
|
|
|
obsFree(o);
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
|
|
|
|
/*
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* NB. We need to explicitly set the 'next' field of any observation
|
|
|
|
|
* we remove to be zero so that, if it currently exists in an array
|
|
|
|
|
* of observations being posted, the posting code can notice that it
|
|
|
|
|
* has been removed from its linked list.
|
1999-06-17 13:17:28 +00:00
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
static Observation *listPurge(Observation *list, id observer)
|
|
|
|
|
{
|
|
|
|
|
Observation *tmp;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
while (list != ENDOBS && list->observer == observer)
|
|
|
|
|
{
|
|
|
|
|
tmp = list->next;
|
|
|
|
|
list->next = 0;
|
|
|
|
|
obsFree(list);
|
|
|
|
|
list = tmp;
|
|
|
|
|
}
|
|
|
|
|
if (list != ENDOBS)
|
|
|
|
|
{
|
|
|
|
|
tmp = list;
|
|
|
|
|
while (tmp->next != ENDOBS)
|
|
|
|
|
{
|
|
|
|
|
if (tmp->next->observer == observer)
|
|
|
|
|
{
|
|
|
|
|
Observation *next = tmp->next;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
tmp->next = next->next;
|
|
|
|
|
next->next = 0;
|
|
|
|
|
obsFree(next);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
tmp = tmp->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return list;
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
static void obsRetain(Observation *o)
|
|
|
|
|
{
|
|
|
|
|
o->retained++;
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
/*
|
|
|
|
|
* Utility function to remove all the observations from a particular
|
|
|
|
|
* map table node that match the specified observer. If the observer
|
|
|
|
|
* is nil, then all observations are removed.
|
|
|
|
|
* If the list of observations in the map node is emptied, the node is
|
|
|
|
|
* removed from the map.
|
|
|
|
|
*/
|
|
|
|
|
static inline void
|
|
|
|
|
purgeMapNode(GSIMapTable map, GSIMapNode node, id observer)
|
|
|
|
|
{
|
|
|
|
|
Observation *list = node->value.ext;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (observer == 0)
|
|
|
|
|
{
|
|
|
|
|
listFree(list);
|
|
|
|
|
GSIMapRemoveKey(map, node->key);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Observation *start = list;
|
|
|
|
|
|
|
|
|
|
list = listPurge(list, observer);
|
|
|
|
|
if (list == ENDOBS)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* The list is empty so remove from map.
|
|
|
|
|
*/
|
|
|
|
|
GSIMapRemoveKey(map, node->key);
|
|
|
|
|
}
|
|
|
|
|
else if (list != start)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* The list is not empty, but we have changed its
|
|
|
|
|
* start, so we must place the new head in the map.
|
|
|
|
|
*/
|
|
|
|
|
node->value.ext = list;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* In order to hide pointers from garbage collection, we OR in an
|
|
|
|
|
* extra bit. This should be ok for the objects we deal with
|
|
|
|
|
* which are all aligned on 4 or 8 byte boundaries on all the machines
|
|
|
|
|
* I know of.
|
1999-06-24 19:30:29 +00:00
|
|
|
|
*
|
|
|
|
|
* We also use this trick to differentiate between map table keys that
|
|
|
|
|
* should be treated as objects (notification names) and thise that
|
|
|
|
|
* should be treated as pointers (notification objects)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
#define CHEATGC(X) (id)(((gsaddr)X) | 1)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
|
|
|
|
|
1996-02-13 16:09:50 +00:00
|
|
|
|
|
|
|
|
|
@implementation NSNotificationCenter
|
|
|
|
|
|
1999-06-17 13:17:28 +00:00
|
|
|
|
/* The default instance, most often the only one created.
|
|
|
|
|
It is accessed by the class methods at the end of this file.
|
|
|
|
|
There is no need to mutex locking of this variable. */
|
|
|
|
|
|
|
|
|
|
static NSNotificationCenter *default_center = nil;
|
1996-02-13 16:09:50 +00:00
|
|
|
|
|
1996-03-03 01:41:20 +00:00
|
|
|
|
+ (void) initialize
|
1996-02-13 16:09:50 +00:00
|
|
|
|
{
|
1996-03-03 01:41:20 +00:00
|
|
|
|
if (self == [NSNotificationCenter class])
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
cHash = (unsigned (*)(id, SEL))
|
|
|
|
|
[NSGCString instanceMethodForSelector: hSel];
|
|
|
|
|
uHash = (unsigned (*)(id, SEL))
|
|
|
|
|
[NSGString instanceMethodForSelector: hSel];
|
|
|
|
|
cEqual = (BOOL (*)(id, SEL, id))
|
|
|
|
|
[NSGCString instanceMethodForSelector: eqSel];
|
|
|
|
|
uEqual = (BOOL (*)(id, SEL, id))
|
|
|
|
|
[NSGString instanceMethodForSelector: eqSel];
|
|
|
|
|
|
1999-06-17 13:17:28 +00:00
|
|
|
|
/*
|
|
|
|
|
* Do alloc and init separately so the default center can refer to
|
|
|
|
|
* the 'default_center' variable during initialisation.
|
|
|
|
|
*/
|
|
|
|
|
default_center = [self alloc];
|
|
|
|
|
[default_center init];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (NSNotificationCenter*) defaultCenter
|
|
|
|
|
{
|
|
|
|
|
return default_center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Initializing. */
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
- (void) _becomeThreaded: (NSNotification*)notification
|
|
|
|
|
{
|
|
|
|
|
unsigned count;
|
|
|
|
|
|
|
|
|
|
TABLE->_lock = [NSRecursiveLock new];
|
|
|
|
|
TABLE->lImp = [TABLE->_lock methodForSelector: @selector(lock)];
|
|
|
|
|
TABLE->uImp = [TABLE->_lock methodForSelector: @selector(unlock)];
|
|
|
|
|
count = LOCKCOUNT;
|
|
|
|
|
/*
|
|
|
|
|
* If we start locking inside a method that would normally have been
|
|
|
|
|
* locked, we must lock the lock enough times so that when we leave
|
|
|
|
|
* the method the number of unlocks will match.
|
|
|
|
|
*/
|
|
|
|
|
while (count-- > 0)
|
|
|
|
|
{
|
|
|
|
|
(*TABLE->lImp)(TABLE->_lock, @selector(lock));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-06-17 13:17:28 +00:00
|
|
|
|
- (id) init
|
|
|
|
|
{
|
|
|
|
|
[super init];
|
1999-06-24 19:30:29 +00:00
|
|
|
|
TABLE = newNCTable();
|
|
|
|
|
if ([NSThread isMultiThreaded])
|
|
|
|
|
{
|
|
|
|
|
[self _becomeThreaded: nil];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
|
|
|
addObserver: self
|
|
|
|
|
selector: @selector(_becomeThreaded:)
|
|
|
|
|
name: NSWillBecomeMultiThreadedNotification
|
|
|
|
|
object: nil];
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
|
{
|
|
|
|
|
[self gcFinalize];
|
|
|
|
|
|
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) gcFinalize
|
|
|
|
|
{
|
|
|
|
|
/*
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* Release all memory used to store Observations etc.
|
1999-06-17 13:17:28 +00:00
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
endNCTable(TABLE);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Adding new observers. */
|
|
|
|
|
|
|
|
|
|
- (void) addObserver: (id)observer
|
|
|
|
|
selector: (SEL)selector
|
|
|
|
|
name: (NSString*)name
|
|
|
|
|
object: (id)object
|
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
IMP method;
|
|
|
|
|
Observation *list;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
Observation *o;
|
1999-06-24 19:30:29 +00:00
|
|
|
|
GSIMapTable m;
|
|
|
|
|
GSIMapNode n;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
|
|
|
|
if (observer == nil)
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Nil observer passed to addObserver ..."];
|
|
|
|
|
|
|
|
|
|
if (selector == 0)
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Null selector passed to addObserver ..."];
|
|
|
|
|
|
|
|
|
|
#if defined(DEBUG)
|
|
|
|
|
if ([observer respondsToSelector: selector] == NO)
|
|
|
|
|
NSLog(@"Observer '%@' does not respond to selector '%@'", observer,
|
|
|
|
|
NSStringFromSelector(selector));
|
|
|
|
|
#endif
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
method = [observer methodForSelector: selector];
|
|
|
|
|
if (method == 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Observer can not handle specified selector"];
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
lockNCTable(TABLE);
|
|
|
|
|
|
|
|
|
|
if (TABLE->immutableInPost == YES && LOCKCOUNT > 1)
|
|
|
|
|
{
|
|
|
|
|
unlockNCTable(TABLE);
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Attempt to add to immutable center."];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
o = obsNew(TABLE);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
o->selector = selector;
|
1999-06-24 19:30:29 +00:00
|
|
|
|
o->method = method;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
o->observer = observer;
|
|
|
|
|
o->retained = 0;
|
|
|
|
|
o->next = 0;
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (object != nil)
|
|
|
|
|
object = CHEATGC(object);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
/*
|
|
|
|
|
* Record the Observation in one of the linked lists.
|
|
|
|
|
*
|
|
|
|
|
* NB. It is possible to register an observr for a notification more than
|
|
|
|
|
* once - in which case, the observer will receive multiple messages when
|
|
|
|
|
* the notification is posted... odd, but the MacOS-X docs specify this.
|
|
|
|
|
*/
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
|
|
|
|
if (name)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Locate the map table for this name - create it if not present.
|
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
n = GSIMapNodeForKey(NAMED, (GSIMapKey)name);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
if (n == 0)
|
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
Class c;
|
|
|
|
|
|
|
|
|
|
m = mapNew(TABLE);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
/*
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* As this is the first observation for the given name, we take a
|
1999-06-17 13:17:28 +00:00
|
|
|
|
* copy of the name so it cannot be mutated while in the map.
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* Also ensure the copy is one of our well-known string types so
|
|
|
|
|
* we can optimise it's hash and isEqualToString:.
|
1999-06-17 13:17:28 +00:00
|
|
|
|
*/
|
|
|
|
|
name = [name copyWithZone: NSDefaultMallocZone()];
|
1999-06-24 19:30:29 +00:00
|
|
|
|
c = fastClassOfInstance(name);
|
|
|
|
|
if (c != _fastCls._NSGString && c != _fastCls._NSGCString
|
|
|
|
|
&& c != _fastCls._NXConstantString)
|
|
|
|
|
{
|
|
|
|
|
id n = [[NSGString alloc] initWithString: name];
|
|
|
|
|
RELEASE(name);
|
|
|
|
|
name = n;
|
|
|
|
|
}
|
|
|
|
|
GSIMapAddPair(NAMED, (GSIMapKey)name, (GSIMapVal)(void*)m);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
m = (GSIMapTable)n->value.ptr;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Add the observation to the list for the correct object.
|
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
n = GSIMapNodeForSimpleKey(m, (GSIMapKey)object);
|
|
|
|
|
if (n == 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
|
|
|
|
o->next = ENDOBS;
|
1999-06-24 19:30:29 +00:00
|
|
|
|
GSIMapAddPair(m, (GSIMapKey)object, (GSIMapVal)o);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
list = (Observation*)n->value.ptr;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
o->next = list->next;
|
|
|
|
|
list->next = o;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (object)
|
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
n = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)object);
|
|
|
|
|
if (n == 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
|
|
|
|
o->next = ENDOBS;
|
1999-06-24 19:30:29 +00:00
|
|
|
|
GSIMapAddPair(NAMELESS, (GSIMapKey)object, (GSIMapVal)o);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
list = (Observation*)n->value.ptr;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
o->next = list->next;
|
|
|
|
|
list->next = o;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
o->next = WILDCARD;
|
|
|
|
|
WILDCARD = o;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
unlockNCTable(TABLE);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) removeObserver: (id)observer
|
1999-06-24 19:30:29 +00:00
|
|
|
|
name: (NSString*)name
|
|
|
|
|
object: (id)object
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (name == nil && object == nil && observer == nil)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1999-06-24 19:30:29 +00:00
|
|
|
|
format: @"Attempt to remove nil observer/name/object"];
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
/*
|
|
|
|
|
* NB. The removal algorithm depends on an implementation characteristic
|
|
|
|
|
* of our map tables - while enumerating a table, it is safe to remove
|
|
|
|
|
* the entry returned by the enumerator.
|
|
|
|
|
*/
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
lockNCTable(TABLE);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (TABLE->immutableInPost == YES && LOCKCOUNT > 1)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
unlockNCTable(TABLE);
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Attempt to remove from immutable center."];
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (object != nil)
|
|
|
|
|
object = CHEATGC(object);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
|
|
|
|
if (name == nil && object == nil)
|
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
WILDCARD = listPurge(WILDCARD, observer);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (name == nil)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-21 08:30:26 +00:00
|
|
|
|
GSIMapNode n;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
|
|
|
|
/*
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* First try removing all named items set for this object.
|
1999-06-17 13:17:28 +00:00
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
n = NAMED->firstNode;
|
|
|
|
|
while (n != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
GSIMapTable m = (GSIMapTable)n->value.ptr;
|
|
|
|
|
NSString *thisName = (NSString*)n->key.obj;
|
|
|
|
|
GSIMapNode n1;
|
|
|
|
|
|
|
|
|
|
n = n->nextInMap;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
if (object == nil)
|
|
|
|
|
{
|
|
|
|
|
/*
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* Nil object and nil name, so we step through all the maps
|
|
|
|
|
* keyed under the current name and remove all the objects
|
|
|
|
|
* that match the observer.
|
1999-06-17 13:17:28 +00:00
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
n1 = m->firstNode;
|
|
|
|
|
while (n1 != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
GSIMapNode next = n1->nextInMap;
|
|
|
|
|
|
|
|
|
|
purgeMapNode(m, n1, observer);
|
|
|
|
|
n1 = next;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/*
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* Nil name, but non-nil object - we locate the map for the
|
|
|
|
|
* specified object, and remove all the items that match
|
|
|
|
|
* the observer.
|
1999-06-17 13:17:28 +00:00
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
n1 = GSIMapNodeForSimpleKey(m, (GSIMapKey)object);
|
|
|
|
|
if (n1 != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
purgeMapNode(m, n1, observer);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
/*
|
|
|
|
|
* If we removed all the observations keyed under this name, we
|
|
|
|
|
* must remove the map table too.
|
|
|
|
|
*/
|
|
|
|
|
if (m->nodeCount == 0)
|
|
|
|
|
{
|
|
|
|
|
mapFree(TABLE, m);
|
|
|
|
|
GSIMapRemoveKey(NAMED, (GSIMapKey)thisName);
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* Now remove unnamed items
|
1999-06-17 13:17:28 +00:00
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (object == nil)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
n = NAMELESS->firstNode;
|
|
|
|
|
while (n != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
GSIMapNode next = n->nextInMap;
|
|
|
|
|
|
|
|
|
|
purgeMapNode(NAMELESS, n, observer);
|
|
|
|
|
n = next;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
else
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
n = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)object);
|
|
|
|
|
if (n != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
purgeMapNode(NAMELESS, n, observer);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
else
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
GSIMapTable m;
|
|
|
|
|
GSIMapNode n;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Locate the map table for this name.
|
|
|
|
|
*/
|
|
|
|
|
n = GSIMapNodeForKey(NAMED, (GSIMapKey)name);
|
|
|
|
|
if (n == 0)
|
|
|
|
|
{
|
|
|
|
|
unlockNCTable(TABLE);
|
|
|
|
|
return; /* Nothing to do. */
|
|
|
|
|
}
|
|
|
|
|
m = (GSIMapTable)n->value.ptr;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (object == nil)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
n = m->firstNode;
|
|
|
|
|
while (n != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
GSIMapNode next = n->nextInMap;
|
|
|
|
|
|
|
|
|
|
purgeMapNode(m, n, observer);
|
|
|
|
|
n = next;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
n = GSIMapNodeForSimpleKey(m, (GSIMapKey)object);
|
|
|
|
|
if (n != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
purgeMapNode(m, n, observer);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (m->nodeCount == 0)
|
|
|
|
|
{
|
|
|
|
|
mapFree(TABLE, m);
|
|
|
|
|
GSIMapRemoveKey(NAMED, (GSIMapKey)name);
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
unlockNCTable(TABLE);
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
/* Remove all records pertaining to OBSERVER. For instance, this
|
|
|
|
|
should be called before the OBSERVER is -dealloc'ed. */
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
- (void) removeObserver: (id)observer
|
|
|
|
|
{
|
|
|
|
|
if (observer == nil)
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Nil observer passed to removeObserver:"];
|
|
|
|
|
|
|
|
|
|
[self removeObserver: observer name: nil object: nil];
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Post NOTIFICATION to all the observers that match its NAME and OBJECT.
|
|
|
|
|
*
|
|
|
|
|
* For performance reasons, we don't wrap an exception handler round every
|
|
|
|
|
* message sent to an observer. This means that, if one observer raises
|
|
|
|
|
* an exception, later observers in the lists will not get the notification.
|
|
|
|
|
*/
|
|
|
|
|
- (void) postNotification: (NSNotification*)notification
|
|
|
|
|
{
|
|
|
|
|
NSString *n_name;
|
|
|
|
|
id n_object;
|
|
|
|
|
Observation *o;
|
|
|
|
|
unsigned count;
|
1999-06-24 19:30:29 +00:00
|
|
|
|
volatile GSIArray a;
|
|
|
|
|
unsigned arrayBase;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
|
|
|
|
if (notification == nil)
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Tried to post a nil notification."];
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
n_name = ((NotificationStruct*)notification)->_name;
|
|
|
|
|
n_object = ((NotificationStruct*)notification)->_object;
|
|
|
|
|
if (n_object != nil)
|
|
|
|
|
n_object = CHEATGC(n_object);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
|
|
|
|
if (n_name == nil)
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Tried to post a notification with no name."];
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
lockNCTable(TABLE);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
a = ARRAY;
|
|
|
|
|
/*
|
|
|
|
|
* If this is a recursive posting of a notification, the array will already
|
|
|
|
|
* be in use, so we restrict our operation to array indices beyond the end
|
2000-02-19 00:40:47 +00:00
|
|
|
|
* of those used by the posting that caused this one.
|
1999-06-24 19:30:29 +00:00
|
|
|
|
*/
|
|
|
|
|
arrayBase = GSIArrayCount(a);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
#if 0
|
1999-06-17 13:17:28 +00:00
|
|
|
|
NS_DURING
|
1999-06-24 19:30:29 +00:00
|
|
|
|
#endif
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
GSIMapNode n;
|
|
|
|
|
GSIMapTable m;
|
|
|
|
|
|
1999-06-17 13:17:28 +00:00
|
|
|
|
/*
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* If the notification center guarantees that it will be immutable
|
|
|
|
|
* while a notification is being posted, we can simply send the
|
|
|
|
|
* message to each matching Observation. Otherwise, we put the
|
|
|
|
|
* Observations in a temporary array before starting sending the
|
|
|
|
|
* messages, so any changes to the tables don't mess us up.
|
1999-06-17 13:17:28 +00:00
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (TABLE->immutableInPost)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
/*
|
|
|
|
|
* Post the notification to all the observers that specified neither
|
|
|
|
|
* NAME nor OBJECT.
|
|
|
|
|
*/
|
|
|
|
|
for (o = WILDCARD; o != ENDOBS; o = o->next)
|
|
|
|
|
{
|
|
|
|
|
(*o->method)(o->observer, o->selector, notification);
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
/*
|
|
|
|
|
* Post the notification to all the observers that specified OBJECT,
|
|
|
|
|
* but didn't specify NAME.
|
|
|
|
|
*/
|
|
|
|
|
if (n_object)
|
|
|
|
|
{
|
|
|
|
|
n = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)n_object);
|
|
|
|
|
if (n != 0)
|
|
|
|
|
{
|
|
|
|
|
o = n->value.ext;
|
|
|
|
|
while (o != ENDOBS)
|
|
|
|
|
{
|
|
|
|
|
(*o->method)(o->observer, o->selector, notification);
|
|
|
|
|
o = o->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Post the notification to all the observers of NAME, except those
|
|
|
|
|
* observers with a non-nil OBJECT that doesn't match the
|
|
|
|
|
* notification's OBJECT).
|
|
|
|
|
*/
|
|
|
|
|
if (n_name)
|
|
|
|
|
{
|
|
|
|
|
n = GSIMapNodeForKey(NAMED, (GSIMapKey)n_name);
|
|
|
|
|
if (n)
|
|
|
|
|
m = (GSIMapTable)n->value.ptr;
|
|
|
|
|
else
|
|
|
|
|
m = 0;
|
|
|
|
|
if (m != 0)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* First, observers with a matching object.
|
|
|
|
|
*/
|
|
|
|
|
n = GSIMapNodeForSimpleKey(m, (GSIMapKey)n_object);
|
|
|
|
|
if (n != 0)
|
|
|
|
|
{
|
|
|
|
|
o = n->value.ext;
|
|
|
|
|
while (o != ENDOBS)
|
|
|
|
|
{
|
|
|
|
|
(*o->method)(o->observer, o->selector,
|
|
|
|
|
notification);
|
|
|
|
|
o = o->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (n_object != nil)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Now observers with a nil object.
|
|
|
|
|
*/
|
|
|
|
|
n = GSIMapNodeForSimpleKey(m, (GSIMapKey)nil);
|
|
|
|
|
if (n != 0)
|
|
|
|
|
{
|
|
|
|
|
o = n->value.ext;
|
|
|
|
|
while (o != ENDOBS)
|
|
|
|
|
{
|
|
|
|
|
(*o->method)(o->observer, o->selector,
|
|
|
|
|
notification);
|
|
|
|
|
o = o->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
/*
|
|
|
|
|
* Post the notification to all the observers that specified neither
|
|
|
|
|
* NAME nor OBJECT.
|
|
|
|
|
*/
|
1999-09-13 04:11:39 +00:00
|
|
|
|
for (o = WILDCARD; o != ENDOBS; o = o->next)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-09-13 04:11:39 +00:00
|
|
|
|
GSIArrayAddItem(a, (GSIArrayItem)o);
|
|
|
|
|
}
|
|
|
|
|
count = GSIArrayCount(a);
|
|
|
|
|
while (count-- > arrayBase)
|
|
|
|
|
{
|
|
|
|
|
o = GSIArrayItemAtIndex(a, count).ext;
|
|
|
|
|
if (o->next != 0)
|
|
|
|
|
(*o->method)(o->observer, o->selector, notification);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
1999-09-13 04:11:39 +00:00
|
|
|
|
GSIArrayRemoveItemsFromIndex(a, arrayBase);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
/*
|
|
|
|
|
* Post the notification to all the observers that specified OBJECT,
|
|
|
|
|
* but didn't specify NAME.
|
|
|
|
|
*/
|
|
|
|
|
if (n_object)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
n = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)n_object);
|
|
|
|
|
if (n != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
o = n->value.ext;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
while (o != ENDOBS)
|
|
|
|
|
{
|
1999-06-21 08:30:26 +00:00
|
|
|
|
GSIArrayAddItem(a, (GSIArrayItem)o);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
o = o->next;
|
|
|
|
|
}
|
1999-06-21 08:30:26 +00:00
|
|
|
|
count = GSIArrayCount(a);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
while (count-- > arrayBase)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-21 08:30:26 +00:00
|
|
|
|
o = GSIArrayItemAtIndex(a, count).ext;
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (o->next != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
(*o->method)(o->observer, o->selector, notification);
|
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
GSIArrayRemoveItemsFromIndex(a, arrayBase);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
/*
|
|
|
|
|
* Post the notification to all the observers of NAME, except those
|
2000-02-19 00:40:47 +00:00
|
|
|
|
* observers with a non-nil OBJECT that doesn't match the
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* notification's OBJECT).
|
|
|
|
|
*/
|
|
|
|
|
if (n_name)
|
|
|
|
|
{
|
|
|
|
|
n = GSIMapNodeForKey(NAMED, (GSIMapKey)n_name);
|
|
|
|
|
if (n)
|
|
|
|
|
m = (GSIMapTable)n->value.ptr;
|
|
|
|
|
else
|
|
|
|
|
m = 0;
|
|
|
|
|
if (m != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
|
|
|
|
/*
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* First, observers with a matching object.
|
1999-06-17 13:17:28 +00:00
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
n = GSIMapNodeForSimpleKey(m, (GSIMapKey)n_object);
|
|
|
|
|
if (n != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
o = n->value.ext;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
while (o != ENDOBS)
|
|
|
|
|
{
|
1999-06-21 08:30:26 +00:00
|
|
|
|
GSIArrayAddItem(a, (GSIArrayItem)o);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
o = o->next;
|
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (n_object != nil)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Now observers with a nil object.
|
|
|
|
|
*/
|
|
|
|
|
n = GSIMapNodeForSimpleKey(m, (GSIMapKey)nil);
|
|
|
|
|
if (n != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
o = n->value.ext;
|
|
|
|
|
while (o != ENDOBS)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayAddItem(a, (GSIArrayItem)o);
|
|
|
|
|
o = o->next;
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
|
|
|
|
|
count = GSIArrayCount(a);
|
|
|
|
|
while (count-- > arrayBase)
|
|
|
|
|
{
|
|
|
|
|
o = GSIArrayItemAtIndex(a, count).ext;
|
|
|
|
|
if (o->next != 0)
|
|
|
|
|
(*o->method)(o->observer, o->selector,
|
|
|
|
|
notification);
|
|
|
|
|
}
|
|
|
|
|
GSIArrayRemoveItemsFromIndex(a, arrayBase);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
#if 0
|
1999-06-17 13:17:28 +00:00
|
|
|
|
NS_HANDLER
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* If we had a problem - release memory and unlock before going on.
|
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
GSIArrayRemoveItemsFromIndex(ARRAY, arrayBase);
|
|
|
|
|
unlockNCTable(TABLE);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
|
|
|
|
[localException raise];
|
|
|
|
|
}
|
|
|
|
|
NS_ENDHANDLER
|
1999-06-24 19:30:29 +00:00
|
|
|
|
#endif
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
unlockNCTable(TABLE);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) postNotificationName: (NSString*)name
|
|
|
|
|
object: (id)object
|
|
|
|
|
{
|
|
|
|
|
[self postNotification: [NSNotification notificationWithName: name
|
|
|
|
|
object: object]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) postNotificationName: (NSString*)name
|
|
|
|
|
object: (id)object
|
|
|
|
|
userInfo: (NSDictionary*)info
|
|
|
|
|
{
|
|
|
|
|
[self postNotification: [NSNotification notificationWithName: name
|
|
|
|
|
object: object
|
|
|
|
|
userInfo: info]];
|
1996-02-13 16:09:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
@implementation NSNotificationCenter (GNUstep)
|
|
|
|
|
|
|
|
|
|
- (BOOL) setImmutableInPost: (BOOL)flag
|
|
|
|
|
{
|
|
|
|
|
BOOL old;
|
|
|
|
|
|
|
|
|
|
lockNCTable(TABLE);
|
|
|
|
|
|
|
|
|
|
if (self == default_center)
|
|
|
|
|
{
|
|
|
|
|
unlockNCTable(TABLE);
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Can't change behavior of default center."];
|
|
|
|
|
}
|
|
|
|
|
if (LOCKCOUNT > 1)
|
|
|
|
|
{
|
|
|
|
|
unlockNCTable(TABLE);
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Can't change behavior during post."];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
old = TABLE->immutableInPost;
|
|
|
|
|
TABLE->immutableInPost = flag;
|
|
|
|
|
unlockNCTable(TABLE);
|
|
|
|
|
|
|
|
|
|
return old;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL) setLockingDisabled: (BOOL)flag
|
|
|
|
|
{
|
|
|
|
|
BOOL old;
|
|
|
|
|
|
|
|
|
|
lockNCTable(TABLE);
|
|
|
|
|
if (self == default_center)
|
|
|
|
|
{
|
|
|
|
|
unlockNCTable(TABLE);
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Can't change locking of default center."];
|
|
|
|
|
}
|
|
|
|
|
if (LOCKCOUNT > 1)
|
|
|
|
|
{
|
|
|
|
|
unlockNCTable(TABLE);
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Can't change locking during post."];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
old = TABLE->lockingDisabled;
|
|
|
|
|
TABLE->lockingDisabled = flag;
|
|
|
|
|
unlockNCTable(TABLE);
|
|
|
|
|
return old;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|