New stuff

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@15286 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-Macdonald 2002-12-12 15:14:13 +00:00
parent 44bb297525
commit 5bbbb1b94c
8 changed files with 318 additions and 82 deletions

View file

@ -1,3 +1,15 @@
2002-12-12 Richard Frith-Macdonald <rfm@gnu.org>
* Headers/gnustep/base/NSDistributedNotificationCenter.h: add type
for LAN-wide notifications. Add ivar to support this.
* Source/NSDistributedNotificationCenter.m: Implement support for
a LAN-wide notification center. Fully document class.
* Tools/gdnc.m: Add new GSNetwork flag to operate as LAN-wide
notification center.
* Tools/AGSHtml.m: Don't output contents section if there is
nothing to show (ie must have at least two sections to index).
* Source/NSConnection.m: Add locks to protect proxy cache in timeout.
2002-12-10 Adam Fedor <fedor@gnu.org> 2002-12-10 Adam Fedor <fedor@gnu.org>
* configure.ac: Enable libffi on darwin by default. * configure.ac: Enable libffi on darwin by default.

View file

@ -39,11 +39,15 @@ typedef enum {
} NSNotificationSuspensionBehavior; } NSNotificationSuspensionBehavior;
GS_EXPORT NSString *NSLocalNotificationCenterType; GS_EXPORT NSString *NSLocalNotificationCenterType;
#ifndef NO_GNUSTEP
GS_EXPORT NSString *GSNetworkNotificationCenterType;
#endif
@interface NSDistributedNotificationCenter : NSNotificationCenter @interface NSDistributedNotificationCenter : NSNotificationCenter
{ {
NSRecursiveLock *_centerLock; /* For thread safety. */ NSRecursiveLock *_centerLock; /* For thread safety. */
id _remote; /* Proxy for center. */ NSString *_type; /* Type of notification center. */
id _remote; /* Proxy for center. */
BOOL _suspended; /* Is delivery suspended? */ BOOL _suspended; /* Is delivery suspended? */
} }
+ (NSNotificationCenter*) defaultCenter; + (NSNotificationCenter*) defaultCenter;

View file

