On 7 Nov 2006, at 14:29, Graham J Lee wrote:
Hi all,
The code below demonstrates the problem I'm seeing with gnustep-
base 1.13.0 on i386/Linux, where NSThread
+detachNewThreadSelector:toTarget:withObject: is not consistently
executing the method indicated. I'm trying to choose my words
carefully here, because it *does* create a new LWP. In fact, if I
set a breakpoint at the [NSThread ...] line, I can step through
NSThread.m and see that objc_thread_dispatch returns non-NULL[*].
However, the global variable x never gets set and none of the NSLog
() lines in the -doStuff method appear. I'm not so good with
debugging multiple-threaded apps but if I step past
objc_thread_detach then look at the threads display in ddd, I only
see one thread, although I do get a notification of a new LWP from
the debugger.
It looks like it might be racy, because if I step past the section
(lines 551-555 here, I'm on base 1.13.0) in NSThread.m:
if (objc_thread_detach(@selector(__sendThreadMethod),thread,nil)
== NULL)
{
[NSException raise: NSInternalConsistencyException
format: @"Unable to detach thread (unknown error)"];
} // <--step to here
}
and continue from there, I do get the change in x and the NSLog()
messages as expected. Similarly if I execute the objc_thread_detach
() function myself in the debugger by assigning its return value to
a convenience variable, I get the expected result. But never when
just running the code, debug=yes or not. The same program does
what I expect - i.e. prints out all the NSLog()s and sets x to 3 -
with the Apple Foundation.
It is indeed a race condition ... I'm not really familiar with gdb/
thread interaction so I can't tell you about problems in gdb, but see
below ...
[*] I can't step _into_ it, I guess because my objc runtime wasn't
built with debugging symbols.
Here's the source.
--8<--
#include <Foundation/Foundation.h>
NSLock *lock;
int x=0;
@interface AnObject:NSObject
{
}
- (void)doStuff;
@end
@implementation AnObject
- (void)doStuff
{
id localPool=[[NSAutoreleasePool alloc] init];
NSLog(@"Here we go, acquiring lock...");
[lock lock];
x=3;
NSLog(@"Relinquish lock...");
[lock unlock];
[localPool release];
}
@end
int
main(int argc, const char *argv[])
{
id pool = [[NSAutoreleasePool alloc] init];
AnObject *obj=[[AnObject alloc] init];
lock=[[NSLock alloc] init];
// Your code here...
NSLog(@"Spawning the thread...");
[NSThread detachNewThreadSelector:@selector(doStuff)
toTarget:obj withObject:nil];
NSLog(@"It's been launched.");
// The end...
while([lock tryLock]==NO);
NSLog(@"Teardown: x=%d",x);
[obj release];
[lock release];
[pool release];
return 0;
}
There is a bug in your code in the main() function ... the intention
is plainly to busy loop in the main thread doing a tryLock until the
detached thread releases the lock, but you don't actually wait for
the detached thread to aquire the lock in the first place ... which
means that it's possible (even likely) for the main thread to aquire
the lock before the other thread does ... in which case the main
thread will run to completion and exit before the other thread can do
anything.
Presumably the program happens to work on MacOS-X because the thread
scheduling in the kernel happens to execute the new thread before the
main thread continues and comes to the lock. You can test this by
putting a sleep() for a second before the main thread tries the
lock ... which will usually (though not guaranteed) mean that the
detached thread gets round to aquiring the lock first.
There are various safe locking schemes you could use to achieve the
desired effect.
A simple one is to use a condition lock which you initialise in one
condition and then have the main thread attempt to lock it with a
different condition ... this will block until the detached thread
locks it with the initial condition and then unlocks it with the
condition the main thread is waiting for.
_______________________________________________
Bug-gnustep mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/bug-gnustep