Fix for possible deadlock in notification system

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@18988 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-Macdonald 2004-03-31 13:23:38 +00:00
parent 93863e6087
commit bedec3d02a
3 changed files with 129 additions and 221 deletions

View file

@ -5,6 +5,11 @@
* Source/NSString.m: ([-hash]) include full string length in the hash.
* Source/GSString.m: ditto
Disambiguates long strings with a common prefix.
* Headers/Additions/GNUstepBase/GSIArray.h: add support for an array
with statically allocated memory, which grows by using the heap.
* Source/NSNotificationCenter.m: Rewrite posting so that we don't hold
a lock on the center while actually posting ... should prevent
possible deadlock in multithreaded programs.
2004-03-30 Willem Rein Oudshoorn <Wim.Oudshoorn@agilisys.com>

View file

@ -160,9 +160,27 @@ GSIArrayGrow(GSIArray array)
unsigned int size;
GSIArrayItem *tmp;
next = array->cap + array->old;
size = next*sizeof(GSIArrayItem);
tmp = NSZoneRealloc(array->zone, array->ptr, size);
if (array->old == 0)
{
/*
* Statically initialised buffer ... copy into new heap buffer.
*/
array->old = array->cap / 2;
if (array->old < 1)
{
array->old = 1;
}
next = array->cap + array->old;
size = next*sizeof(GSIArrayItem);
tmp = NSZoneMalloc(array->zone, size);
memcpy(tmp, array->ptr, array->count * sizeof(GSIArrayItem));
}
else
{
next = array->cap + array->old;
size = next*sizeof(GSIArrayItem);
tmp = NSZoneRealloc(array->zone, array->ptr, size);
}
if (tmp == 0)
{
@ -437,7 +455,13 @@ GSIArrayClear(GSIArray array)
{
if (array->ptr)
{
NSZoneFree(array->zone, (void*)array->ptr);
/*
* Only free memory if it was dynamically initialised (old > 0)
*/
if (array->old > 0)
{
NSZoneFree(array->zone, (void*)array->ptr);
}
array->ptr = 0;
array->cap = 0;
}
@ -493,6 +517,18 @@ GSIArrayInitWithZoneAndCapacity(GSIArray array, NSZone *zone, size_t capacity)
return array;
}
static INLINE GSIArray
GSIArrayInitWithZoneAndStaticCapacity(GSIArray array, NSZone *zone,
size_t capacity, GSIArrayItem *buffer)
{
array->zone = zone;
array->count = 0;
array->cap = capacity;
array->old = 0;
array->ptr = buffer;
return array;
}
static INLINE GSIArray
GSIArrayCopyWithZone(GSIArray array, NSZone *zone)
{

View file

@ -1,5 +1,4 @@
/** Implementation of NSNotificationCenter for GNUstep
notification = (id)NSAllocateObject(concrete, 0, NSDefaultMallocZone());
Copyright (C) 1999 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
@ -238,11 +237,9 @@ 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. */
GSLazyRecursiveLock *_lock; /* Lock out other threads. */
BOOL lockingDisabled;
BOOL immutableInPost;
Observation *freeList;
Observation **chunks;
@ -256,7 +253,6 @@ typedef struct NCTbl {
#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)
@ -328,12 +324,6 @@ static void endNCTable(NCTable *t)
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.
*/
@ -408,9 +398,6 @@ static NCTable *newNCTable(void)
t->named = NSZoneMalloc(NSDefaultMallocZone(), sizeof(GSIMapTable_t));
GSIMapInitWithZoneAndCapacity(t->named, NSDefaultMallocZone(), 128);
t->array = NSZoneMalloc(NSDefaultMallocZone(), sizeof(GSIArray_t));
GSIArrayInitWithZoneAndCapacity(t->array, NSDefaultMallocZone(), 16);
// t->_lock = [GSLazyRecursiveLock new];
t->_lock = [NSRecursiveLock new];
return t;
@ -644,13 +631,6 @@ static NSNotificationCenter *default_center = nil;
lockNCTable(TABLE);
if (TABLE->immutableInPost == YES && LOCKCOUNT > 1)
{
unlockNCTable(TABLE);
[NSException raise: NSInvalidArgumentException
format: @"Attempt to add to immutable center."];
}
o = obsNew(TABLE);
o->selector = selector;
o->method = method;
@ -745,13 +725,6 @@ static NSNotificationCenter *default_center = nil;
lockNCTable(TABLE);
if (TABLE->immutableInPost == YES && LOCKCOUNT > 1)
{
unlockNCTable(TABLE);
[NSException raise: NSInvalidArgumentException
format: @"Attempt to remove from immutable center."];
}
if (object != nil)
{
object = CHEATGC(object);
@ -914,10 +887,13 @@ static NSNotificationCenter *default_center = nil;
{
Observation *o;
unsigned count;
volatile GSIArray a;
unsigned arrayBase;
NSString *name = [notification name];
id object;
GSIMapNode n;
GSIMapTable m;
GSIArrayItem i[64];
GSIArray_t b;
GSIArray a = &b;
if (name == nil)
{
@ -930,138 +906,79 @@ static NSNotificationCenter *default_center = nil;
{
object = CHEATGC(object);
}
/*
* Initialise static array to store copies of observers.
*/
GSIArrayInitWithZoneAndStaticCapacity(a, NSDefaultMallocZone(), 64, i);
/*
* Lock the table of observers while we traverse it.
*/
lockNCTable(TABLE);
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
* of those used by the posting that caused this one.
* Find all the observers that specified neither NAME nor OBJECT.
*/
arrayBase = GSIArrayCount(a);
#if 0
NS_DURING
#endif
for (o = WILDCARD; o != ENDOBS; o = o->next)
{
GSIMapNode n;
GSIMapTable m;
GSIArrayAddItem(a, (GSIArrayItem)o);
}
/*
* 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.
*/
if (TABLE->immutableInPost)
/*
* Find the observers that specified OBJECT, but didn't specify NAME.
*/
if (object)
{
n = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)object);
if (n != 0)
{
/*
* Post the notification to all the observers that specified neither
* NAME nor OBJECT.
*/
for (o = WILDCARD; o != ENDOBS; o = o->next)
o = n->value.ext;
while (o != ENDOBS)
{
(*o->method)(o->observer, o->selector, notification);
GSIArrayAddItem(a, (GSIArrayItem)o);
o = o->next;
}
}
}
/*
* Post the notification to all the observers that specified OBJECT,
* but didn't specify NAME.
*/
if (object)
{
n = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)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 (name)
{
n = GSIMapNodeForKey(NAMED, (GSIMapKey)name);
if (n)
{
m = (GSIMapTable)n->value.ptr;
}
else
{
m = 0;
}
if (m != 0)
{
/*
* First, observers with a matching object.
*/
n = GSIMapNodeForSimpleKey(m, (GSIMapKey)object);
if (n != 0)
{
o = n->value.ext;
while (o != ENDOBS)
{
(*o->method)(o->observer, o->selector,
notification);
o = o->next;
}
}
if (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;
}
}
}
}
}
/*
* Find the observers of NAME, except those observers with a non-nil OBJECT
* that doesn't match the notification's OBJECT).
*/
if (name)
{
n = GSIMapNodeForKey(NAMED, (GSIMapKey)name);
if (n)
{
m = (GSIMapTable)n->value.ptr;
}
else
{
m = 0;
}
if (m != 0)
{
/*
* Post the notification to all the observers that specified neither
* NAME nor OBJECT.
* First, observers with a matching object.
*/
for (o = WILDCARD; o != ENDOBS; o = o->next)
n = GSIMapNodeForSimpleKey(m, (GSIMapKey)object);
if (n != 0)
{
GSIArrayAddItem(a, (GSIArrayItem)o);
o = n->value.ext;
while (o != ENDOBS)
{
GSIArrayAddItem(a, (GSIArrayItem)o);
o = o->next;
}
}
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);
/*
* Post the notification to all the observers that specified OBJECT,
* but didn't specify NAME.
*/
if (object)
if (object != nil)
{
n = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)object);
/*
* Now observers with a nil object.
*/
n = GSIMapNodeForSimpleKey(m, (GSIMapKey)nil);
if (n != 0)
{
o = n->value.ext;
@ -1070,95 +987,47 @@ static NSNotificationCenter *default_center = nil;
GSIArrayAddItem(a, (GSIArrayItem)o);
o = o->next;
}
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);
}
}
/*
* 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 (name)
{
n = GSIMapNodeForKey(NAMED, (GSIMapKey)name);
if (n)
{
m = (GSIMapTable)n->value.ptr;
}
else
{
m = 0;
}
if (m != 0)
{
/*
* First, observers with a matching object.
*/
n = GSIMapNodeForSimpleKey(m, (GSIMapKey)object);
if (n != 0)
{
o = n->value.ext;
while (o != ENDOBS)
{
GSIArrayAddItem(a, (GSIArrayItem)o);
o = o->next;
}
}
if (object != nil)
{
/*
* Now observers with a nil object.
*/
n = GSIMapNodeForSimpleKey(m, (GSIMapKey)nil);
if (n != 0)
{
o = n->value.ext;
while (o != ENDOBS)
{
GSIArrayAddItem(a, (GSIArrayItem)o);
o = o->next;
}
}
}
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);
}
}
}
}
/*
* Finished with the table ... we can unlock it.
*/
unlockNCTable(TABLE);
#if 0
NS_DURING
#endif
/*
* Now send all the notifications.
*/
count = GSIArrayCount(a);
while (count-- > 0)
{
o = GSIArrayItemAtIndex(a, count).ext;
if (o->next != 0)
{
(*o->method)(o->observer, o->selector, notification);
}
}
GSIArrayEmpty(a);
#if 0
NS_HANDLER
{
/*
* If we had a problem - release memory and unlock before going on.
* If we had a problem - release memory before going on.
*/
GSIArrayRemoveItemsFromIndex(ARRAY, arrayBase);
unlockNCTable(TABLE);
GSIArrayEmpty(a);
RELEASE(notification);
[localException raise];
}
NS_ENDHANDLER
#endif
unlockNCTable(TABLE);
RELEASE(notification);
}
@ -1231,8 +1100,6 @@ static NSNotificationCenter *default_center = nil;
format: @"Can't change behavior during post."];
}
old = TABLE->immutableInPost;
TABLE->immutableInPost = flag;
unlockNCTable(TABLE);
return old;