mirror of
https://github.com/gnustep/libs-base.git
synced 2025-05-31 00:30:53 +00:00
Fix thread memory leak.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@17336 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
e8fb1797f9
commit
778c3a3c7c
3 changed files with 152 additions and 122 deletions
|
@ -4,6 +4,10 @@
|
||||||
change ... seemed to cause problems on some systems.
|
change ... seemed to cause problems on some systems.
|
||||||
* Tools/gdnc.m: ditto.
|
* Tools/gdnc.m: ditto.
|
||||||
* Source/NSDebug.m: Small thread safely fix.
|
* Source/NSDebug.m: Small thread safely fix.
|
||||||
|
* Source/NSThread.m: Avoid multiple copies of housekeeping timer.
|
||||||
|
* Source/NSConnection.m: Fix thread related memory leak leaving
|
||||||
|
an NSRunLoop in a connection when the connection is no longer
|
||||||
|
using it.
|
||||||
|
|
||||||
2003-07-23 Richard Frith-Macdonald <rfm@gnu.org>
|
2003-07-23 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
|
|
|
@ -2633,122 +2633,160 @@ static void callEncoder (DOContext *ctxt)
|
||||||
NSTimeInterval delay_interval = last_interval;
|
NSTimeInterval delay_interval = last_interval;
|
||||||
NSDate *delay_date = nil;
|
NSDate *delay_date = nil;
|
||||||
NSRunLoop *runLoop = [runLoopClass currentRunLoop];
|
NSRunLoop *runLoop = [runLoopClass currentRunLoop];
|
||||||
|
BOOL isLocked = NO;
|
||||||
|
BOOL addedRunLoop = NO;
|
||||||
|
|
||||||
if (debug_connection > 5)
|
/*
|
||||||
NSLog(@"Waiting for reply sequence %d on %x:%x",
|
* If we have sent out a request on a run loop that we don't already
|
||||||
sn, self, [NSThread currentThread]);
|
* know about, it must be on a new thread - so if we have multipleThreads
|
||||||
M_LOCK(_queueGate);
|
* enabled, we must add the run loop of the new thread so that we can
|
||||||
while (_isValid == YES
|
* get the reply in this thread.
|
||||||
&& (node = GSIMapNodeForKey(_replyMap, (GSIMapKey)sn)) != 0
|
*/
|
||||||
&& node->value.obj == dummyObject)
|
if (_multipleThreads == YES
|
||||||
|
&& [_runLoops indexOfObjectIdenticalTo: runLoop] == NSNotFound)
|
||||||
{
|
{
|
||||||
M_UNLOCK(_queueGate);
|
[self addRunLoop: runLoop];
|
||||||
if (timeout_date == nil)
|
addedRunLoop = YES;
|
||||||
{
|
}
|
||||||
timeout_date = [dateClass allocWithZone: NSDefaultMallocZone()];
|
|
||||||
timeout_date
|
|
||||||
= [timeout_date initWithTimeIntervalSinceNow: _replyTimeout];
|
|
||||||
}
|
|
||||||
if (_multipleThreads == YES)
|
|
||||||
{
|
|
||||||
NSDate *limit_date;
|
|
||||||
NSTimeInterval next_interval;
|
|
||||||
|
|
||||||
/*
|
NS_DURING
|
||||||
* If multiple threads are using this connections, another
|
{
|
||||||
* thread may read the reply we are waiting for - so we must
|
if (debug_connection > 5)
|
||||||
* break out of the runloop frequently to check. We do this
|
NSLog(@"Waiting for reply sequence %d on %x:%x",
|
||||||
* by setting a small delay and increasing it each time round
|
sn, self, [NSThread currentThread]);
|
||||||
* so that this semi-busy wait doesn't consume too much
|
M_LOCK(_queueGate); isLocked = YES;
|
||||||
* processor time (I hope).
|
while (_isValid == YES
|
||||||
* We set an upper limit on the delay to avoid responsiveness
|
&& (node = GSIMapNodeForKey(_replyMap, (GSIMapKey)sn)) != 0
|
||||||
* problems.
|
&& node->value.obj == dummyObject)
|
||||||
*/
|
{
|
||||||
RELEASE(delay_date);
|
M_UNLOCK(_queueGate); isLocked = NO;
|
||||||
delay_date = [dateClass allocWithZone: NSDefaultMallocZone()];
|
if (timeout_date == nil)
|
||||||
if (delay_interval < 1.0)
|
|
||||||
{
|
{
|
||||||
next_interval = last_interval + delay_interval;
|
timeout_date = [dateClass allocWithZone: NSDefaultMallocZone()];
|
||||||
last_interval = delay_interval;
|
timeout_date
|
||||||
delay_interval = next_interval;
|
= [timeout_date initWithTimeIntervalSinceNow: _replyTimeout];
|
||||||
}
|
}
|
||||||
delay_date
|
if (_multipleThreads == YES)
|
||||||
= [delay_date initWithTimeIntervalSinceNow: delay_interval];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We must not set a delay date that is further in the future
|
|
||||||
* than the timeout date for the response to be returned.
|
|
||||||
*/
|
|
||||||
if ([timeout_date earlierDate: delay_date] == timeout_date)
|
|
||||||
{
|
{
|
||||||
limit_date = timeout_date;
|
NSDate *limit_date;
|
||||||
|
NSTimeInterval next_interval;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If multiple threads are using this connections, another
|
||||||
|
* thread may read the reply we are waiting for - so we must
|
||||||
|
* break out of the runloop frequently to check. We do this
|
||||||
|
* by setting a small delay and increasing it each time round
|
||||||
|
* so that this semi-busy wait doesn't consume too much
|
||||||
|
* processor time (I hope).
|
||||||
|
* We set an upper limit on the delay to avoid responsiveness
|
||||||
|
* problems.
|
||||||
|
*/
|
||||||
|
RELEASE(delay_date);
|
||||||
|
delay_date = [dateClass allocWithZone: NSDefaultMallocZone()];
|
||||||
|
if (delay_interval < 1.0)
|
||||||
|
{
|
||||||
|
next_interval = last_interval + delay_interval;
|
||||||
|
last_interval = delay_interval;
|
||||||
|
delay_interval = next_interval;
|
||||||
|
}
|
||||||
|
delay_date
|
||||||
|
= [delay_date initWithTimeIntervalSinceNow: delay_interval];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We must not set a delay date that is further in the future
|
||||||
|
* than the timeout date for the response to be returned.
|
||||||
|
*/
|
||||||
|
if ([timeout_date earlierDate: delay_date] == timeout_date)
|
||||||
|
{
|
||||||
|
limit_date = timeout_date;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
limit_date = delay_date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the runloop returns without having done anything, AND we
|
||||||
|
* were waiting for the final timeout, then we must break out
|
||||||
|
* of the loop.
|
||||||
|
*/
|
||||||
|
if ([runLoop runMode: NSConnectionReplyMode
|
||||||
|
beforeDate: limit_date] == NO)
|
||||||
|
{
|
||||||
|
if (limit_date == timeout_date)
|
||||||
|
{
|
||||||
|
M_LOCK(_queueGate); isLocked = YES;
|
||||||
|
node = GSIMapNodeForKey(_replyMap, (GSIMapKey)sn);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
limit_date = delay_date;
|
/*
|
||||||
}
|
* Normal operation - wait for data or for a timeout.
|
||||||
|
*/
|
||||||
/*
|
if ([runLoop runMode: NSConnectionReplyMode
|
||||||
* If the runloop returns without having done anything, AND we
|
beforeDate: timeout_date] == NO)
|
||||||
* were waiting for the final timeout, then we must break out
|
|
||||||
* of the loop.
|
|
||||||
*/
|
|
||||||
if ([runLoop runMode: NSConnectionReplyMode
|
|
||||||
beforeDate: limit_date] == NO)
|
|
||||||
{
|
|
||||||
if (limit_date == timeout_date)
|
|
||||||
{
|
{
|
||||||
M_LOCK(_queueGate);
|
M_LOCK(_queueGate); isLocked = YES;
|
||||||
node = GSIMapNodeForKey(_replyMap, (GSIMapKey)sn);
|
node = GSIMapNodeForKey(_replyMap, (GSIMapKey)sn);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
M_LOCK(_queueGate); isLocked = YES;
|
||||||
|
}
|
||||||
|
if (node == 0)
|
||||||
|
{
|
||||||
|
rmc = nil;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/*
|
rmc = node->value.obj;
|
||||||
* Normal operation - wait for data to be received or for a timeout.
|
GSIMapRemoveKey(_replyMap, (GSIMapKey)sn);
|
||||||
*/
|
}
|
||||||
if ([runLoop runMode: NSConnectionReplyMode
|
M_UNLOCK(_queueGate); isLocked = NO;
|
||||||
beforeDate: timeout_date] == NO)
|
TEST_RELEASE(delay_date);
|
||||||
|
TEST_RELEASE(timeout_date);
|
||||||
|
if (rmc == nil)
|
||||||
|
{
|
||||||
|
[NSException raise: NSInternalInconsistencyException
|
||||||
|
format: @"no reply message available"];
|
||||||
|
}
|
||||||
|
if (rmc == dummyObject)
|
||||||
|
{
|
||||||
|
if (_isValid == YES)
|
||||||
{
|
{
|
||||||
M_LOCK(_queueGate);
|
[NSException raise: NSPortTimeoutException
|
||||||
node = GSIMapNodeForKey(_replyMap, (GSIMapKey)sn);
|
format: @"timed out waiting for reply"];
|
||||||
break;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[NSException raise: NSPortTimeoutException
|
||||||
|
format: @"invalidated while awaiting reply"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
M_LOCK(_queueGate);
|
if (addedRunLoop == YES)
|
||||||
}
|
|
||||||
if (node == 0)
|
|
||||||
{
|
|
||||||
rmc = nil;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rmc = node->value.obj;
|
|
||||||
GSIMapRemoveKey(_replyMap, (GSIMapKey)sn);
|
|
||||||
}
|
|
||||||
M_UNLOCK(_queueGate);
|
|
||||||
TEST_RELEASE(delay_date);
|
|
||||||
TEST_RELEASE(timeout_date);
|
|
||||||
if (rmc == nil)
|
|
||||||
{
|
|
||||||
[NSException raise: NSInternalInconsistencyException
|
|
||||||
format: @"no reply message available"];
|
|
||||||
}
|
|
||||||
if (rmc == dummyObject)
|
|
||||||
{
|
|
||||||
if (_isValid == YES)
|
|
||||||
{
|
{
|
||||||
[NSException raise: NSPortTimeoutException
|
addedRunLoop = NO;
|
||||||
format: @"timed out waiting for reply"];
|
[self removeRunLoop: runLoop];
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[NSException raise: NSPortTimeoutException
|
|
||||||
format: @"invalidated while awaiting reply"];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
NS_HANDLER
|
||||||
|
{
|
||||||
|
if (addedRunLoop == YES)
|
||||||
|
{
|
||||||
|
addedRunLoop = NO;
|
||||||
|
[self removeRunLoop: runLoop];
|
||||||
|
}
|
||||||
|
if (isLocked == YES)
|
||||||
|
{
|
||||||
|
M_UNLOCK(_queueGate);
|
||||||
|
}
|
||||||
|
[localException raise];
|
||||||
|
}
|
||||||
|
NS_ENDHANDLER
|
||||||
|
|
||||||
NSDebugMLLog(@"NSConnection", @"Consuming reply RMC %d on %x", sn, self);
|
NSDebugMLLog(@"NSConnection", @"Consuming reply RMC %d on %x", sn, self);
|
||||||
return rmc;
|
return rmc;
|
||||||
}
|
}
|
||||||
|
@ -2951,21 +2989,6 @@ static void callEncoder (DOContext *ctxt)
|
||||||
reserved: [_sendPort reservedSpaceLength]];
|
reserved: [_sendPort reservedSpaceLength]];
|
||||||
|
|
||||||
M_LOCK(_refGate);
|
M_LOCK(_refGate);
|
||||||
/*
|
|
||||||
* If we have sent out a request on a run loop that we don't already
|
|
||||||
* know about, it must be on a new thread - so if we have multipleThreads
|
|
||||||
* enabled, we must add the run loop of the new thread so that we can
|
|
||||||
* get the reply in this thread.
|
|
||||||
*/
|
|
||||||
if (_multipleThreads == YES && needsReply == YES)
|
|
||||||
{
|
|
||||||
NSRunLoop *loop = [runLoopClass currentRunLoop];
|
|
||||||
|
|
||||||
if ([_runLoops indexOfObjectIdenticalTo: loop] == NSNotFound)
|
|
||||||
{
|
|
||||||
[self addRunLoop: loop];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We replace the code we have just used in the cache, and tell it not to
|
* We replace the code we have just used in the cache, and tell it not to
|
||||||
|
|
|
@ -325,8 +325,9 @@ GSRunLoopForThread(NSThread *t)
|
||||||
r = [NSRunLoop new];
|
r = [NSRunLoop new];
|
||||||
[d setObject: r forKey: key];
|
[d setObject: r forKey: key];
|
||||||
RELEASE(r);
|
RELEASE(r);
|
||||||
if (t == nil || t == defaultThread)
|
if (housekeeper == nil && (t == nil || t == defaultThread))
|
||||||
{
|
{
|
||||||
|
CREATE_AUTORELEASE_POOL (arp);
|
||||||
NSNotificationCenter *ctr;
|
NSNotificationCenter *ctr;
|
||||||
NSNotification *not;
|
NSNotification *not;
|
||||||
NSInvocation *inv;
|
NSInvocation *inv;
|
||||||
|
@ -334,7 +335,7 @@ GSRunLoopForThread(NSThread *t)
|
||||||
|
|
||||||
ctr = [NSNotificationCenter defaultCenter];
|
ctr = [NSNotificationCenter defaultCenter];
|
||||||
not = [NSNotification notificationWithName: @"GSHousekeeping"
|
not = [NSNotification notificationWithName: @"GSHousekeeping"
|
||||||
object: r
|
object: nil
|
||||||
userInfo: nil];
|
userInfo: nil];
|
||||||
sel = @selector(postNotification:);
|
sel = @selector(postNotification:);
|
||||||
inv = [NSInvocation invocationWithMethodSignature:
|
inv = [NSInvocation invocationWithMethodSignature:
|
||||||
|
@ -344,10 +345,14 @@ GSRunLoopForThread(NSThread *t)
|
||||||
[inv setArgument: ¬ atIndex: 2];
|
[inv setArgument: ¬ atIndex: 2];
|
||||||
[inv retainArguments];
|
[inv retainArguments];
|
||||||
|
|
||||||
housekeeper = [NSTimer timerWithTimeInterval: 30.0
|
housekeeper = [[NSTimer alloc] initWithFireDate: nil
|
||||||
invocation: inv
|
interval: 30.0
|
||||||
repeats: YES];
|
target: inv
|
||||||
|
selector: NULL
|
||||||
|
userInfo: nil
|
||||||
|
repeats: YES];
|
||||||
[r addTimer: housekeeper forMode: NSDefaultRunLoopMode];
|
[r addTimer: housekeeper forMode: NSDefaultRunLoopMode];
|
||||||
|
RELEASE(arp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -408,23 +413,17 @@ gnustep_base_thread_callback()
|
||||||
*/
|
*/
|
||||||
+ (NSThread*) currentThread
|
+ (NSThread*) currentThread
|
||||||
{
|
{
|
||||||
NSThread *t;
|
NSThread *t = nil;
|
||||||
|
|
||||||
if (entered_multi_threaded_state == NO)
|
if (entered_multi_threaded_state == NO)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The NSThread class has been initialized - so we will have a default
|
* The NSThread class has been initialized - so we will have a default
|
||||||
* thread set up.
|
* thread set up unless the default thread subsequently exited.
|
||||||
*/
|
*/
|
||||||
t = defaultThread;
|
t = defaultThread;
|
||||||
if (t == nil)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "ALERT ... [NSThread +currentThread] ... the "
|
|
||||||
"default thread is nil!");
|
|
||||||
fflush(stderr); // Needed for windoze
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
if (t == nil)
|
||||||
{
|
{
|
||||||
t = (NSThread*)objc_thread_get_data();
|
t = (NSThread*)objc_thread_get_data();
|
||||||
if (t == nil)
|
if (t == nil)
|
||||||
|
@ -630,6 +629,10 @@ gnustep_base_thread_callback()
|
||||||
[NSAutoreleasePool _endThread: self];
|
[NSAutoreleasePool _endThread: self];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (self == defaultThread)
|
||||||
|
{
|
||||||
|
defaultThread = nil;
|
||||||
|
}
|
||||||
NSDeallocateObject(self);
|
NSDeallocateObject(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue