I've always been interested in how to write the following code to use it for unit testing:
Is it possible to extend NSThread with a method that would check if a particular thread is blocked?
Right now I'am working with NSCondition: Xcode shows me the chain which is called by -wait to block the thread:
[NSCondition wait]
pthread_cond_wait$UNIX2003
_pthread_cond_wait
__psynch_cvwait
Besides checking the locks done by NSCondition, if it is even possible, I would highly appreciate method working also for any other blocking capabilities (dispatch semaphores, condition locks, sleeping threads and so on, ) - I have no idea about Objective-C internals, if maybe they could be catched by one method or each needs its own.
Here is a simple example of what I would like to achieve. The mysterious method is called isBlocked.
// Some test case
// ...
__block NSThread *thread;
NSCondition *condition = [NSCondition alloc] init];
dispatch_async(someQueue(), ^{
thread = NSThread.currentThread;
[condition lock];
[condition wait];
[condition unlock];
});
while(1) {
NSLog(@"Thread is blocked: %d", thread.isBlocked);
}
Note: I am not good at C and all this low-level POSIX stuff, so, please, be verbose.
Note 2: I am interested in solutions working for dispatch queues as well: if someone can show me how to test the fact that someQueue() is blocked by -[NSCondition wait] (not the fact that it is going to be blocked (fx hacking some code before -[condition wait] is run and the block is set), but the fact that thread/queue is blocked), I will accept this as an answer as much like I would do with working -[NSThread isBlocked] method.
Note 3: Suspecting bad news like "it is not possible", I claim that any ideas about catching the fact that -[condition wait] was run and the thread was set blocked (see Note 2) are appreciated and can be also accepted as an answer!
UPDATE 1 in address to the nice answer by Richard J. Ross III. Unfortunately, his answer does not work in my original example, the version which is closer to my real work (though it does not differ much from the example I've initially provided - sorry that I didn't include it in the first edition of the question):
// Example
// Here I've bootstrapped Richard's isLocking categories for both NSThread and NSCondition
// ...
// somewhere in SenTesting test case...
__block NSThread *thread;
NSCondition *condition = [NSCondition alloc] init];
__block BOOL wePassedBlocking = NO;
dispatch_async(someQueue(), ^{
thread = NSThread.currentThread;
[condition lock];
[condition wait];
[condition unlock];
wePassedBlocking = YES; // (*) This line is occasionally never reached!
});
while(!thread.isWaitingOnCondition); // I want this loop to exit after the condition really locks someQueue() and _thread_ __.
// sleep(1);
[condition lock];
[condition broadcast]; // BUT SOMETIMES this line is called before -[condition wait] is called inside someQueue() so the entire test case becomes blocked!
[condition unlock];
while(!wePassedBlocking); // (*) And so this loop occasionally never ends!
If I uncomment sleep(1) test begins working very stable without any occasional locks!
This leads us to the problem, that Richard's category does set state exactly one line before the actual blocking is done meaning that sometimes test case's main thread catches this new state before we actually have someQueue/thread blocked because Richard's code does not contain any synchronization mechanisms: @synchronized, NSLock or something like that! I hope I am making a clear explanation of this tricky case. For anyone who has doubts about what I've posted here, I would say that I have been also experimenting with multiple queues and even more complex cases, and if needed I'm ready to provide more examples. Richard, thanks again for your effort, let's think more together, if you understand these my points!
UPDATE 2
I see the dead-end paradox: obviously, to really set the state of waitingOnCondition we need to wrap this state's change inside some synchronization closures, but the problem is that the closing one, unlocking the synchronization lock, should be called after -[condition wait], but it can't, because the thread is already blocked. Again, I hope I am describing it pretty clear.