Reputation: 5337
I have multiple asynchronous tasks that all depend on an initial async authentication step to succeed. I'm using a semaphore to block all the secure tasks until the authentication completes. It's mostly for timing purposes, as the tasks rely on a secure token obtained at the end of authentication. The authentication involves a network request, and may take several seconds.
The difficulty in my code below seems to be that the dispatch_semaphore_signal()
issued after authentication only signals that the first semaphore lock may continue. The second would continue to block. There could in future be many moew blocking tasks, all waiting on the semaphore.
I'm wondering if there is a cleaner way to go about this blocking. I believe that each waiting task could immediately issue another dispatch_semaphore_signal()
, thus releasing the next task, and so on. Is there a way to release all blocking semaphores in one call?
Is there a cleaner way to do this with GCD? I'm not adept with GCD, so code snippets help, in the context of the below usage.
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
// in actuality, these 3 may be any where in the app, in different classes, methods, etc
// so a completionHandler is not possible
[self authentication]; // async, could take many seconds
[self authenticatedTask1]; // async
[self authenticatedTask2]; // async
- (void) authentication {
// async url request, assume it is configured here
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
// authenticate
authenticated = TRUE;
secure_token = @"4rjiofwefsdf"; // obtained during auth
dispatch_semaphore_signal(sem);
}];
}
- (void) authenticatedTask1 {
// put on new thread, so semaphore doesn't block program
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
if(!authenticated){
// wait until authenticated
dispatch_semaphore_wait(sem)
}
// continue after authenticated, using secure_token
});
}
- (void) authenticatedTask2 {
// put on new thread, so semaphore doesn't block program
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
if(!authenticated){
// wait until authenticated
dispatch_semaphore_wait(sem)
}
// continue after authenticated, using secure_token
});
}
Upvotes: 3
Views: 1025
Reputation: 17053
It is not very elegant but you can call 'dispatch_semaphore_signal' right after 'dispatch_semaphore_wait'. It should solve the problem.
- (void)authenticatedTask1 {
// put on new thread, so semaphore doesn't block program
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
if(!authenticated){
// wait until authenticated
dispatch_semaphore_wait(sem);
dispatch_semaphore_signal(sem); // !!!
}
// continue after authenticated, using secure_token
});
}
Upvotes: 2
Reputation: 8202
You could pass in the methods to be executed in a block to be run in the completltion block, then you wouldn't need to use semaphores. Also you would then not need to bother with the dispatch_async
waiting for the semaphore to finish:
[self authenticationWithCompletionBlock:^{
[self authenticatedTask1];
[self authenticatedTask2];
}];
- (void) authenticationWithCompletionBlock:(dispatch_block_t)block {
// async url request, assume it is configured here
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
// authenticate
authenticated = TRUE;
secure_token = @"4rjiofwefsdf"; // obtained during auth
block();
}];
}
If the methods are in the same class, you could just call the methods directly instead of the block.
And if you need to know when both async tasks (in your case authenticatedTask1
and authenticatedTask2
) are finished, then you'd need to use dispatch groups.
Upvotes: 1
Reputation: 6515
You can put the authenticated tasks into their own suspended dispatch queue, and resume the dispatch queue once the authentication succeeded.
Upvotes: 3