Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save prachigauriar/8152704 to your computer and use it in GitHub Desktop.
Save prachigauriar/8152704 to your computer and use it in GitHub Desktop.
Examples in Objective-C and Swift that use dispatch_async and dispatch_semaphore to cleanly replicate the behavior of ‑[NSCondition waitUntilDate:].
//
// QLCDispatchAsyncWithCompletionAndTimeout.m
// Blocks-WaitUntilDate
//
// Created by Prachi Gauriar on 12/23/2013.
// Copyright (c) 2013 Prachi Gauriar.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import <dispatch/dispatch.h>
#import <Foundation/Foundation.h>
/*!
@abstract Asynchronously dispatches the specified (primary) block, dispatching the timeout block if
the timeout period elapses before the block completes; otherwise dispatches the completion block.
@param queue A concurrent queue on which to dispatch the specified blocks. May not be NULL.
@param timeoutInNanoseconds The number of nanoseconds to wait before dispatching the timeout block.
May not be negative. The timeout period begins when the primary block is dispatched, not when this
function is called.
@param block The primary block to dispatch asynchronously. May not be NULL.
@param completionBlock A block that is asynchronously dispatched after the primary block has completed.
This block is only dispatched if the primary block finished executing before the timeout period
elapsed.
@param timeoutBlock A block that is asynchronously dispatched after the the timeout period has elapsed.
This block is only dispatched if the primary block did not finish executing before the timeout
period elapsed.
@discussion No guarantee is made about when the completion block or timeout block will be executed.
The only guarantees are that the completion block will be dispatched after the primary block
finishes executing and that the timeout block will be dispatched after the timeout period elapses.
When the blocks are executed depends entirely on the priority of the concurrent queue that the
blocks are dispatched on.
*/
void QLCDispatchAsyncWithCompletionAndTimeout(dispatch_queue_t queue, int64_t timeoutInNanoseconds,
dispatch_block_t block, dispatch_block_t completionBlock,
dispatch_block_t timeoutBlock)
{
NSCParameterAssert(queue);
NSCParameterAssert(timeoutInNanoseconds >= 0);
NSCParameterAssert(block);
dispatch_async(queue, ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, timeoutInNanoseconds);
dispatch_async(queue, ^{
long timedOut = dispatch_semaphore_wait(semaphore, timeoutTime);
if (timedOut) {
if (timeoutBlock) {
timeoutBlock();
}
} else if (completionBlock) {
completionBlock();
}
});
block();
dispatch_semaphore_signal(semaphore);
});
}
int main(int argc, const char * argv[])
{
const int32_t QLCTimeoutInterval = 4;
@autoreleasepool {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// All this group stuff here is just for our example. It allows us to wait until everything is
// done before exiting.
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
NSDate *startDate = [NSDate date];
QLCDispatchAsyncWithCompletionAndTimeout(queue, QLCTimeoutInterval * NSEC_PER_SEC , ^{
// Sleep time is between 1 and 2*QLCTimeInterval seconds
uint32_t sleepTimeInterval = arc4random_uniform(2 * QLCTimeoutInterval) + 1;
NSLog(@"Sleeping for %ds.", sleepTimeInterval);
sleep(sleepTimeInterval);
}, ^{
NSTimeInterval elapsedTime = [[NSDate date] timeIntervalSinceDate:startDate];
NSLog(@"Completed after %.3fs.", elapsedTime);
// Execution is complete, so we can exit now
dispatch_group_leave(group);
}, ^{
NSTimeInterval elapsedTime = [[NSDate date] timeIntervalSinceDate:startDate];
NSLog(@"Timed out after %.3fs.", elapsedTime);
// Execution is complete, so we can exit now
dispatch_group_leave(group);
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}
return 0;
}
//
// QLCDispatchAsyncWithCompletionAndTimeout.swift
// Blocks-WaitUntilDate
//
// Created by Prachi Gauriar on 8/25/2015.
// Copyright (c) 2015 Prachi Gauriar.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
/// Asynchronously dispatches the specified (primary) block, dispatching the timeout block if the
/// timeout period elapses before the block completes; otherwise dispatches the completion block.
///
/// No guarantee is made about when the completion block or timeout block will be executed. The only
/// guarantees are that the completion block will be dispatched after the primary block finishes
/// executing and that the timeout block will be dispatched after the timeout period elapses. When
/// the blocks are executed depends entirely on the priority of the concurrent queue that the blocks
/// are dispatched on.
///
/// - parameter queue: A concurrent queue on which to dispatch the specified blocks.
/// - parameter timeoutInNanoseconds: The number of nanoseconds to wait before dispatching the
/// timeout block. May not be negative. The timeout period begins when the primary block is
/// dispatched, not when this function is called.
/// - parameter block: The primary block to dispatch asynchronously.
/// - parameter completionBlock: A block that is asynchronously dispatched after the primary block
/// has completed. This block is only dispatched if the primary block finished executing before
/// the timeout period elapsed.
/// - parameter timeoutBlock: A block that is asynchronously dispatched after the the timeout period
/// has elapsed. This block is only dispatched if the primary block did not finish executing
/// before the timeout period elapsed.
func dispatchAsyncToQueue(queue: dispatch_queue_t,
timeoutInNanoseconds timeout: Int64,
block: dispatch_block_t,
completionBlock: dispatch_block_t?,
timeoutBlock: dispatch_block_t?) {
assert(timeout >= 0)
// Asynchronously begin executing the block
dispatch_async(queue) {
let semaphore = dispatch_semaphore_create(0)
let timeoutTime = dispatch_time(DISPATCH_TIME_NOW, timeout)
// Before actually executing the block, dispatch another asynchronous call
// that waits for the block to finish
dispatch_async(queue) {
let timedOut = dispatch_semaphore_wait(semaphore, timeoutTime)
if timedOut > 0 {
if let timeoutBlock = timeoutBlock {
timeoutBlock()
}
} else if let completionBlock = completionBlock {
completionBlock()
}
}
// Execute the block (synchronously) and then signal completion
block()
dispatch_semaphore_signal(semaphore)
}
}
let timeoutInterval: Int64 = 4
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
let group = dispatch_group_create()
dispatch_group_enter(group)
let startDate = NSDate()
dispatchAsyncToQueue(queue, timeoutInNanoseconds: timeoutInterval * Int64(NSEC_PER_SEC), block: {
let sleepTimeInterval = arc4random_uniform(2 * UInt32(timeoutInterval)) + 1
NSLog("Sleeping for \(sleepTimeInterval)s.")
sleep(sleepTimeInterval)
}, completionBlock: {
let elapsedTimeInterval = -startDate.timeIntervalSinceNow
NSLog("Completed after %.3fs.", elapsedTimeInterval)
dispatch_group_leave(group)
}, timeoutBlock: {
let elapsedTimeInterval = -startDate.timeIntervalSinceNow
NSLog("Timed out after %.3fs.", elapsedTimeInterval)
dispatch_group_leave(group)
})
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment