The Rules
  • Feel free to leave constructive criticism, or point out a better way to do something.
  • Personal attacks or flames, on me or anyone else, will be deleted.
  • Past history has shown that 99% of comments I can't read (i.e. those in other languages) to be spam. Therefore, any comment I can't read will be removed.
  • I'm pretty mellow concerning profanity, but excessive (as determined subjectively by me), bad language will be removed.

Wednesday, May 21, 2008

NSThreads when you need to wait.

For my webserver project I need to use queues. The main thread places connections that need to be serviced in a queue. "Worker" threads need to place log entries in a queue to be written out to the log file. These queues obviously need to be thread-safe. Writing a thread-safe queue is quite simple thanks to the NSLock and NSConditionLock classes. Just like any other semaphore, you create a NSLock object and surround your critical section with:

[myLock lock]
critical section...
[myLock unlock]

The real question I had to deal with here, though, was thoroughly testing my queues to make sure that they were _really_ thread safe. Unlike POSIX threads, Cocoa/Foundation has no mechanism for waiting on NSThread objects. But I needed to know that all of my testing threads were finished before I started comparing expected and actual results. The solution is not that difficult. The entry point/run loop for an NSThread object is a method with the mandatory signature:

(void) threadFuncName:(id)

So you create a struct or a class that has any data you need to pass in to your thread's main function. In addition, include in this struct or class an object of NSConditionLock. Immediately upon entry to the function, lock this with some condition code that will let your main program thread know that the thread is running. After spawning the thread, you want the main program execution to try and lock your condition lock when the run condition is no longer true. This will cause your main program thread to block and wait for your new thread to unlock with the condition you're waiting on. The last thing you'll want to do before your thread exits the run loop function is unlock the NSConditionLock with some condition that will let your main program thread know it's done. This will unblock your main thread of execution and you're off.
Sample code: (not complete, just the snippets that you'll need)


#import <Foundation/Foundation.h>
#define NOT_DONE 0
#define DONE 1

// this will need to be on some class that is calling the NSThread object
-(void) someFunc:(id) arg {
NSConditionLock* myLock = arg;
[myLock lock];
//do stuff that we need to know about in main
[myLock unlockWithCondition:DONE];
}


int main() {

NSConditionLock* finishedLock = [[NSConditionLock alloc]
initWithCondition: NOT_DONE];

NSThread* myThread = [[NSThread alloc] initWithTarget:self
selector:@selector(someFunc:) object:finishedLock];

[myThread start];

[finishedLock lockWhenCondition:DONE];

// now we can do whatever we need to do with the results from
// the thread we spawned

}


5 comments:

Anonymous said...

The source: NSThread* myThread = [[NSThread alloc] initWithTarget:self
selector:@selector(someFunc:) object:arg];
Where does the object arg come from? it should perhaps be finishedLock?

Alex said...

Yup. Fixed. Thanks.

Alexander Smirnov said...
This comment has been removed by the author.
Anonymous said...

initWithCondition is misspelled

Alex said...

Fixed. Thanks.