Skip to content

Instantly share code, notes, and snippets.

@amw
Last active November 4, 2016 22:58
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save amw/9d62f247d0de3fa08e5facc59bc91037 to your computer and use it in GitHub Desktop.
Save amw/9d62f247d0de3fa08e5facc59bc91037 to your computer and use it in GitHub Desktop.
Swift wrapper for Grand Central Dispatch (GCD)
//
// dispatch.swift
//
// Created by Adam Wróbel. Read more at:
// http://adamwrobel.com/blog/2016/05/08/swift-gcd-wrapper/
//
import Foundation
internal extension dispatch_queue_t {
/// Submits a block for asynchronous execution on this queue and returns
/// immediately.
final func async(block: dispatch_block_t) {
dispatch_async(self, block)
}
/// Submits a block for asynchronous execution on this queue and associates
/// the block with given group.
final func async(group: dispatch_group_t, block: dispatch_block_t) {
dispatch_group_async(group, self, block)
}
/// Submits a block object for execution on this queue and waits until that
/// block completes.
///
/// Calling this method on the current queue results in deadlock.
final func sync(block: dispatch_block_t) {
dispatch_sync(self, block)
}
/// Submits a block object for execution on this queue and waits until that
/// block completes.
///
/// Prevents deadlock by executing the block immediately if called on the
/// current queue. Performing this safety check adds a little overhead when
/// compared with `sync`.
final func safeSync(block: dispatch_block_t) {
isCurrent() ? block() : sync(block)
}
/// Enqueue a block for execution after specified number of seconds.
final func after(seconds: Double, block: dispatch_block_t) {
let when = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * 1_000_000_000))
dispatch_after(when, self, block)
}
/// Asynchronously runs a block on this queue when the group's tasks are
/// complete.
final func after(group: dispatch_group_t, block: dispatch_block_t) {
group.notify(self, block: block)
}
}
internal extension dispatch_group_t {
/// Waits until all of the group's tasks are complete.
final func wait() {
dispatch_group_wait(self, DISPATCH_TIME_FOREVER)
}
/// Asynchronously runs a block on the main queue when the group's tasks are
/// complete.
final func notify(block: dispatch_block_t) {
dispatch_group_notify(self, dispatch.main, block)
}
/// Asynchronously runs a block on the given queue when the group's tasks are
/// complete.
final func notify(queue: dispatch_queue_t, block: dispatch_block_t) {
dispatch_group_notify(self, queue, block)
}
}
final internal class dispatch {
/// Serial queue associated with the application’s main thread.
class var main: dispatch_queue_t {
return dispatch_get_main_queue()
}
/// Parallel queue used for work that is not user initiated or visible.
class var bg: dispatch_queue_t {
return dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)
}
/// Parallel queue used for work which the user is unlikely to be immediately
/// waiting for the results.
class var utility: dispatch_queue_t {
return dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)
}
/// Parallel queue used for work that has been explicitly requested by the
/// user, and for which results must be immediately presented in order to
/// allow for further user interaction.
class var user: dispatch_queue_t {
return dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)
}
/// Parallel queue used for work directly involved in providing an
/// interactive UI.
class var interactive: dispatch_queue_t {
return dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0)
}
/// Creates a new group with which block objects can be associated.
class func createGroup() -> dispatch_group_t {
return dispatch_group_create()
}
}
// Private extensions used exclusively to support `safeSync`
private extension dispatch {
static var identityKey = UnsafePointer<Int8>(bitPattern: 1234)
class var currentIdentity: UnsafeMutablePointer<Void> {
return dispatch_get_specific(identityKey)
}
}
private extension dispatch_queue_t {
/// Uses queue specific context to determine if we're executing on this queue.
func isCurrent() -> Bool {
var ourIdentity = identity
if ourIdentity == nil {
ourIdentity = UnsafeMutablePointer<Void>(label)
dispatch_queue_set_specific(self, dispatch.identityKey, ourIdentity, nil)
}
return ourIdentity == dispatch.currentIdentity
}
var label: UnsafePointer<Int8> {
return dispatch_queue_get_label(self)
}
var identity: UnsafeMutablePointer<Void> {
return dispatch_queue_get_specific(self, dispatch.identityKey)
}
}
// this will wait for completion and will deadlock when called from main queue
dispatch.interactive.sync {
print("hello from main queue")
}
// this will return immediately
dispatch.utility.async {
print("working with the Utility QoS")
}
// a safe sync operation that can be sent from any queue to any queue
dispatch.main.safeSync {
print("safety in mind")
}
// this schedules a delayed block
dispatch.main.after(2.5) {
printWithTime("hello from main after 2.5 seconds")
}
let group = dispatch.createGroup()
// schedule some asynchronous tasks
dispatch.interactive.async(group) {
print("part of the job")
}
dispatch.user.async(group) {
print("another part of the job")
}
// schedule asynchronous finalizer
group.notify {
print("this will be executed when all of the group tasks complete")
}
// or synchronously wait for the group to complete
group.wait()
print("group completed")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment