3

I tried to subclass NSThread in order to operate a thread with some data. I want to simulate the join() in python, according to the doc:

join(): Wait until the thread terminates. This blocks the calling thread until the thread whose join() method is called terminates

So I think using performSelector: onThread: withObject: waitUntilDone:YES would be fine, but it does not work. It just do nothing and would not exit, running like forever.

This is my code:

@interface MyClass : NSThread
@property (strong, nonatomic) NSMutableArray *msgQueue;
@property (assign, nonatomic) BOOL stop;
@end

@implementation MyClass

-(id)init
{
    self = [super init];
    if (self) {
        self.msgQueue = [NSMutableArray array];
        self.stop = NO;
        [self start];
        return self;
    }
    return nil;
}

-(void)myRun
{
    while (!self.stop) {
        NSLock *arrayLock = [[NSLock alloc] init];
        [arrayLock lock];
        NSArray *message = [self.msgQueue firstObject];
        [self.msgQueue removeObjectAtIndex:0];
        [arrayLock unlock];
        NSLog(@"%@", message);
        if ([message[0] isEqualToString:@"terminate"]) {
            self.stop = YES;
        }
    }
}

-(void)join
{
    [self performSelector:@selector(myRun) onThread:self withObject:nil waitUntilDone:YES];
}

@end

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        MyClass *a = [[MyClass alloc] init];
        [a.msgQueue addObject:@[@"terminate",@"hello world"]];
        //[a myRun];   // this line works so the myRun method should be good,
        [a join];      // but I want this line work, and I have no idea what the problem is.
    }
    return 0;
}
Kun Hu
  • 417
  • 5
  • 11
  • `performSelector:` queues it to run on the run loop of the thread, so you need to make sure that you have a run loop on that thread. – Gavin Feb 25 '14 at 18:46
  • I just posted an answer which will give more details. – Gavin Feb 25 '14 at 18:57

2 Answers2

1

From Apple's documentation on performSelector:onThread:withObject:waitUntilDone::

This method queues the message on the run loop of the target thread using the default run loop modes—that is, the modes associated with the NSRunLoopCommonModes constant. As part of its normal run loop processing, the target thread dequeues the message (assuming it is running in one of the default run loop modes) and invokes the desired method.

You probably never started a run loop on the thread, so it will never execute your myRun method, since it has no run loop to execute on.

As for Merlevede's answer, myRun is not enqueued on the same thread as join. join was called on your main thread, whereas you're trying to enqueue myRun on your secondary thread. So his theory is incorrect. Also from Apple's documentation regarding the wait parameter:

If the current thread and target thread are the same, and you specify YES for this parameter, the selector is performed immediately on the current thread. If you specify NO, this method queues the message on the thread’s run loop and returns, just like it does for other threads. The current thread must then dequeue and process the message when it has an opportunity to do so.

So even if it was on the same thread, it wouldn't be stuck waiting, it would just execute it right away as if you had directly called the method instead of using performSelector: in the first place.

Gavin
  • 8,204
  • 3
  • 32
  • 42
  • How can he be overriding join() if the base class doesn't have this method? And join is NOT being called on the main thread, it is called from the Thread's main() function which runs in a different thread!!! – Merlevede Feb 25 '14 at 19:01
  • @Merlevede My mistake on that detail. I removed it from my answer, but the rest is applicable. – Gavin Feb 25 '14 at 19:04
  • @Merlevede His `main` thread is outside of `MyClass`, which is his `NSThread` subclass. – Gavin Feb 25 '14 at 19:07
  • My bad, I didn't see the @end. And now I start to see your point. Ohhhh, so in this case the user doesn't have a thread entry point!!! But still that doesn't explain why the code hangs! – Merlevede Feb 25 '14 at 19:10
  • @Merlevede He just says the method never executes, which is understandable since he doesn't have a run loop to execute it. And as for the thread not exiting, I don't know what happens if you have no main method for the `NSThread`, no run loop, and you don't specify a target and selector to have it run. It may be that the thread exits almost right away and he doesn't realize it, or maybe the thread just sits there sleeping. – Gavin Feb 25 '14 at 19:17
  • Yes, I didn't have a run loop since I had no idea about it..Could you give some code to illustrate more details? Like how I should modify this code to make it work? Thanks. – Kun Hu Feb 25 '14 at 21:49
0

You're basically in a deadlock condition.

-(void)join
{
    [self performSelector:@selector(myRun) onThread:self withObject:nil waitUntilDone:YES];
}

join is waiting for myRun to finish (waitUntilDone flag), but myRun is enqueued on the same thread as join, so it's also waiting for join to finish.

For performSelector:onThread:withObject:waitUntilDone: you would never pass the current thread as the thread parameter.

Merlevede
  • 8,140
  • 1
  • 24
  • 39
  • This is incorrect. They're on different threads, so there wouldn't be any deadlock, and even if they were, there still wouldn't be a deadlock because of how `performSelector:` works. See my answer for more details. – Gavin Feb 25 '14 at 18:58
  • @Gavin No sir, I'm afraid you're wrong. See my comments to your answer. – Merlevede Feb 25 '14 at 19:03
  • I was wrong with that one detail, which I removed from my answer. But as for the other parts, they're still applicable, and your answer is still incorrect. See the quote from Apple's documentation in my answer. – Gavin Feb 25 '14 at 19:06