@ -662,6 +662,7 @@ static BOOL multi_threaded = NO;
NSArray *cached_locals; NSArray *cached_locals;
int i; int i;
M_LOCK(global_proxies_gate);
cached_locals = NSAllMapTableValues(targetToCached); cached_locals = NSAllMapTableValues(targetToCached);
for (i = [cached_locals count]; i > 0; i--) for (i = [cached_locals count]; i > 0; i--)
{ {
@ -678,6 +679,7 @@ static BOOL multi_threaded = NO;
[t invalidate]; [t invalidate];
timer = nil; timer = nil;
} }
M_UNLOCK(global_proxies_gate);
} }
/** /**
@ -3107,12 +3109,12 @@ static void callEncoder (DOContext *ctxt)
node = GSIMapNodeForKey(_localObjects, (GSIMapKey)anObj); node = GSIMapNodeForKey(_localObjects, (GSIMapKey)anObj);
if (node == 0) if (node == 0)
{ {
prox = nil; M_LOCK(global_proxies_gate);
} M_LOCK(_proxiesGate);
else [NSException raise: NSInternalInconsistencyException
{ format: @"Attempt to remove non-existent local %@", anObj];
prox = node->value.obj;
} }
prox = node->value.obj;
target = ((ProxyStruct*)prox)->_handle; target = ((ProxyStruct*)prox)->_handle;
/* /*

View file

@ -47,6 +47,8 @@
*/ */
NSString *NSLocalNotificationCenterType = NSString *NSLocalNotificationCenterType =
@"NSLocalNotificationCenterType"; @"NSLocalNotificationCenterType";
NSString *GSNetworkNotificationCenterType =
@"GSNetworkNotificationCenterType";
@interface NSDistributedNotificationCenter (Private) @interface NSDistributedNotificationCenter (Private)
@ -59,41 +61,143 @@ NSString *NSLocalNotificationCenterType =
to: (unsigned long)observer; to: (unsigned long)observer;
@end @end
/**
* <p>The NSDistributedNotificationCenter provides a versatile yet
* simple mechanism for objects in different processes to communicate
* effectively while knowing very little about each others internals.<br />
* A distributed notification center acts much like a normal
* notification center, but it handles notifications on a machine-wide
* (or local network wide) basis rather than just notifications within
* a single process. Objects are able to register themselves as
* observers for particular notification names and objects, and they
* will then receive notifications (including optional user information
* consisting of a dictionary of property-list objects) as they are posted.
* </p>
* <p>Since posting of distributed notifications involves inter-process
* (and sometimes inter-host) communication, it is fundamentally slower
* than normal notifications, and should be used relatively sparingly.
* In order to help with this, the NSDistributedNotificationCenter
* provides a notion of 'suspension', whereby a center can be suspended
* causing notifications for observers in the process where the center
* was suspended to cease receiving notifications. Observers can
* specify how notifications are to be handled in this case (queued
* or discarded) and posters can specify that particular notifications
* are to be delivered immediately irrespective of suspension.
* </p>
* <p>Distributed notifications are mediated by a server process which
* handles all notifications for a particular center type. In GNUstep
* this process is the gdnc tool, and when started without special
* options, a gdnc process acts as the local centre for the host it is
* running on. When started with the GSNetwork user default set to YES,
* the gdnc tool acts as a local network wide server (you should only
* run one copy of gdnc like this on your LAN).<br />
* The gdnc process should be started at machine boot time, but GNUstep
* will attempt to start it automatically if it can't find it.
* </p>
* <p>MacOS-X currently defines only a notification center for the
* local host. GNUstep also defines a local network center which can
* be used from multiple hosts. By default the system sends this to
* any gdnc process it can find which is configured as a network-wide
* server, but the GDNCHost user default may be used to specify a
* particular host to be contacted ... this may be of use where you
* wish to have logically separate clusters of machines on a shared LAN.
* </p>
*/
@implementation NSDistributedNotificationCenter @implementation NSDistributedNotificationCenter
static NSDistributedNotificationCenter *defCenter = nil; static NSDistributedNotificationCenter *locCenter = nil;
static NSDistributedNotificationCenter *netCenter = nil;
+ (id) allocWithZone: (NSZone*)z
{
[NSException raise: NSInternalInconsistencyException
format: @"Should not call +alloc for NSDistributedNotificationCenter"];
return nil;
}
/**
* Returns the default notification center ... a shared notification
* center for the local host. This is simply a convenience method
* equivalent to calling +notificationCenterForType: with
* NSLocalNotificationCenterType as its argument.
*/
+ (id) defaultCenter + (id) defaultCenter
{ {
return [self notificationCenterForType: NSLocalNotificationCenterType]; return [self notificationCenterForType: NSLocalNotificationCenterType];
} }
/**
* Returns a notification center of the specified type.<br />
* The NSLocalNotificationCenterType provides a shared access to
* a notificatiuon center used by processes on the local host.<br />
* The GSNetworkNotificationCenterType provides a shared access to
* a notificatiuon center used by processes on the local network.<br />
* MacOS-X supports only NSLocalNotificationCenterType.
*/
+ (id) notificationCenterForType: (NSString*)type + (id) notificationCenterForType: (NSString*)type
{ {
NSAssert([type isEqual: NSLocalNotificationCenterType], if ([type isEqual: NSLocalNotificationCenterType] == YES)
NSInvalidArgumentException);
if (defCenter == nil)
{ {
[gnustep_global_lock lock]; if (locCenter == nil)
if (defCenter == nil) {
{ [gnustep_global_lock lock];
NS_DURING if (locCenter == nil)
{ {
id tmp; NS_DURING
{
NSDistributedNotificationCenter *tmp;
tmp = NSAllocateObject(self, 0, NSDefaultMallocZone()); tmp = (NSDistributedNotificationCenter*)
defCenter = (NSDistributedNotificationCenter*)[tmp init]; NSAllocateObject(self, 0, NSDefaultMallocZone());
tmp->_centerLock = [NSRecursiveLock new];
tmp->_type = RETAIN(NSLocalNotificationCenterType);
locCenter = tmp;
}
NS_HANDLER
{
[gnustep_global_lock unlock];
[localException raise];
}
NS_ENDHANDLER
} }
NS_HANDLER [gnustep_global_lock unlock];
{ }
[gnustep_global_lock unlock]; return locCenter;
[localException raise]; }
} else if ([type isEqual: GSNetworkNotificationCenterType] == YES)
NS_ENDHANDLER {
} if (netCenter == nil)
[gnustep_global_lock unlock]; {
[gnustep_global_lock lock];
if (netCenter == nil)
{
NS_DURING
{
NSDistributedNotificationCenter *tmp;
tmp = (NSDistributedNotificationCenter*)
NSAllocateObject(self, 0, NSDefaultMallocZone());
tmp->_centerLock = [NSRecursiveLock new];
tmp->_type = RETAIN(GSNetworkNotificationCenterType);
netCenter = tmp;
}
NS_HANDLER
{
[gnustep_global_lock unlock];
[localException raise];
}
NS_ENDHANDLER
}
[gnustep_global_lock unlock];
}
return netCenter;
}
else
{
[NSException raise: NSInvalidArgumentException
format: @"Unknown center type (%@)", type];
return nil; /* NOT REACHED */
} }
return defCenter;
} }
- (void) dealloc - (void) dealloc
@ -103,16 +207,26 @@ static NSDistributedNotificationCenter *defCenter = nil;
[_remote unregisterClient: (id<GDNCClient>)self]; [_remote unregisterClient: (id<GDNCClient>)self];
} }
RELEASE(_remote); RELEASE(_remote);
[super dealloc]; RELEASE(_type);
NSDeallocateObject(self);
} }
/**
* Should not be used.
*/
- (id) init - (id) init
{ {
NSAssert(_centerLock == nil, NSInternalInconsistencyException); RELEASE(self);
_centerLock = [NSRecursiveLock new]; [NSException raise: NSInternalInconsistencyException
return self; format: @"Should not call -init for NSDistributedNotificationCenter"];
return nil;
} }
/**
* Adds an observer to the receiver. Calls
* -addObserver:selector:name:object:suspensionBehavior: with
* NSNotificationSuspensionBehaviorCoalesce.
*/
- (void) addObserver: (id)anObserver - (void) addObserver: (id)anObserver
selector: (SEL)aSelector selector: (SEL)aSelector
name: (NSString*)notificationName name: (NSString*)notificationName
@ -125,6 +239,38 @@ static NSDistributedNotificationCenter *defCenter = nil;
suspensionBehavior: NSNotificationSuspensionBehaviorCoalesce]; suspensionBehavior: NSNotificationSuspensionBehaviorCoalesce];
} }
/**
* Adds an observer to the receiver.<br />
* When a notification matching notificationName and anObject is
* sent to the center, the object anObserver is sent the message
* aSelector with the notification info dictionary as its argument.<br />
* The suspensionBehavior governs how the center deals with notifications
* when the process to which the notification should be delivered is
* suspended:
* <deflist>
* <term>NSNotificationSuspensionBehaviorDrop</term>
* <desc>
* Discards the notification if the observing process is suspended.
* </desc>
* <term>NSNotificationSuspensionBehaviorCoalesce</term>
* <desc>
* Discards previously queued notifications when the observing process
* is suspended, leaving only the last notification posted in the queue.
* Delivers this single notification when the process becomes unsuspended.
* </desc>
* <term>NSNotificationSuspensionBehaviorHold</term>
* <desc>
* Queues notifications when the observing process is suspended,
* delivering all the queued notifications when the process becomes
* unsuspended again.
* </desc>
* <term>NSNotificationSuspensionBehaviorDeliverImmediately</term>
* <desc>
* Deliver the notification immediately, even if the destination
* process is suspended.
* </desc>
* </deflist>
*/
- (void) addObserver: (id)anObserver - (void) addObserver: (id)anObserver
selector: (SEL)aSelector selector: (SEL)aSelector
name: (NSString*)notificationName name: (NSString*)notificationName
@ -141,8 +287,8 @@ static NSDistributedNotificationCenter *defCenter = nil;
[NSException raise: NSInvalidArgumentException [NSException raise: NSInvalidArgumentException
format: @"null selector"]; format: @"null selector"];
} }
if (notificationName != nil && if (notificationName != nil
[notificationName isKindOfClass: [NSString class]] == NO) && [notificationName isKindOfClass: [NSString class]] == NO)
{ {
[NSException raise: NSInvalidArgumentException [NSException raise: NSInvalidArgumentException
format: @"invalid notification name"]; format: @"invalid notification name"];
@ -178,6 +324,11 @@ static NSDistributedNotificationCenter *defCenter = nil;
[_centerLock unlock]; [_centerLock unlock];
} }
/**
* Posts the notification to the center using
* postNotificationName:object:userInfo:deliverImmediately: with the
* delivery flag set to NO.
*/
- (void) postNotification: (NSNotification*)notification - (void) postNotification: (NSNotification*)notification
{ {
[self postNotificationName: [notification name] [self postNotificationName: [notification name]
@ -186,6 +337,11 @@ static NSDistributedNotificationCenter *defCenter = nil;
deliverImmediately: NO]; deliverImmediately: NO];
} }
/**
* Posts the notificationName and anObject to the center using
* postNotificationName:object:userInfo:deliverImmediately: with the
* user info set to nil and the delivery flag set to NO.
*/
- (void) postNotificationName: (NSString*)notificationName - (void) postNotificationName: (NSString*)notificationName
object: (NSString*)anObject object: (NSString*)anObject
{ {
@ -195,6 +351,11 @@ static NSDistributedNotificationCenter *defCenter = nil;
deliverImmediately: NO]; deliverImmediately: NO];
} }
/**
* Posts the notificationName, anObject and userInfo to the center using
* postNotificationName:object:userInfo:deliverImmediately: with the
* delivery flag set to NO.
*/
- (void) postNotificationName: (NSString*)notificationName - (void) postNotificationName: (NSString*)notificationName
object: (NSString*)anObject object: (NSString*)anObject
userInfo: (NSDictionary*)userInfo userInfo: (NSDictionary*)userInfo
@ -205,13 +366,19 @@ static NSDistributedNotificationCenter *defCenter = nil;
deliverImmediately: NO]; deliverImmediately: NO];
} }
/**
* The primitive notification posting method ...<br />
* The userInfo dictionary may contain only property-list objects.<br />
* The deliverImmediately flag specifies whether the suspension
* state of the receiving process is to be ignored.
*/
- (void) postNotificationName: (NSString*)notificationName - (void) postNotificationName: (NSString*)notificationName
object: (NSString*)anObject object: (NSString*)anObject
userInfo: (NSDictionary*)userInfo userInfo: (NSDictionary*)userInfo
deliverImmediately: (BOOL)deliverImmediately deliverImmediately: (BOOL)deliverImmediately
{ {
if (notificationName == nil || if (notificationName == nil
[notificationName isKindOfClass: [NSString class]] == NO) || [notificationName isKindOfClass: [NSString class]] == NO)
{ {
[NSException raise: NSInvalidArgumentException [NSException raise: NSInvalidArgumentException
format: @"invalid notification name"]; format: @"invalid notification name"];
@ -244,12 +411,15 @@ static NSDistributedNotificationCenter *defCenter = nil;
[_centerLock unlock]; [_centerLock unlock];
} }
/**
* Removes the observer from the center.
*/
- (void) removeObserver: (id)anObserver - (void) removeObserver: (id)anObserver
name: (NSString*)notificationName name: (NSString*)notificationName
object: (NSString*)anObject object: (NSString*)anObject
{ {
if (notificationName != nil && if (notificationName != nil
[notificationName isKindOfClass: [NSString class]] == NO) && [notificationName isKindOfClass: [NSString class]] == NO)
{ {
[NSException raise: NSInvalidArgumentException [NSException raise: NSInvalidArgumentException
format: @"invalid notification name"]; format: @"invalid notification name"];
@ -278,6 +448,12 @@ static NSDistributedNotificationCenter *defCenter = nil;
[_centerLock unlock]; [_centerLock unlock];
} }
/**
* Sets the suspension state of the receiver ... if the receiver is
* suspended, it won't handle notification until it is unsuspended
* again, unless the notifications are posted to be delivered
* immediately.
*/
- (void) setSuspended: (BOOL)flag - (void) setSuspended: (BOOL)flag
{ {
[_centerLock lock]; [_centerLock lock];
@ -296,6 +472,9 @@ static NSDistributedNotificationCenter *defCenter = nil;
[_centerLock unlock]; [_centerLock unlock];
} }
/**
* Returns the current suspension state of the receiver.
*/
- (BOOL) suspended - (BOOL) suspended
{ {
return _suspended; return _suspended;
@ -309,58 +488,83 @@ static NSDistributedNotificationCenter *defCenter = nil;
{ {
if (_remote == nil) if (_remote == nil)
{ {
NSString *host; NSString *host = nil;
NSString *description; NSString *service = nil;
NSString *description = nil;
/*
* Connect to the NSDistributedNotificationCenter for this host.
*/
host = [[NSUserDefaults standardUserDefaults] stringForKey: @"NSHost"];
if (host == nil)
{
host = @"";
}
else
{
NSHost *h;
if (_type == NSLocalNotificationCenterType)
{
/* /*
* If we have a host specified, but it is the current host, * Connect to the NSDistributedNotificationCenter for this host.
* we do not need to ask for a host by name (nameserver lookup
* can be faster) and the empty host name can be used to
* indicate that we may start a gdnc server locally.
*/ */
h = [NSHost hostWithName: host]; host = [[NSUserDefaults standardUserDefaults]
if (h == nil) stringForKey: @"NSHost"];
{ if (host == nil)
NSLog(@"Unknown -NSHost '%@' ignored", host);
host = @"";
}
else if ([h isEqual: [NSHost currentHost]] == YES)
{ {
host = @""; host = @"";
} }
else else
{ {
host = [h name]; NSHost *h;
}
}
if ([host length] == 0) /*
{ * If we have a host specified, but it is the current host,
description = @"local host"; * we do not need to ask for a host by name (nameserver lookup
* can be faster) and the empty host name can be used to
* indicate that we may start a gdnc server locally.
*/
h = [NSHost hostWithName: host];
if (h == nil)
{
NSLog(@"Unknown -NSHost '%@' ignored", host);
host = @"";
}
else if ([h isEqual: [NSHost currentHost]] == YES)
{
host = @"";
}
else
{
host = [h name];
}
}
if ([host length] == 0)
{
description = @"local host";
}
else
{
description = host;
}
service = GDNC_SERVICE;
}
else if (_type == GSNetworkNotificationCenterType)
{
host = [[NSUserDefaults standardUserDefaults]
stringForKey: @"GDNCHost"];
description = host;
if (host == nil)
{
host = @"*";
description = @"network host";
}
service = GDNC_NETWORK;
} }
else else
{ {
description = host; [NSException raise: NSInternalInconsistencyException
format: @"Unknown center type - %@", _type];
} }
_remote = RETAIN([NSConnection rootProxyForConnectionWithRegisteredName: _remote = RETAIN([NSConnection rootProxyForConnectionWithRegisteredName:
GDNC_SERVICE host: host]); service host: host]);
if (_remote == nil && [host isEqual: @""] == NO)
if (_type == NSLocalNotificationCenterType
&& _remote == nil && [host isEqual: @""] == NO)
{ {
_remote = [NSConnection rootProxyForConnectionWithRegisteredName: _remote = [NSConnection rootProxyForConnectionWithRegisteredName:
[GDNC_SERVICE stringByAppendingFormat: @"-%@", host] host: @"*"]; [service stringByAppendingFormat: @"-%@", host] host: @"*"];
RETAIN(_remote); RETAIN(_remote);
} }
@ -405,7 +609,12 @@ static NSDistributedNotificationCenter *defCenter = nil;
@"login or (better) when your computer is started up.\n", description, @"login or (better) when your computer is started up.\n", description,
[cmd stringByDeletingLastPathComponent]); [cmd stringByDeletingLastPathComponent]);
if ([host length] > 0) if (_type == GSNetworkNotificationCenterType)
{
args = [[NSArray alloc] initWithObjects:
@"-GSNetwork", @"YES", nil];
}
else if ([host length] > 0)
{ {
args = [[NSArray alloc] initWithObjects: args = [[NSArray alloc] initWithObjects:
@"-NSHost", host, nil]; @"-NSHost", host, nil];

View file

@ -343,13 +343,15 @@ con_loop (id prx)
id cobj; id cobj;
arp = [NSAutoreleasePool new]; arp = [NSAutoreleasePool new];
[prx addObject: [NSObject new]]; // So loss of this connection is logged.
cobj = [prx connectionForProxy]; cobj = [prx connectionForProxy];
printf("%d\n", [cobj retainCount]); printf("%d\n", [cobj retainCount]);
printf("%s\n", [[[cobj statistics] description] cString]); printf("%s\n", [[[cobj statistics] description] cString]);
//printf("%s\n", GSDebugAllocationList(YES)); //printf("%s\n", GSDebugAllocationList(YES));
printf("loop left running idle for 30 seconds\n");
[[NSRunLoop currentRunLoop] runUntilDate: [[NSRunLoop currentRunLoop] runUntilDate:
[NSDate dateWithTimeIntervalSinceNow: 2 * 60]]; [NSDate dateWithTimeIntervalSinceNow: 30]];
[cobj invalidate]; [cobj invalidate];
[arp release]; [arp release];
return 0; return 0;

View file

@ -607,7 +607,7 @@ static NSMutableSet *textNodes = nil;
NSDictionary *dict; NSDictionary *dict;
dict = [[localRefs refs] objectForKey: @"contents"]; dict = [[localRefs refs] objectForKey: @"contents"];
if ([dict count] > 0) if ([dict count] > 1)
{ {
NSArray *a; NSArray *a;
unsigned i; unsigned i;

View file

@ -22,6 +22,7 @@
*/ */
#define GDNC_SERVICE @"GDNCServer" #define GDNC_SERVICE @"GDNCServer"
#define GDNC_NETWORK @"GDNCNetwork"
@protocol GDNCClient @protocol GDNCClient
- (oneway void) postNotificationName: (NSString*)name - (oneway void) postNotificationName: (NSString*)name

View file

@ -216,6 +216,7 @@
- (id) init - (id) init
{ {
NSString *hostname; NSString *hostname;
NSString *service = GDNC_SERVICE;
connections = NSCreateMapTable(NSObjectMapKeyCallBacks, connections = NSCreateMapTable(NSObjectMapKeyCallBacks,
NSNonOwnedPointerMapValueCallBacks, 0); NSNonOwnedPointerMapValueCallBacks, 0);
@ -225,13 +226,18 @@
conn = [NSConnection defaultConnection]; conn = [NSConnection defaultConnection];
[conn setRootObject: self]; [conn setRootObject: self];
if ([[NSUserDefaults standardUserDefaults] boolForKey: @"GSNetwork"] == YES)
{
service = GDNC_NETWORK;
}
hostname = [[NSUserDefaults standardUserDefaults] stringForKey: @"NSHost"]; hostname = [[NSUserDefaults standardUserDefaults] stringForKey: @"NSHost"];
if ([hostname length] == 0 if ([hostname length] == 0
|| [[NSHost hostWithName: hostname] isEqual: [NSHost currentHost]] == YES) || [[NSHost hostWithName: hostname] isEqual: [NSHost currentHost]] == YES)
{ {
if ([conn registerName: GDNC_SERVICE] == NO) if ([conn registerName: service] == NO)
{ {
NSLog(@"gdnc - unable to register with name server - quiting."); NSLog(@"gdnc - unable to register with name server as %@ - quiting.",
service);
DESTROY(self); DESTROY(self);
return self; return self;
} }
@ -256,7 +262,7 @@
{ {
NSString *name = [a objectAtIndex: c]; NSString *name = [a objectAtIndex: c];
name = [GDNC_SERVICE stringByAppendingFormat: @"-%@", name]; name = [service stringByAppendingFormat: @"-%@", name];
if ([ns registerPort: port forName: name] == NO) if ([ns registerPort: port forName: name] == NO)
{ {
} }
@ -267,7 +273,7 @@
{ {
NSString *name = [a objectAtIndex: c]; NSString *name = [a objectAtIndex: c];
name = [GDNC_SERVICE stringByAppendingFormat: @"-%@", name]; name = [service stringByAppendingFormat: @"-%@", name];
if ([ns registerPort: port forName: name] == NO) if ([ns registerPort: port forName: name] == NO)
{ {
} }
@ -555,8 +561,8 @@
* the queue is empty ([obs->queue count] == 0) * the queue is empty ([obs->queue count] == 0)
* the observer is removed (obs is not in allObservers) * the observer is removed (obs is not in allObservers)
*/ */
while (obs != nil && [obs->queue count] > 0 && while (obs != nil && [obs->queue count] > 0
NSHashGet(allObservers, obs) != 0) && NSHashGet(allObservers, obs) != 0)
{ {
NS_DURING NS_DURING
{ {