Wrapping asynchronous calls into a synchronous blocking thread?

valheru picture valheru · Dec 20, 2011 · Viewed 12.2k times · Source

I'm writing an iOS module which currently sends an email asynchronously (using delegates). It uses SKPSMTPMessage which works great. My problem is the customer wants the code to fully block the thread until after the email has been sent (or failed to be sent). So they are basically asking for a synchronous solution, when currently it will attempt to send the email and then return from that block of code before the email has been sent.

So instead of trying to rewrite the SKPSMTPMessage code in a synchronous way (there doesn't seem to be any synchronous options for it), I am hoping to find some way of wrapping that block of asynchronous code in its own thread and maybe make the main thread wait for it to fully end (delegates and all).

I've tried a few different methods using NSOperations and NSThread but maybe I'm not doing something right because everytime I try to block the main thread, the asynchronous delegate calls still never seem to finish (do they come back on the main thread or something?).

Any information or even other ideas appreciated.

PS ~ I realize that this is a bit backwards. In most cases, asynchronous seems to be the way to go but this is a special case and the customer has their reasons for wanting it.

EDIT: Thanks for all the input. As suggested by one of the answers, I ended up just using a while loop that waited for the delegates to return yet let the runLoop continue as well like so:

while( ![messageDelegate hasFinishedOrFailed] ){
    // Allow the run loop to do some processing of the stream
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}

Answer

Jeff Kelley picture Jeff Kelley · Dec 20, 2011

I would try using dispatch semaphores. From the man page for dispatch_semaphore_create(3):

dispatch_semaphore_t sema = dispatch_semaphore_create(0);

dispatch_async(queue, ^{
    foo();
    dispatch_semaphore_signal(sema);
});

bar();

dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
dispatch_release(sema);
sema = NULL;

The call to dispatch_semaphore_wait() will block until the call to dispatch_semaphore_signal() completes.