Minor thread handling tweaks

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@17672 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
CaS 2003-09-15 13:54:06 +00:00
parent cb5b1310d0
commit 3fe833176d
2 changed files with 124 additions and 43 deletions

View file

@ -1,3 +1,11 @@
2003-09-13 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSConnection.m: Add a couple of checks to raise exceptions
when trying to use a connection from the wrong thread. Also, change
the code for handling multithreaded connections so that they are
removed from the runloops of exiting threads in response to the
thread exit notification.
2003-09-13 David Ayers <d.ayers@inode.at> 2003-09-13 David Ayers <d.ayers@inode.at>
* Headers/Additions/GNUstepBase/GSCategories.h: Move declarations * Headers/Additions/GNUstepBase/GSCategories.h: Move declarations

View file

@ -87,6 +87,8 @@
NSString * const NSFailedAuthenticationException = NSString * const NSFailedAuthenticationException =
@"NSFailedAuthenticationExceptions"; @"NSFailedAuthenticationExceptions";
NSString * const NSObjectInaccessibleException =
@"NSObjectInaccessibleException";
/* /*
* Set up a type to permit us to have direct access into an NSDistantObject * Set up a type to permit us to have direct access into an NSDistantObject
@ -244,6 +246,7 @@ static unsigned local_object_counter = 0;
- (NSPortCoder*) _getReplyRmc: (int)sn; - (NSPortCoder*) _getReplyRmc: (int)sn;
- (NSPortCoder*) _makeInRmc: (NSMutableArray*)components; - (NSPortCoder*) _makeInRmc: (NSMutableArray*)components;
- (NSPortCoder*) _makeOutRmc: (int)sequence generate: (int*)sno reply: (BOOL)f; - (NSPortCoder*) _makeOutRmc: (int)sequence generate: (int*)sno reply: (BOOL)f;
- (void) _portIsInvalid: (NSNotification*)notification;
- (void) _sendOutRmc: (NSPortCoder*)c type: (int)msgid; - (void) _sendOutRmc: (NSPortCoder*)c type: (int)msgid;
- (void) _service_forwardForProxy: (NSPortCoder*)rmc; - (void) _service_forwardForProxy: (NSPortCoder*)rmc;
@ -252,6 +255,7 @@ static unsigned local_object_counter = 0;
- (void) _service_rootObject: (NSPortCoder*)rmc; - (void) _service_rootObject: (NSPortCoder*)rmc;
- (void) _service_shutdown: (NSPortCoder*)rmc; - (void) _service_shutdown: (NSPortCoder*)rmc;
- (void) _service_typeForSelector: (NSPortCoder*)rmc; - (void) _service_typeForSelector: (NSPortCoder*)rmc;
- (void) _threadWillExit: (NSNotification*)notification;
@end @end
#define _proxiesGate _refGate #define _proxiesGate _refGate
@ -1025,20 +1029,35 @@ static BOOL multi_threaded = NO;
a substitute. Note: The delegate is responsible for freeing a substitute. Note: The delegate is responsible for freeing
newConn if it returns something different. */ newConn if it returns something different. */
if ([del respondsToSelector: @selector(connection:didConnect:)]) if ([del respondsToSelector: @selector(connection:didConnect:)])
self = [del connection: parent didConnect: self]; {
self = [del connection: parent didConnect: self];
}
/* Register ourselves for invalidation notification when the
ports become invalid. */
nCenter = [NSNotificationCenter defaultCenter]; nCenter = [NSNotificationCenter defaultCenter];
/*
* Register ourselves for invalidation notification when the
* ports become invalid.
*/
[nCenter addObserver: self [nCenter addObserver: self
selector: @selector(portIsInvalid:) selector: @selector(_portIsInvalid:)
name: NSPortDidBecomeInvalidNotification name: NSPortDidBecomeInvalidNotification
object: r]; object: r];
if (s != nil) if (s != nil)
[nCenter addObserver: self {
selector: @selector(portIsInvalid:) [nCenter addObserver: self
name: NSPortDidBecomeInvalidNotification selector: @selector(_portIsInvalid:)
object: s]; name: NSPortDidBecomeInvalidNotification
object: s];
}
/*
* When any thread exits, we must check to see if we are using its
* runloop, and remove ourselves from it if necessary.
*/
[nCenter addObserver: self
selector: @selector(_threadWillExit:)
name: NSThreadWillExitNotification
object: nil];
/* In order that connections may be deallocated - there is an /* In order that connections may be deallocated - there is an
implementation of [-release] to automatically remove the connection implementation of [-release] to automatically remove the connection
@ -1046,9 +1065,8 @@ static BOOL multi_threaded = NO;
NSHashInsert(connection_table, (void*)self); NSHashInsert(connection_table, (void*)self);
M_UNLOCK(connection_table_gate); M_UNLOCK(connection_table_gate);
[[NSNotificationCenter defaultCenter] [nCenter postNotificationName: NSConnectionDidInitializeNotification
postNotificationName: NSConnectionDidInitializeNotification object: self];
object: self];
return self; return self;
} }
@ -1078,7 +1096,7 @@ static BOOL multi_threaded = NO;
M_UNLOCK(_refGate); M_UNLOCK(_refGate);
/* /*
* Don't need notifications any more - so remove self as observer. * Don't need notifications any more - so remove self as observer.
*/ */
[[NSNotificationCenter defaultCenter] removeObserver: self]; [[NSNotificationCenter defaultCenter] removeObserver: self];
@ -1100,11 +1118,11 @@ static BOOL multi_threaded = NO;
(gsaddr)self, _receivePort, _sendPort); (gsaddr)self, _receivePort, _sendPort);
} }
/* /*
* We need to notify any watchers of our death - but if we are already * We need to notify any watchers of our death - but if we are already
* in the deallocation process, we can't have a notification retaining * in the deallocation process, we can't have a notification retaining
* and autoreleasing us later once we are deallocated - so we do the * and autoreleasing us later once we are deallocated - so we do the
* notification with a local autorelease pool to ensure that any release * notification with a local autorelease pool to ensure that any release
* is done before the deallocation completes. * is done before the deallocation completes.
*/ */
{ {
CREATE_AUTORELEASE_POOL(arp); CREATE_AUTORELEASE_POOL(arp);
@ -1124,8 +1142,8 @@ static BOOL multi_threaded = NO;
M_LOCK(_proxiesGate); M_LOCK(_proxiesGate);
if (_localTargets != 0) if (_localTargets != 0)
{ {
NSMutableArray *targets; NSMutableArray *targets;
unsigned i = _localTargets->nodeCount; unsigned i = _localTargets->nodeCount;
GSIMapEnumerator_t enumerator; GSIMapEnumerator_t enumerator;
GSIMapNode node; GSIMapNode node;
@ -1173,6 +1191,17 @@ static BOOL multi_threaded = NO;
} }
M_UNLOCK(_proxiesGate); M_UNLOCK(_proxiesGate);
#if 1
/*
* If we are invalidated, we shouldn't be receiving any event and
* should not need to be in any run loops.
*/
while ([_runLoops count] > 0)
{
[self removeRunLoop: [_runLoops lastObject]];
}
#endif
RELEASE(self); RELEASE(self);
} }
@ -1811,6 +1840,7 @@ static void retEncoder (DOContext *ctxt)
const char *type; const char *type;
retval_t retframe; retval_t retframe;
DOContext ctxt; DOContext ctxt;
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
memset(&ctxt, 0, sizeof(ctxt)); memset(&ctxt, 0, sizeof(ctxt));
ctxt.connection = self; ctxt.connection = self;
@ -1819,6 +1849,19 @@ static void retEncoder (DOContext *ctxt)
NSParameterAssert (_isValid); NSParameterAssert (_isValid);
if ([_runLoops indexOfObjectIdenticalTo: runLoop] == NSNotFound)
{
if (_multipleThreads == NO)
{
[NSException raise: NSObjectInaccessibleException
format: @"Forwarding message in wrong thread"];
}
else
{
[self addRunLoop: runLoop];
}
}
/* get the method types from the selector */ /* get the method types from the selector */
#if NeXT_RUNTIME #if NeXT_RUNTIME
[NSException [NSException
@ -1940,7 +1983,21 @@ static void retEncoder (DOContext *ctxt)
BOOL needsResponse; BOOL needsResponse;
const char *type; const char *type;
DOContext ctxt; DOContext ctxt;
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
if ([_runLoops indexOfObjectIdenticalTo: runLoop] == NSNotFound)
{
if (_multipleThreads == NO)
{
[NSException raise: NSObjectInaccessibleException
format: @"Forwarding message in wrong thread"];
}
else
{
[self addRunLoop: runLoop];
}
}
/* Encode the method on an RMC, and send it. */ /* Encode the method on an RMC, and send it. */
NSParameterAssert (_isValid); NSParameterAssert (_isValid);
@ -2389,7 +2446,21 @@ static void callEncoder (DOContext *ctxt)
*/ */
NS_DURING NS_DURING
{ {
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
NSParameterAssert (_isValid); NSParameterAssert (_isValid);
if ([_runLoops indexOfObjectIdenticalTo: runLoop] == NSNotFound)
{
if (_multipleThreads == YES)
{
[self addRunLoop: runLoop];
}
else
{
[NSException raise: NSObjectInaccessibleException
format: @"Message received in wrong thread"];
}
}
/* Save this for later */ /* Save this for later */
[aRmc decodeValueOfObjCType: @encode(int) at: &ctxt.seq]; [aRmc decodeValueOfObjCType: @encode(int) at: &ctxt.seq];
@ -2634,7 +2705,6 @@ static void callEncoder (DOContext *ctxt)
NSDate *delay_date = nil; NSDate *delay_date = nil;
NSRunLoop *runLoop = [runLoopClass currentRunLoop]; NSRunLoop *runLoop = [runLoopClass currentRunLoop];
BOOL isLocked = NO; BOOL isLocked = NO;
BOOL addedRunLoop = NO;
/* /*
* If we have sent out a request on a run loop that we don't already * If we have sent out a request on a run loop that we don't already
@ -2642,11 +2712,17 @@ static void callEncoder (DOContext *ctxt)
* enabled, we must add the run loop of the new thread so that we can * enabled, we must add the run loop of the new thread so that we can
* get the reply in this thread. * get the reply in this thread.
*/ */
if (_multipleThreads == YES if ([_runLoops indexOfObjectIdenticalTo: runLoop] == NSNotFound)
&& [_runLoops indexOfObjectIdenticalTo: runLoop] == NSNotFound)
{ {
[self addRunLoop: runLoop]; if (_multipleThreads == YES)
addedRunLoop = YES; {
[self addRunLoop: runLoop];
}
else
{
[NSException raise: NSObjectInaccessibleException
format: @"Waiting for reply in wrong thread"];
}
} }
NS_DURING NS_DURING
@ -2766,19 +2842,9 @@ static void callEncoder (DOContext *ctxt)
format: @"invalidated while awaiting reply"]; format: @"invalidated while awaiting reply"];
} }
} }
if (addedRunLoop == YES)
{
addedRunLoop = NO;
[self removeRunLoop: runLoop];
}
} }
NS_HANDLER NS_HANDLER
{ {
if (addedRunLoop == YES)
{
addedRunLoop = NO;
[self removeRunLoop: runLoop];
}
if (isLocked == YES) if (isLocked == YES)
{ {
M_UNLOCK(_queueGate); M_UNLOCK(_queueGate);
@ -3457,32 +3523,24 @@ static void callEncoder (DOContext *ctxt)
return ret; return ret;
} }
/* Accessing ivars */
/* Prevent trying to encode the connection itself */ /* Prevent trying to encode the connection itself */
- (void) encodeWithCoder: (NSCoder*)anEncoder - (void) encodeWithCoder: (NSCoder*)anEncoder
{ {
[self shouldNotImplement: _cmd]; [self shouldNotImplement: _cmd];
} }
- (id) initWithCoder: (NSCoder*)aDecoder; - (id) initWithCoder: (NSCoder*)aDecoder;
{ {
[self shouldNotImplement: _cmd]; [self shouldNotImplement: _cmd];
return self; return self;
} }
/* Shutting down and deallocating. */
/* /*
* We register this method for a notification when a port dies. * We register this method for a notification when a port dies.
* NB. It is possible that the death of a port could be notified * NB. It is possible that the death of a port could be notified
* to us after we are invalidated - in which case we must ignore it. * to us after we are invalidated - in which case we must ignore it.
*/ */
- (void) portIsInvalid: (NSNotification*)notification - (void) _portIsInvalid: (NSNotification*)notification
{ {
if (_isValid) if (_isValid)
{ {
@ -3504,6 +3562,21 @@ static void callEncoder (DOContext *ctxt)
} }
} }
/**
* On thread exit, we need to be removed from the runloop of the thread
* or we will retain that and cause a memory leak.
*/
- (void) _threadWillExit: (NSNotification*)notification
{
extern NSRunLoop *GSRunLoopForThread(NSThread*);
NSRunLoop *runLoop = GSRunLoopForThread([notification object]);
if ([_runLoops indexOfObjectIdenticalTo: runLoop] != NSNotFound)
{
[self removeRunLoop: runLoop];
}
}
@end @end