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.
Here you go! It won't detect threads being waited on by anything other than -[NSCondition wait], but it could easily be extended to detect other kinds of waiting.
It's probably not the best implementation out there, but it does in fact work, and will do what you need it to.
#import <objc/runtime.h>
#implementation NSThread(isLocking)
static int waiting_condition_key;
-(BOOL) isWaitingOnCondition {
// here, we sleep for a microsecond (1 millionth of a second) so that the
// other thread can catch up, and actually call 'wait'. This time
// interval is so small that you will never notice it in an actual
// application, it's just here because of how multithreaded
// applications work.
usleep(1);
BOOL val = [objc_getAssociatedObject(self, &waiting_condition_key) boolValue];
// sleep before and after so it works on both edges
usleep(1);
return val;
}
-(void) setIsWaitingOnCondition:(BOOL) value {
objc_setAssociatedObject(self, &waiting_condition_key, #(value), OBJC_ASSOCIATION_RETAIN);
}
#end
#implementation NSCondition(isLocking)
+(void) load {
Method old = class_getInstanceMethod(self, #selector(wait));
Method new = class_getInstanceMethod(self, #selector(_wait));
method_exchangeImplementations(old, new);
}
-(void) _wait {
// this is the replacement for the original wait method
[[NSThread currentThread] setIsWaitingOnCondition:YES];
// call the original implementation, which now resides in the same name as this method
[self _wait];
[[NSThread currentThread] setIsWaitingOnCondition:NO];
}
#end
int main()
{
__block NSCondition *condition = [NSCondition new];
NSThread *otherThread = [[NSThread alloc] initWithTarget:^{
NSLog(#"Thread started");
[condition lock];
[condition wait];
[condition unlock];
NSLog(#"Thread ended");
} selector:#selector(invoke) object:nil];
[otherThread start];
while (![otherThread isWaitingOnCondition]);
[condition lock];
[condition signal];
[condition unlock];
NSLog(#"%i", [otherThread isWaitingOnCondition]);
}
Output:
2013-03-20 10:43:01.422 TestProj[11354:1803] Thread started
2013-03-20 10:43:01.424 TestProj[11354:1803] Thread ended
2013-03-20 10:43:01.425 TestProj[11354:303] 0
Here is a solution using dispatch_semaphore_t
PGFoo.h
#import <Foundation/Foundation.h>
#interface PGFoo : NSObject
- (void)longRunningAsynchronousMethod:(void (^)(NSInteger result))completion;
#end
PGFoo.m
#import "PGFoo.h"
#implementation PGFoo
- (void)longRunningAsynchronousMethod:(void (^)(NSInteger))completion {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(5);
completion(1);
});
}
#end
Test Methods
- (void)testThatFailsBecauseItIsImpatient {
PGFoo *foo = [[PGFoo alloc] init];
__block NSInteger theResult = 0;
[foo longRunningAsynchronousMethod:^(NSInteger result) {
theResult = result;
}];
STAssertEquals(theResult, 1, nil);
}
- (void)testThatPassesBecauseItIsPatient {
PGFoo *foo = [[PGFoo alloc] init];
__block NSInteger theResult = 0;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[foo longRunningAsynchronousMethod:^(NSInteger result) {
theResult = result;
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
STAssertEquals(theResult, 1, nil);
}
By using a dispatch_semaphore_t you can "track" whether a thread that is waiting on that semaphore is blocked. For every call of dispatch_semaphore_wait the semaphore's count is decremented and the thread waits until a call of dispatch_semaphore_signal is made, when dispatch_semaphore_signal is called the semaphore's count is incremented, if the count is incremented to a value greater than -1 the thread continues.
This solution fails to answer your question about checking whether an NSThread is "blocked" but I think it provides what you are reaching for, assuming you're not reaching to check on NSThread instances that are maintained within an existing framework.
Related
In the initialization method of a class I am declaring the thread as such:
NSThread* myThread = [[[NSThread alloc] initWithTarget:self selector:#selector(m_run_thread) object:nil] autorelease];
[myThread start];
I also have a boolean value which is set to NO. Later on in the code I set the boolean value to YES.
bool_run_progress_thread = YES;
The contents of the method m_run_thread is as follows:
-(void) m_run_thread
{
if (bool_run_progress_thread)
{
//do processing here
}
bool_run_progress_thread = NO;
}
The problem is that the method m_run_thread is never being accessed. What am I doing wrong?
P.S. I have also tried to set up the Thread using the following (and older)method:
[NSThread detachNewThreadSelector:#selector(m_run_thread)
toTarget:self
withObject:nil];
... but to no avail as well.
"...and I am only getting it to show once" Yes, that's exactly how it should be. After being started, a thread runs once from its start to its end (ignoring errors here for the moment), and having reached the end, the thread is essentially dead and gone.
If you want the thread to repeat its execution, you have to prepare for that yourself:
- (void) m_run_thread
{
for (;;)
{
if (bool_run_progress_thread)
{
//do processing here
bool_run_progress_thread = NO;
}
}
}
But there is still a lot wrong with this code: essentially, when run, the code forms a busy waiting loop. Assuming, that bool_run_progress_thread is only ever true for short periods of time, the background thread should be sleeping most of the time. Insead, if you try the code as its stands, it will instead consume CPU time (and lots of it).
A better approach to this would involve condition variables:
#class Whatsoever
{
NSCondition* cvar;
BOOL doProgress;
...
}
...
#end
and
- (void) m_run_thread
{
for (;;)
{
[cvar lock];
while (!doProgress)
{
[cvar wait];
}
doProgress = NO;
[cvar unlock];
... do work here ...
}
}
and in order to trigger the execution, you'd do:
- (void) startProgress
{
[cvar lock];
doProgress = YES;
[cvar signal];
[cvar unlock];
}
Doing things this way also takes care of another subtle problem: the visibility of the changes made to the global flag (your bool_run_progress_thread, my doProgess). Depending on the processor and its memory order, changes made without special protection might or might not become (ever) visible to other threads. This problem is taken care of by the NSCondition, too.
I have a method in some class which performs some task using a block. When I execute that method using NSInvocationOperation then control never goes to the block. I tried logging inside the block but that is never called actually. But if I simply call that method with instance of that class then everything works as expected.
Don’t blocks run inside NSOperation?
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:myClassObj selector:#selector(myClassMethod:) object:obj1];
[[AppDelegate sharedOpQueue] addOperation:op];
[op release];
- (void)myClassMethod:(id)obj
{
AnotherClass *otherClass = [[AnotherClass allco] init]
[otherClass fetchXMLWithCompletionHandler:^(WACloudURLRequest* request, xmlDocPtr doc, NSError* error)
{
if(error){
if([_delegate respondsToSelector:#selector(handleFail:)]){
[_delegate handleFail:error];
}
return;
}
if([_delegate respondsToSelector:#selector(doSomeAction)]){
[_delegate doSomeAction];
}
}];
}
- (void) fetchXMLWithCompletionHandler:(WAFetchXMLHandler)block
{
_xmlBlock = [block copy];
[NSURLConnection connectionWithRequest:request delegate:self];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if(_xmlBlock) {
const char *baseURL = NULL;
const char *encoding = NULL;
xmlDocPtr doc = xmlReadMemory([_data bytes], (int)[_data length], baseURL, encoding, (XML_PARSE_NOCDATA | XML_PARSE_NOBLANKS));
NSError* error = [WAXMLHelper checkForError:doc];
if(error){
_xmlBlock(self, nil, error);
} else {
_xmlBlock(self, doc, nil);
}
xmlFreeDoc(doc);
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
if(_xmlBlock) {
_xmlBlock(self, nil, error);
}
}
You are performing your NSConnection asynchronously (which you don't need to do in an NSOperation because you should already be on a background thread).
After your call to fetchXMLWithCompletionHandler, your method ends. This signals that the NSOperation is finished and it gets released and it's thread gets either reused for something else or, more likely, released as well. This means that by the time you get your callbacks, your initial object doesn't exist anymore!
There are two solutions :
1) Use NSURLConnection synchronously. This will wait in your myClassMethod until it has got a response.
2) Learn about NSOperations's concurrent mode. I don't know if this will work with NSInvocationOperation though :( And it's fairly complicated compared to option (1).
I would use method (1) - you have already created a background thread to perform your operation in, why bother creating another one to do your connection request?
There are two ways of fixing your problem:
The easy way out
is — as Dean suggests — using +[NSURLConnection sendSynchronousRequest:returningResponse:error:], as you already are on a different thread. This has you covered — I'd say — 80-90% of the time, is really simple to implement and Just Works™.
The other way
is only slightly more complicated and has you covered for all the cases where the first method does not suffice — by visiting the root of your problem:
NSURLConnection works in conjunction with the runloop — and the threads managed by NSOperationQueue don't necessarily use (or even have!) an associated runloop.
While calling +[NSURLConnection connectionWithRequest:delegate:] will implicitly create a runloop, if needed, it does not cause the runloop to actually run!
This is your responsibility, when the NSOperationQueue you use is not the queue associated with the main thread.
To do so, change your implementation of fetchXMLWithCompletionHandler: to look similar to the following:
- (void)fetchXMLWithCompletionHandler:(WAFetchXMLHandler)block
{
self.xmlHandler = block; // Declare a #property for the block with the copy attribute set
self.mutableXMLData = [NSMutableData data]; // again, you should have a property for this...
self.currentConnection = [NSURLConnection connectionWithRequest:request delegate:self]; // having a #property for the connection allows you to cancel it, if needed.
self.connectionShouldBeRunning = YES; // ...and have a BOOL like this one, setting it to NO in connectionDidFinishLoad: and connection:didFailWithError:
NSRunLoop *loop = [NSRunLoop currentRunLoop];
NSDate *neverExpire = [NSDate distantFuture];
BOOL runLoopDidIterateRegularly = YES;
while( self.connectionShouldBeRunning && runLoopDidIterateRegularly ) {
runLoopDidIterateRegularly = [loop runMode:NSDefaultRunLoopMode beforeDate:neverExpire];
}
}
With these small changes, you're good to go. Bonus: this is really flexible and (eventually) reusable throughout all your code — if you move the XML-parsing out of that class and make your handler simply take an NSData, an NSError and (optionally) an NSURLResponse.
Since you probably don't want the clients of your loader to see and possibly mess with the properties I just suggested you should add, you can declare them in a class continuation.
I am new to Objective C programming.
I have created two threads called add and display using the NSInvocationOperation and added it on to the NSOperationQueue.
I make the display thread to run first and then run the add thread. The display thread after printing the "Welcome to display" has to wait for the results to print from the add method.
So i have set the waitUntilFinished method.
Both the Operations are on the same queue. If i use waitUntilFinished for operations on the same queue there may be a situation for deadlock to happen(from apples developer documentation). Is it so?
To wait for particular time interval there is a method called waitUntilDate:
But if i need to like this wait(min(100,dmax)); let dmax = 20; How to do i wait for these conditions?
It would be much helpful if anyone can explain with an example.
EDITED:
threadss.h
------------
#import <Foundation/Foundation.h>
#interface threadss : NSObject {
BOOL m_bRunThread;
int a,b,c;
NSOperationQueue* queue;
NSInvocationOperation* operation;
NSInvocationOperation* operation1;
NSConditionLock* theConditionLock;
}
-(void)Thread;
-(void)add;
-(void)display;
#end
threadss.m
------------
#import "threadss.h"
#implementation threadss
-(id)init
{
if (self = [super init]) {
queue = [[NSOperationQueue alloc]init];
operation = [[NSInvocationOperation alloc]initWithTarget:self selector:#selector(display) object:nil];
operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:#selector(add) object:nil];
theConditionLock = [[NSConditionLock alloc]init];
}
return self;
}
-(void)Thread
{
m_bRunThread = YES;
//[operation addDependency:operation1];
if (m_bRunThread) {
[queue addOperation:operation];
}
//[operation addDependency:operation1];
[queue addOperation:operation1];
//[self performSelectorOnMainThread:#selector(display) withObject:nil waitUntilDone:YES];
//NSLog(#"I'm going to do the asynchronous communication btwn the threads!!");
//[self add];
//[operation addDependency:self];
sleep(1);
[queue release];
[operation release];
//[operation1 release];
}
-(void)add
{
NSLog(#"Going to add a and b!!");
a=1;
b=2;
c = a + b;
NSLog(#"Finished adding!!");
}
-(void)display
{
NSLog(#"Into the display method");
[operation1 waitUntilFinished];
NSLog(#"The Result is:%d",c);
}
#end
main.m
-------
#import <Foundation/Foundation.h>
#import "threadss.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
threadss* thread = [[threadss alloc]init];
[thread Thread];
[pool drain];
return 0;
}
This is what i have tried with a sample program.
output
2011-06-03 19:40:47.898 threads_NSOperationQueue[3812:1503] Going to add a and b!!
2011-06-03 19:40:47.898 threads_NSOperationQueue[3812:1303] Into the display method
2011-06-03 19:40:47.902 threads_NSOperationQueue[3812:1503] Finished adding!!
2011-06-03 19:40:47.904 threads_NSOperationQueue[3812:1303] The Result is:3
Is the way of invoking the thread is correct.
1.Will there be any deadlock condition?
2.How to do wait(min(100,dmax)) where dmax = 50.
Assuming I'm understanding your question correctly, you have two operations:
Operation A: prints a message, waits for operation B to finish, continues
Operation B: prints a message
If this is the case, can you just print the first message, start operation B, then start operation A?
Also, when you are using NSOperationQueue you don't directly manage threads, it does all the thread management for you. So in your question when you said 'thread' you actually meant to say 'operation'.
To directly answer your question, "Can this cause a deadlock", yes it could. If you change the queue to be sequential instead of concurrent or if you make operation 2 dependent on operation 1 you will probably lock up. I'd recommend not trying to do what you're doing, refactor your code so that one operation doesn't need to pause while the other one works. Based on the code you've posted, there's no reason to structure your code like that.
Hope this might help you, it is the iOS version of WaitForSingleObject in Windows:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[object runSomeLongOperation:^{
// your own code here.
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(semaphore);
I am testing some code that does asynchronous processing using Grand Central Dispatch. The testing code looks like this:
[object runSomeLongOperationAndDo:^{
STAssert…
}];
The tests have to wait for the operation to finish. My current solution looks like this:
__block BOOL finished = NO;
[object runSomeLongOperationAndDo:^{
STAssert…
finished = YES;
}];
while (!finished);
Which looks a bit crude, do you know a better way? I could expose the queue and then block by calling dispatch_sync:
[object runSomeLongOperationAndDo:^{
STAssert…
}];
dispatch_sync(object.queue, ^{});
…but that’s maybe exposing too much on the object.
Trying to use a dispatch_semaphore. It should look something like this:
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[object runSomeLongOperationAndDo:^{
STAssert…
dispatch_semaphore_signal(sema);
}];
if (![NSThread isMainThread]) {
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
} else {
while (dispatch_semaphore_wait(sema, DISPATCH_TIME_NOW)) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0]];
}
}
This should behave correctly even if runSomeLongOperationAndDo: decides that the operation isn't actually long enough to merit threading and runs synchronously instead.
In addition to the semaphore technique covered exhaustively in other answers, we can now use XCTest in Xcode 6 to perform asynchronous tests via XCTestExpectation. This eliminates the need for semaphores when testing asynchronous code. For example:
- (void)testDataTask
{
XCTestExpectation *expectation = [self expectationWithDescription:#"asynchronous request"];
NSURL *url = [NSURL URLWithString:#"http://www.apple.com"];
NSURLSessionTask *task = [self.session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
XCTAssertNil(error, #"dataTaskWithURL error %#", error);
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSInteger statusCode = [(NSHTTPURLResponse *) response statusCode];
XCTAssertEqual(statusCode, 200, #"status code was not 200; was %d", statusCode);
}
XCTAssert(data, #"data nil");
// do additional tests on the contents of the `data` object here, if you want
// when all done, Fulfill the expectation
[expectation fulfill];
}];
[task resume];
[self waitForExpectationsWithTimeout:10.0 handler:nil];
}
For the sake of future readers, while the dispatch semaphore technique is a wonderful technique when absolutely needed, I must confess that I see too many new developers, unfamiliar with good asynchronous programming patterns, gravitate too quickly to semaphores as a general mechanism for making asynchronous routines behave synchronously. Worse I've seen many of them use this semaphore technique from the main queue (and we should never block the main queue in production apps).
I know this isn't the case here (when this question was posted, there wasn't a nice tool like XCTestExpectation; also, in these testing suites, we must ensure the test does not finish until the asynchronous call is done). This is one of those rare situations where the semaphore technique for blocking the main thread might be necessary.
So with my apologies to the author of this original question, for whom the semaphore technique is sound, I write this warning to all of those new developers who see this semaphore technique and consider applying it in their code as a general approach for dealing with asynchronous methods: Be forewarned that nine times out of ten, the semaphore technique is not the best approach when encounting asynchronous operations. Instead, familiarize yourself with completion block/closure patterns, as well as delegate-protocol patterns and notifications. These are often much better ways of dealing with asynchronous tasks, rather than using semaphores to make them behave synchronously. Usually there are good reasons that asynchronous tasks were designed to behave asynchronously, so use the right asynchronous pattern rather than trying to make them behave synchronously.
I’ve recently come to this issue again and wrote the following category on NSObject:
#implementation NSObject (Testing)
- (void) performSelector: (SEL) selector
withBlockingCallback: (dispatch_block_t) block
{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self performSelector:selector withObject:^{
if (block) block();
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(semaphore);
}
#end
This way I can easily turn asynchronous call with a callback into a synchronous one in tests:
[testedObject performSelector:#selector(longAsyncOpWithCallback:)
withBlockingCallback:^{
STAssert…
}];
Generally don't use any of these answers, they often won't scale (there's exceptions here and there, sure)
These approaches are incompatible with how GCD is intended to work and will end up either causing deadlocks and/or killing the battery by nonstop polling.
In other words, rearrange your code so that there is no synchronous waiting for a result, but instead deal with a result being notified of change of state (eg callbacks/delegate protocols, being available, going away, errors, etc.). (These can be refactored into blocks if you don't like callback hell.) Because this is how to expose real behavior to the rest of the app than hide it behind a false façade.
Instead, use NSNotificationCenter, define a custom delegate protocol with callbacks for your class. And if you don't like mucking with delegate callbacks all over, wrap them into a concrete proxy class that implements the custom protocol and saves the various block in properties. Probably also provide convenience constructors as well.
The initial work is slightly more but it will reduce the number of awful race-conditions and battery-murdering polling in the long-run.
(Don't ask for an example, because it's trivial and we had to invest the time to learn objective-c basics too.)
Here's a nifty trick that doesn't use a semaphore:
dispatch_queue_t serialQ = dispatch_queue_create("serialQ", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQ, ^
{
[object doSomething];
});
dispatch_sync(serialQ, ^{ });
What you do is wait using dispatch_sync with an empty block to Synchronously wait on a serial dispatch queue until the A-Synchronous block has completed.
- (void)performAndWait:(void (^)(dispatch_semaphore_t semaphore))perform;
{
NSParameterAssert(perform);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
perform(semaphore);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(semaphore);
}
Example usage:
[self performAndWait:^(dispatch_semaphore_t semaphore) {
[self someLongOperationWithSuccess:^{
dispatch_semaphore_signal(semaphore);
}];
}];
There’s also SenTestingKitAsync that lets you write code like this:
- (void)testAdditionAsync {
[Calculator add:2 to:2 block^(int result) {
STAssertEquals(result, 4, nil);
STSuccess();
}];
STFailAfter(2.0, #"Timeout");
}
(See objc.io article for details.) And since Xcode 6 there’s an AsynchronousTesting category on XCTest that lets you write code like this:
XCTestExpectation *somethingHappened = [self expectationWithDescription:#"something happened"];
[testedObject doSomethigAsyncWithCompletion:^(BOOL succeeded, NSError *error) {
[somethingHappened fulfill];
}];
[self waitForExpectationsWithTimeout:1 handler:NULL];
Here is an alternative from one of my tests:
__block BOOL success;
NSCondition *completed = NSCondition.new;
[completed lock];
STAssertNoThrow([self.client asyncSomethingWithCompletionHandler:^(id value) {
success = value != nil;
[completed lock];
[completed signal];
[completed unlock];
}], nil);
[completed waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
[completed unlock];
STAssertTrue(success, nil);
Swift 4:
Use synchronousRemoteObjectProxyWithErrorHandler instead of remoteObjectProxy when creating the remote object. No more need for a semaphore.
Below example will return the version received from the proxy. Without the synchronousRemoteObjectProxyWithErrorHandler it will crash (trying to access non accessible memory):
func getVersion(xpc: NSXPCConnection) -> String
{
var version = ""
if let helper = xpc.synchronousRemoteObjectProxyWithErrorHandler({ error in NSLog(error.localizedDescription) }) as? HelperProtocol
{
helper.getVersion(reply: {
installedVersion in
print("Helper: Installed Version => \(installedVersion)")
version = installedVersion
})
}
return version
}
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[object blockToExecute:^{
// ... your code to execute
dispatch_semaphore_signal(sema);
}];
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) {
[[NSRunLoop currentRunLoop]
runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0]];
}
This did it for me.
Sometimes, Timeout loops are also helpful. May you wait until you get some (may be BOOL) signal from async callback method, but what if no response ever, and you want to break out of that loop?
Here below is solution, mostly answered above, but with an addition of Timeout.
#define CONNECTION_TIMEOUT_SECONDS 10.0
#define CONNECTION_CHECK_INTERVAL 1
NSTimer * timer;
BOOL timeout;
CCSensorRead * sensorRead ;
- (void)testSensorReadConnection
{
[self startTimeoutTimer];
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
while (dispatch_semaphore_wait(sema, DISPATCH_TIME_NOW)) {
/* Either you get some signal from async callback or timeout, whichever occurs first will break the loop */
if (sensorRead.isConnected || timeout)
dispatch_semaphore_signal(sema);
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate dateWithTimeIntervalSinceNow:CONNECTION_CHECK_INTERVAL]];
};
[self stopTimeoutTimer];
if (timeout)
NSLog(#"No Sensor device found in %f seconds", CONNECTION_TIMEOUT_SECONDS);
}
-(void) startTimeoutTimer {
timeout = NO;
[timer invalidate];
timer = [NSTimer timerWithTimeInterval:CONNECTION_TIMEOUT_SECONDS target:self selector:#selector(connectionTimeout) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}
-(void) stopTimeoutTimer {
[timer invalidate];
timer = nil;
}
-(void) connectionTimeout {
timeout = YES;
[self stopTimeoutTimer];
}
Very primitive solution to the problem:
void (^nextOperationAfterLongOperationBlock)(void) = ^{
};
[object runSomeLongOperationAndDo:^{
STAssert…
nextOperationAfterLongOperationBlock();
}];
I have to wait until a UIWebView is loaded before running my method, I was able to get this working by performing UIWebView ready checks on main thread using GCD in combination with semaphore methods mentioned in this thread. Final code looks like this:
-(void)myMethod {
if (![self isWebViewLoaded]) {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block BOOL isWebViewLoaded = NO;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (!isWebViewLoaded) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((0.0) * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
isWebViewLoaded = [self isWebViewLoaded];
});
[NSThread sleepForTimeInterval:0.1];//check again if it's loaded every 0.1s
}
dispatch_sync(dispatch_get_main_queue(), ^{
dispatch_semaphore_signal(semaphore);
});
});
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0]];
}
}
}
//Run rest of method here after web view is loaded
}
Is there a clean way of joining threads in Objective C much like "Thread.join" in Java? I found the method performSelector:onThread:withObject:waitUntilDone: but the limitation of this is I can't call the "blocking" on a different line because I want to do something like this:
[dispatch Thread A];
[process something on main thread];
[wait for Thread A to finish before proceeding];
Thank you in advance.
I'm not aware of any Cocoa API to do this, but it wouldn't be too difficult to do with NSThread, pretty easy to do with a lock, and even easier to do with Grand Central Dispatch.
NSThread
NSThread * otherThread = [[NSThread alloc] initWithTarget:self selector:#selector(methodToPerformInBackground:) object:aParameter];
[otherThread start];
//do some stuff
while ([otherThread isFinished] == NO) {
usleep(1000);
}
[otherThread release];
NSLock
NSLock * lock = [[NSLock alloc] init];
//initiate the background task, which should immediately lock the lock and unlock when done
//do some stuff
[lock lock]; //this will pause until the background stuff unlocks
[lock unlock];
[lock release];
Grand Central Dispatch
dispatch_group_t myGroup = dispatch_group_create();
dispatch_group_async(myGroup, dispatch_get_global_queue(), ^{
//stuff to do in the background
});
//do some stuff
dispatch_group_wait(myGroup, DISPATCH_TIME_FOREVER);
dispatch_release(myGroup);
NSConditionLock is the answer to my question, Sorry Dave DeLong, but I cannot use:
"while ([otherThread isFinished] == NO) "
-- because I need fast continuous processing and cannot use sleep.
NSLock
-- because as you said it "initiate the background task, which should immediately lock the lock and unlock when done", this is not a solution because I tried it and we are not sure if the subthread will execute last before the lock-unlock-release on main thread, I ended up getting random errors.
Grand Central Dispatch
--because it's only available in IOS4 and Snow Leopard 10.6, I'm using a lower version.
But your answer gave me the idea and thank you very much for it, so I just "upped" you.
I ended up doing this:
#define T_START 0
#define T_FINISHED 1
-(void) updateVerticalScopeBackground: (id) aParam {
[lockForThread lock];
NSAutoreleasePool *pool = [NSAutoreleasePool new];
//do something
[pool release];
[lockForThread unlockWithCondition:T_FINISHED];
}
-(void) sumFunc {
lockForThread = [[NSConditionLock alloc]
initWithCondition: T_START];
NSThread* updateVerticalScope = [[NSThread alloc] initWithTarget:self selector:#selector(updateVerticalScopeBackground:) object:nil];
[updateVerticalScope start];
//do some processing
[lockForThread lockWhenCondition:T_FINISHED];
[lockForThread unlockWithCondition:T_FINISHED];
[lockForThread release];
}
You could use NSCondition signal/wait.
Could you use a lock to do this? In other words something like this (pseudocode)
create an object to lock on, visible to both threads
dispatch thread A; thread A immediately takes the lock and keeps it for its duration
process something on main thread
main thread attempts to take the lock (this will block until Thread A releases it)
after acquiring the lock, main thread releases it and continues on
You never want your main thread to be blocked waiting for another thread. At least you don't in any application with a user interface because, if the main thread is blocked, your application is frozen.
It would be far better for the main thread to start the background thread, do the other stuff it needs to do and then return to the run loop. The background thread would notify the main thread of completion by sending -performSelectorOnMainThread:waitUntilDone: