Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 17 additions & 15 deletions DispatchGroup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,49 @@
import CoreGraphics
import Dispatch

public typealias Group = DispatchGroup

public class DispatchGroup {
open class DispatcherGroup {

public init (_ tasks: Int = 0) {
for _ in 0..<tasks { ++self }
}

public private(set) var tasks = 0
open fileprivate(set) var tasks = 0

public let dispatch_group = dispatch_group_create()
open let dispatch_group = DispatchGroup()

public func done (callback: Void -> Void) {
dispatch_group_notify(dispatch_group, gcd.current.dispatch_queue, callback)
open func done (_ callback: @escaping (Void) -> Void) {
guard let current = gcd.current else {
return
}

dispatch_group.notify(queue: current.dispatch_queue, execute: callback)
}

public func wait (delay: CGFloat, _ callback: Void -> Void) {
dispatch_group_wait(dispatch_group, dispatch_time(DISPATCH_TIME_NOW, Int64(delay * CGFloat(NSEC_PER_SEC))))
open func wait (_ delay: CGFloat, _ callback: (Void) -> Void) {
let _ = dispatch_group.wait(timeout: DispatchTime.now() + Double(Int64(delay * CGFloat(NSEC_PER_SEC))) / Double(NSEC_PER_SEC))
}

deinit { assert(tasks == 0, "A DispatchGroup cannot be deallocated when tasks is greater than zero!") }
}

public prefix func ++ (group: DispatchGroup) {
public prefix func ++ (group: DispatcherGroup) {
objc_sync_enter(group)
group.tasks += 1
dispatch_group_enter(group.dispatch_group)
group.dispatch_group.enter()
objc_sync_exit(group)
}

public prefix func -- (group: DispatchGroup) {
public prefix func -- (group: DispatcherGroup) {
objc_sync_enter(group)
group.tasks -= 1
dispatch_group_leave(group.dispatch_group)
group.dispatch_group.leave()
objc_sync_exit(group)
}

public postfix func ++ (group: DispatchGroup) {
public postfix func ++ (group: DispatcherGroup) {
++group
}

public postfix func -- (group: DispatchGroup) {
public postfix func -- (group: DispatcherGroup) {
--group
}
64 changes: 38 additions & 26 deletions DispatchQueue.swift
Original file line number Diff line number Diff line change
@@ -1,70 +1,82 @@

import Foundation

public typealias Queue = DispatchQueue

public class DispatchQueue {
open class DispatcherQueue {

private static let Label = "com.mobilenatives.dispatcher"

// MARK: Public

public let isConcurrent: Bool
open let isConcurrent: Bool

public var isCurrent: Bool { return dispatch_get_specific(&kCurrentQueue) == getMutablePointer(self) }
open var isCurrent: Bool {
return DispatchQueue.getSpecific(key: kCurrentQueue) == getMutablePointer(self)
}

public func async (callback: Void -> Void) {
dispatch_async(dispatch_queue) { callback() }
open func async (_ callback: @escaping (Void) -> Void) {
dispatch_queue.async(execute: callback)
}

public func sync (callback: Void -> Void) {
open func sync (_ callback: (Void) -> Void) {
if isCurrent { callback(); return } // prevent deadlocks!
dispatch_sync(dispatch_queue) { callback() }
dispatch_queue.sync(execute: callback)
}

public func async <T> (callback: T -> Void) -> T -> Void {
open func async <T> (_ callback: @escaping (T) -> Void) -> (T) -> Void {
return { [weak self] value in
if self == nil { return }
self!.async { callback(value) }
guard let strongSelf = self else { return }
strongSelf.async { callback(value) }
}
}

public func sync <T> (callback: T -> Void) -> T -> Void {
open func sync <T> (_ callback: @escaping (T) -> Void) -> (T) -> Void {
return { [weak self] value in
if self == nil { return }
self!.sync { callback(value) }
guard let strongSelf = self else { return }
strongSelf.sync { callback(value) }
}
}

public let dispatch_queue: dispatch_queue_t
open let dispatch_queue: DispatchQueue



// MARK: Internal

init (_ queue: dispatch_queue_t) {
init (queue: DispatchQueue) {
isConcurrent = false
dispatch_queue = queue
remember()
}
init (_ priority: dispatch_queue_priority_t) {

init (qos: DispatchQoS.QoSClass) {
isConcurrent = true
dispatch_queue = dispatch_get_global_queue(priority, 0)
dispatch_queue = DispatchQueue.global(qos: qos)
remember()
}

init (_ concurrent: Bool) {
init (concurrent: Bool) {
isConcurrent = concurrent
dispatch_queue = dispatch_queue_create(nil, isConcurrent ? DISPATCH_QUEUE_CONCURRENT : DISPATCH_QUEUE_SERIAL)

// https://bugs.swift.org/browse/SR-1859
if #available(iOS 10.0, *) {
dispatch_queue = DispatchQueue(label: DispatcherQueue.Label, attributes: isConcurrent ? [DispatchQueue.Attributes.concurrent, DispatchQueue.Attributes.initiallyInactive] : [DispatchQueue.Attributes.initiallyInactive])
} else {
dispatch_queue = isConcurrent ? DispatchQueue(label: DispatcherQueue.Label, attributes: [DispatchQueue.Attributes.concurrent]) : DispatchQueue(label: DispatcherQueue.Label)
}
remember()
}

func remember () {
dispatch_queue_set_specific(dispatch_queue, &kCurrentQueue, getMutablePointer(self), nil)
guard let mutablePointer = getMutablePointer(self) else {
return
}

dispatch_queue.setSpecific(key: kCurrentQueue, value: mutablePointer)
}
}

var kCurrentQueue = 0
var kCurrentQueue = DispatchSpecificKey<UnsafeMutableRawPointer>()

func getMutablePointer (object: AnyObject) -> UnsafeMutablePointer<Void> {
return UnsafeMutablePointer<Void>(bitPattern: Int(ObjectIdentifier(object).uintValue))
func getMutablePointer (_ object: AnyObject) -> UnsafeMutableRawPointer? {
return UnsafeMutableRawPointer(bitPattern: Int(bitPattern: ObjectIdentifier(object)))
}
52 changes: 29 additions & 23 deletions DispatchTimer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@
import Foundation
import UIKit

public typealias Timer = DispatchTimer
open class DispatcherTimer {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this dangerous? Can it collide with NSTimer -> Timer?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i can remove it all together but then we'll break the public API of the framework. i don't think it matters actually

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i removed this, Group and Dispatch typealiases


public class DispatchTimer {

public convenience init (_ delay: CGFloat, _ callback: Void -> Void) {
public convenience init (_ delay: CGFloat, _ callback: @escaping (Void) -> Void) {
self.init(delay, 0, callback)
}

public init (_ delay: CGFloat, _ tolerance: CGFloat, _ callback: Void -> Void) {
public init (_ delay: CGFloat, _ tolerance: CGFloat, _ callback: @escaping (Void) -> Void) {
self.callback = callback
self.tolerance = tolerance

Expand All @@ -20,57 +18,65 @@ public class DispatchTimer {
}

self.callbackQueue = gcd.current
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue.dispatch_queue)
self.timer = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags(rawValue: 0), queue: queue.dispatch_queue)

if !gcd.main.isCurrent { dispatch_set_target_queue(queue.dispatch_queue, gcd.current.dispatch_queue) }
if !gcd.main.isCurrent {
if let dispatch_queue = gcd.current?.dispatch_queue {
queue.dispatch_queue.setTarget(queue: dispatch_queue)
}
}

let delay_ns = delay * CGFloat(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay_ns))
dispatch_source_set_timer(timer, time, UInt64(delay_ns), UInt64(tolerance * CGFloat(NSEC_PER_SEC)))
dispatch_source_set_event_handler(timer) { [weak self] in let _ = self?.fire() }
dispatch_resume(timer)
let time = DispatchTime.now() + Double(delay)

let interval = DispatchTimeInterval.nanoseconds(Int(delay_ns))
let leeway = DispatchTimeInterval.nanoseconds(Int(UInt64(tolerance * CGFloat(NSEC_PER_SEC))))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we can avoid double converting to UInt64 and Int

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we sure that tolerance * CGFloat(NSEC_PER_SEC) will always be positive?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're converting to UInt64 anyway


timer?.scheduleRepeating(deadline: time, interval: interval, leeway: leeway)
timer?.setEventHandler { [weak self] in let _ = self?.fire() }
timer?.resume()
}

// MARK: Read-only

public let tolerance: CGFloat
open let tolerance: CGFloat

public let callback: Void -> Void
open let callback: (Void) -> Void

// MARK: Instance methods

public func doRepeat (times: UInt! = nil) {
open func doRepeat (_ times: UInt! = nil) {
isRepeating = true
repeatsLeft = times != nil ? Int(times) : -1
}

public func autorelease () {
open func autorelease () {
isAutoReleased = true
autoReleasedTimers[ObjectIdentifier(self)] = self
}

public func fire () {
open func fire () {
if OSAtomicAnd32OrigBarrier(1, &invalidated) == 1 { return }
callbackQueue.sync(callback)
callbackQueue?.sync(callback)
if isRepeating && repeatsLeft > 0 {
repeatsLeft -= 1
}
if !isRepeating || repeatsLeft == 0 { stop() }
}

public func stop () {
open func stop () {
if OSAtomicTestAndSetBarrier(7, &invalidated) { return }
queue.sync({dispatch_source_cancel(self.timer)})
queue.sync({self.timer?.cancel()})
if isAutoReleased { autoReleasedTimers[ObjectIdentifier(self)] = nil }
}

// MARK: Internal

var timer: dispatch_source_t!
var timer: DispatchSourceTimer?

let queue: DispatchQueue = gcd.serial()
let queue: DispatcherQueue = gcd.serial()

var callbackQueue: DispatchQueue!
var callbackQueue: DispatcherQueue?

var invalidated: UInt32 = 0

Expand All @@ -85,4 +91,4 @@ public class DispatchTimer {
}
}

var autoReleasedTimers = [ObjectIdentifier:Timer]()
var autoReleasedTimers = [ObjectIdentifier:DispatcherTimer]()
30 changes: 17 additions & 13 deletions Dispatcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,31 @@ import Dispatch

public let gcd = Dispatcher()

public class Dispatcher : DispatchQueue {
public class Dispatcher : DispatcherQueue {

public var current: DispatchQueue {
return Unmanaged<DispatchQueue>.fromOpaque(COpaquePointer(dispatch_get_specific(&kCurrentQueue))).takeUnretainedValue()
public var current: DispatcherQueue? {
guard let unsafeRawPoiner = DispatchQueue.getSpecific(key: kCurrentQueue) else {
return nil
}

return Unmanaged<DispatcherQueue>.fromOpaque(unsafeRawPoiner).takeUnretainedValue()
}

public let main = DispatchQueue(dispatch_get_main_queue())
public let main = DispatcherQueue(queue: DispatchQueue.main)

public let high = DispatchQueue(DISPATCH_QUEUE_PRIORITY_HIGH)
public let high = DispatcherQueue(qos: .userInitiated)

public let low = DispatchQueue(DISPATCH_QUEUE_PRIORITY_LOW)
public let low = DispatcherQueue(qos: .utility)

public let background = DispatchQueue(DISPATCH_QUEUE_PRIORITY_BACKGROUND)
public let background = DispatcherQueue(qos: .background)

public func serial () -> DispatchQueue {
return DispatchQueue(false)
public func serial () -> DispatcherQueue {
return DispatcherQueue(concurrent: false)
}

public func concurrent () -> DispatchQueue {
return DispatchQueue(true)
public func concurrent () -> DispatcherQueue {
return DispatcherQueue(concurrent: true)
}

init () { super.init(DISPATCH_QUEUE_PRIORITY_DEFAULT) }
}
init () { super.init(qos: .default) }
}
12 changes: 6 additions & 6 deletions Dispatcher.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,11 @@
TargetAttributes = {
1123A3A619F1404E00BF890F = {
CreatedOnToolsVersion = 6.0;
LastSwiftMigration = 0800;
LastSwiftMigration = 0810;
};
1123A3B019F1404F00BF890F = {
CreatedOnToolsVersion = 6.0;
LastSwiftMigration = 0800;
LastSwiftMigration = 0810;
};
};
};
Expand Down Expand Up @@ -373,7 +373,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 2.3;
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
Expand All @@ -396,7 +396,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "larson.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 2.3;
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
Expand All @@ -418,7 +418,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "larson.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 2.3;
SWIFT_VERSION = 3.0;
};
name = Debug;
};
Expand All @@ -433,7 +433,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "larson.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 2.3;
SWIFT_VERSION = 3.0;
};
name = Release;
};
Expand Down
Loading