bug-gnustep
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Thread (usually) not doing what I'd expect


From: Richard Frith-Macdonald
Subject: Re: Thread (usually) not doing what I'd expect
Date: Tue, 7 Nov 2006 14:49:00 +0000


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.






reply via email to

[Prev in Thread] Current Thread [Next in Thread]