From aa3e25d0999c29ab7b9425c0ce59e10eb87b87bd Mon Sep 17 00:00:00 2001 From: Yasuhiro Inami Date: Mon, 18 May 2015 10:42:04 +0900 Subject: [PATCH 1/2] Add _RecursiveLock. --- SwiftTask.xcodeproj/project.pbxproj | 6 ++ SwiftTask/SwiftTask.swift | 110 +++++++++++++--------------- SwiftTask/_RecursiveLock.swift | 41 +++++++++++ SwiftTask/_StateMachine.swift | 68 ++++++++++++++--- 4 files changed, 156 insertions(+), 69 deletions(-) create mode 100644 SwiftTask/_RecursiveLock.swift diff --git a/SwiftTask.xcodeproj/project.pbxproj b/SwiftTask.xcodeproj/project.pbxproj index 55d2778..fa893d9 100644 --- a/SwiftTask.xcodeproj/project.pbxproj +++ b/SwiftTask.xcodeproj/project.pbxproj @@ -31,6 +31,8 @@ 48511C5B19C17563002FE03C /* RetainCycleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48511C5A19C17563002FE03C /* RetainCycleTests.swift */; }; 485C31F11A1D619A00040DA3 /* TypeInferenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 485C31F01A1D619A00040DA3 /* TypeInferenceTests.swift */; }; 485C31F21A1D619A00040DA3 /* TypeInferenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 485C31F01A1D619A00040DA3 /* TypeInferenceTests.swift */; }; + 487858241B09701F0022E56A /* _RecursiveLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 487858231B09701F0022E56A /* _RecursiveLock.swift */; }; + 487858251B09701F0022E56A /* _RecursiveLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 487858231B09701F0022E56A /* _RecursiveLock.swift */; }; 48A1E8221A366F9C007619EB /* SwiftTask.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F46DED4199EDF1000F97868 /* SwiftTask.framework */; }; 48A1E8231A366FA8007619EB /* SwiftTask.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48CD5A0C19AEE3570042B9F1 /* SwiftTask.framework */; }; 48B58D7B1A6F255E0068E18C /* _StateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48B58D7A1A6F255E0068E18C /* _StateMachine.swift */; }; @@ -60,6 +62,7 @@ 4822F0D019D00ABF00F5F572 /* SwiftTask-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SwiftTask-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 48511C5A19C17563002FE03C /* RetainCycleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RetainCycleTests.swift; sourceTree = ""; }; 485C31F01A1D619A00040DA3 /* TypeInferenceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypeInferenceTests.swift; sourceTree = ""; }; + 487858231B09701F0022E56A /* _RecursiveLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _RecursiveLock.swift; sourceTree = ""; }; 48B58D7A1A6F255E0068E18C /* _StateMachine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _StateMachine.swift; sourceTree = ""; }; 48CD5A0C19AEE3570042B9F1 /* SwiftTask.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftTask.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -129,6 +132,7 @@ 1F46DEFA199EDF8100F97868 /* SwiftTask.swift */, 1FD7197A1AFE387C00BC38C4 /* Cancellable.swift */, 48B58D7A1A6F255E0068E18C /* _StateMachine.swift */, + 487858231B09701F0022E56A /* _RecursiveLock.swift */, 1F46DED7199EDF1000F97868 /* Supporting Files */, ); path = SwiftTask; @@ -345,6 +349,7 @@ 1F46DEFB199EDF8100F97868 /* SwiftTask.swift in Sources */, 1FD7197B1AFE387C00BC38C4 /* Cancellable.swift in Sources */, 48B58D7B1A6F255E0068E18C /* _StateMachine.swift in Sources */, + 487858241B09701F0022E56A /* _RecursiveLock.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -385,6 +390,7 @@ 48CD5A3C19AEEBDF0042B9F1 /* SwiftTask.swift in Sources */, 1FD7197C1AFE387C00BC38C4 /* Cancellable.swift in Sources */, 48B58D7C1A6F255E0068E18C /* _StateMachine.swift in Sources */, + 487858251B09701F0022E56A /* _RecursiveLock.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/SwiftTask/SwiftTask.swift b/SwiftTask/SwiftTask.swift index 503151a..e04810c 100644 --- a/SwiftTask/SwiftTask.swift +++ b/SwiftTask/SwiftTask.swift @@ -637,36 +637,37 @@ extension Task var completedCount = 0 let totalCount = tasks.count + let lock = _RecursiveLock() for task in tasks { task.success { (value: Value) -> Void in - synchronized(self) { - completedCount++ - - let progressTuple = BulkProgress(completedCount: completedCount, totalCount: totalCount) - progress(progressTuple) + lock.lock() + completedCount++ + + let progressTuple = BulkProgress(completedCount: completedCount, totalCount: totalCount) + progress(progressTuple) + + if completedCount == totalCount { + var values: [Value] = Array() - if completedCount == totalCount { - var values: [Value] = Array() - - for task in tasks { - values.append(task.value!) - } - - fulfill(values) + for task in tasks { + values.append(task.value!) } + + fulfill(values) } + lock.unlock() }.failure { (errorInfo: ErrorInfo) -> Void in - synchronized(self) { - _reject(errorInfo) - - for task in tasks { - task.cancel() - } + lock.lock() + _reject(errorInfo) + + for task in tasks { + task.cancel() } + lock.unlock() } } @@ -684,32 +685,33 @@ extension Task var completedCount = 0 var rejectedCount = 0 let totalCount = tasks.count + let lock = _RecursiveLock() for task in tasks { task.success { (value: Value) -> Void in - synchronized(self) { - completedCount++ + lock.lock() + completedCount++ + + if completedCount == 1 { + fulfill(value) - if completedCount == 1 { - fulfill(value) - - self.cancelAll(tasks) - } + self.cancelAll(tasks) } + lock.unlock() }.failure { (errorInfo: ErrorInfo) -> Void in - synchronized(self) { - rejectedCount++ + lock.lock() + rejectedCount++ + + if rejectedCount == totalCount { + var isAnyCancelled = (tasks.filter { task in task.state == .Cancelled }.count > 0) - if rejectedCount == totalCount { - var isAnyCancelled = (tasks.filter { task in task.state == .Cancelled }.count > 0) - - let errorInfo = ErrorInfo(error: nil, isCancelled: isAnyCancelled) // NOTE: Task.any error returns nil (spec) - _reject(errorInfo) - } + let errorInfo = ErrorInfo(error: nil, isCancelled: isAnyCancelled) // NOTE: Task.any error returns nil (spec) + _reject(errorInfo) } + lock.unlock() } } @@ -728,28 +730,29 @@ extension Task var completedCount = 0 let totalCount = tasks.count + let lock = _RecursiveLock() for task in tasks { task.then { (value: Value?, errorInfo: ErrorInfo?) -> Void in - synchronized(self) { - completedCount++ - - let progressTuple = BulkProgress(completedCount: completedCount, totalCount: totalCount) - progress(progressTuple) + lock.lock() + completedCount++ + + let progressTuple = BulkProgress(completedCount: completedCount, totalCount: totalCount) + progress(progressTuple) + + if completedCount == totalCount { + var values: [Value] = Array() - if completedCount == totalCount { - var values: [Value] = Array() - - for task in tasks { - if task.state == .Fulfilled { - values.append(task.value!) - } + for task in tasks { + if task.state == .Fulfilled { + values.append(task.value!) } - - fulfill(values) } + + fulfill(values) } + lock.unlock() } } @@ -795,15 +798,4 @@ infix operator ~ { associativity left } public func ~ (task: Task, tryCount: Int) -> Task { return task.try(tryCount) -} - -//-------------------------------------------------- -// MARK: - Utility -//-------------------------------------------------- - -internal func synchronized(object: AnyObject, closure: Void -> Void) -{ - objc_sync_enter(object) - closure() - objc_sync_exit(object) } \ No newline at end of file diff --git a/SwiftTask/_RecursiveLock.swift b/SwiftTask/_RecursiveLock.swift new file mode 100644 index 0000000..9e418ec --- /dev/null +++ b/SwiftTask/_RecursiveLock.swift @@ -0,0 +1,41 @@ +// +// _RecursiveLock.swift +// SwiftTask +// +// Created by Yasuhiro Inami on 2015/05/18. +// Copyright (c) 2015年 Yasuhiro Inami. All rights reserved. +// + +import Darwin + +internal final class _RecursiveLock +{ + private let mutex: UnsafeMutablePointer + private let attribute: UnsafeMutablePointer + + internal init() + { + self.mutex = UnsafeMutablePointer.alloc(sizeof(pthread_mutex_t)) + self.attribute = UnsafeMutablePointer.alloc(sizeof(pthread_mutexattr_t)) + + pthread_mutexattr_init(self.attribute) + pthread_mutexattr_settype(self.attribute, PTHREAD_MUTEX_RECURSIVE) + pthread_mutex_init(self.mutex, self.attribute) + } + + deinit + { + pthread_mutexattr_destroy(self.attribute) + pthread_mutex_destroy(self.mutex) + } + + internal func lock() + { + pthread_mutex_lock(self.mutex) + } + + internal func unlock() + { + pthread_mutex_unlock(self.mutex) + } +} \ No newline at end of file diff --git a/SwiftTask/_StateMachine.swift b/SwiftTask/_StateMachine.swift index ba1da0e..cfb4841 100644 --- a/SwiftTask/_StateMachine.swift +++ b/SwiftTask/_StateMachine.swift @@ -35,6 +35,8 @@ internal class _StateMachine internal let configuration = TaskConfiguration() + private let _recursiveLock = _RecursiveLock() + internal init(weakified: Bool, paused: Bool) { self.weakified = weakified @@ -43,42 +45,63 @@ internal class _StateMachine internal func addProgressTupleHandler(inout token: _HandlerToken?, _ progressTupleHandler: ProgressTupleHandler) -> Bool { + self._recursiveLock.lock() if self.state == .Running || self.state == .Paused { token = self.progressTupleHandlers.append(progressTupleHandler) + self._recursiveLock.unlock() return token != nil } - return false + else { + self._recursiveLock.unlock() + return false + } } internal func removeProgressTupleHandler(handlerToken: _HandlerToken?) -> Bool { + self._recursiveLock.lock() if let handlerToken = handlerToken { let removedHandler = self.progressTupleHandlers.remove(handlerToken) + self._recursiveLock.unlock() return removedHandler != nil } - return false + else { + self._recursiveLock.unlock() + return false + } } internal func addCompletionHandler(inout token: _HandlerToken?, _ completionHandler: Void -> Void) -> Bool { + self._recursiveLock.lock() if self.state == .Running || self.state == .Paused { token = self.completionHandlers.append(completionHandler) + self._recursiveLock.unlock() return token != nil } - return false + else { + self._recursiveLock.unlock() + return false + } } internal func removeCompletionHandler(handlerToken: _HandlerToken?) -> Bool { + self._recursiveLock.lock() if let handlerToken = handlerToken { let removedHandler = self.completionHandlers.remove(handlerToken) + self._recursiveLock.unlock() return removedHandler != nil } - return false + else { + self._recursiveLock.unlock() + return false + } } internal func handleProgress(progress: Progress) { + self._recursiveLock.lock() if self.state == .Running { let oldProgress = self.progress @@ -91,35 +114,52 @@ internal class _StateMachine for handler in self.progressTupleHandlers { handler(oldProgress: oldProgress, newProgress: progress) } + self._recursiveLock.unlock() + } + else { + self._recursiveLock.unlock() } } internal func handleFulfill(value: Value) { + self._recursiveLock.lock() if self.state == .Running { self.state = .Fulfilled self.value = value - self.finish() + self._finish() + self._recursiveLock.unlock() + } + else { + self._recursiveLock.unlock() } } internal func handleRejectInfo(errorInfo: ErrorInfo) { + self._recursiveLock.lock() if self.state == .Running || self.state == .Paused { self.state = errorInfo.isCancelled ? .Cancelled : .Rejected self.errorInfo = errorInfo - self.finish() + self._finish() + self._recursiveLock.unlock() + } + else { + self._recursiveLock.unlock() } } internal func handlePause() -> Bool { + self._recursiveLock.lock() if self.state == .Running { self.configuration.pause?() self.state = .Paused + self._recursiveLock.unlock() return true } else { + self._recursiveLock.unlock() return false } } @@ -135,9 +175,15 @@ internal class _StateMachine // which eventually calls upstream's `initResumeClosure` // and thus upstream starts sending values. // + + self._recursiveLock.lock() + self._handleInitResumeIfNeeded() + let resumed = _handleResume() - return _handleResume() + self._recursiveLock.unlock() + + return resumed } /// @@ -151,7 +197,6 @@ internal class _StateMachine if (self.initResumeClosure != nil) { let isInitPaused = (self.state == .Paused) - if isInitPaused { self.state = .Running // switch `.Paused` => `.Resume` temporarily without invoking `configure.resume()` } @@ -182,18 +227,21 @@ internal class _StateMachine internal func handleCancel(error: Error? = nil) -> Bool { + self._recursiveLock.lock() if self.state == .Running || self.state == .Paused { self.state = .Cancelled self.errorInfo = ErrorInfo(error: error, isCancelled: true) - self.finish() + self._finish() + self._recursiveLock.unlock() return true } else { + self._recursiveLock.unlock() return false } } - internal func finish() + private func _finish() { for handler in self.completionHandlers { handler() From c210b51455cf553c95a83faaa9bec7242683faee Mon Sep 17 00:00:00 2001 From: Yasuhiro Inami Date: Mon, 18 May 2015 23:04:34 +0900 Subject: [PATCH 2/2] Add _Atomic. --- SwiftTask.xcodeproj/project.pbxproj | 6 +++ SwiftTask/SwiftTask.swift | 22 ++++---- SwiftTask/_Atomic.swift | 47 +++++++++++++++++ SwiftTask/_StateMachine.swift | 80 ++++++++++++++--------------- 4 files changed, 104 insertions(+), 51 deletions(-) create mode 100644 SwiftTask/_Atomic.swift diff --git a/SwiftTask.xcodeproj/project.pbxproj b/SwiftTask.xcodeproj/project.pbxproj index fa893d9..37639a7 100644 --- a/SwiftTask.xcodeproj/project.pbxproj +++ b/SwiftTask.xcodeproj/project.pbxproj @@ -16,6 +16,8 @@ 1F4C76A41AD8CF40004E47C1 /* AlamofireTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F5FA35619A374E600975FB9 /* AlamofireTests.swift */; }; 1F4C76A51AD8CF41004E47C1 /* AlamofireTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F5FA35619A374E600975FB9 /* AlamofireTests.swift */; }; 1F6A8CA319A4E4F200369A5D /* SwiftTaskTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F46DEE3199EDF1000F97868 /* SwiftTaskTests.swift */; }; + 1FB3B8F31B0A194F00F78900 /* _Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB3B8F21B0A194F00F78900 /* _Atomic.swift */; }; + 1FB3B8F41B0A194F00F78900 /* _Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB3B8F21B0A194F00F78900 /* _Atomic.swift */; }; 1FCF71121AD8CD2B007079C2 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FCF71111AD8CD2B007079C2 /* Alamofire.framework */; }; 1FCF71141AD8CD2F007079C2 /* Async.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FCF71131AD8CD2F007079C2 /* Async.framework */; }; 1FCF71161AD8CD38007079C2 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FCF71151AD8CD38007079C2 /* Alamofire.framework */; }; @@ -53,6 +55,7 @@ 1F46DEFA199EDF8100F97868 /* SwiftTask.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftTask.swift; sourceTree = ""; }; 1F46DEFC199EE2C200F97868 /* _TestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _TestCase.swift; sourceTree = ""; }; 1F5FA35619A374E600975FB9 /* AlamofireTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlamofireTests.swift; sourceTree = ""; }; + 1FB3B8F21B0A194F00F78900 /* _Atomic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _Atomic.swift; sourceTree = ""; }; 1FCF71111AD8CD2B007079C2 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = "../Carthage/Checkouts/Alamofire/build/Debug-iphoneos/Alamofire.framework"; sourceTree = ""; }; 1FCF71131AD8CD2F007079C2 /* Async.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Async.framework; path = "../Carthage/Checkouts/Async/build/Debug-iphoneos/Async.framework"; sourceTree = ""; }; 1FCF71151AD8CD38007079C2 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = ../Carthage/Checkouts/Alamofire/build/Debug/Alamofire.framework; sourceTree = ""; }; @@ -133,6 +136,7 @@ 1FD7197A1AFE387C00BC38C4 /* Cancellable.swift */, 48B58D7A1A6F255E0068E18C /* _StateMachine.swift */, 487858231B09701F0022E56A /* _RecursiveLock.swift */, + 1FB3B8F21B0A194F00F78900 /* _Atomic.swift */, 1F46DED7199EDF1000F97868 /* Supporting Files */, ); path = SwiftTask; @@ -349,6 +353,7 @@ 1F46DEFB199EDF8100F97868 /* SwiftTask.swift in Sources */, 1FD7197B1AFE387C00BC38C4 /* Cancellable.swift in Sources */, 48B58D7B1A6F255E0068E18C /* _StateMachine.swift in Sources */, + 1FB3B8F31B0A194F00F78900 /* _Atomic.swift in Sources */, 487858241B09701F0022E56A /* _RecursiveLock.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -390,6 +395,7 @@ 48CD5A3C19AEEBDF0042B9F1 /* SwiftTask.swift in Sources */, 1FD7197C1AFE387C00BC38C4 /* Cancellable.swift in Sources */, 48B58D7C1A6F255E0068E18C /* _StateMachine.swift in Sources */, + 1FB3B8F41B0A194F00F78900 /* _Atomic.swift in Sources */, 487858251B09701F0022E56A /* _RecursiveLock.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/SwiftTask/SwiftTask.swift b/SwiftTask/SwiftTask.swift index e04810c..63ba4bb 100644 --- a/SwiftTask/SwiftTask.swift +++ b/SwiftTask/SwiftTask.swift @@ -84,16 +84,16 @@ public class Task: Cancellable, Printable internal let _paused: Bool internal var _initClosure: _InitClosure! // retained throughout task's lifetime - public var state: TaskState { return self._machine.state } + public var state: TaskState { return self._machine.state.rawValue } /// progress value (NOTE: always nil when `weakified = true`) - public var progress: Progress? { return self._machine.progress } + public var progress: Progress? { return self._machine.progress.rawValue } /// fulfilled value - public var value: Value? { return self._machine.value } + public var value: Value? { return self._machine.value.rawValue } /// rejected/cancelled tuple info - public var errorInfo: ErrorInfo? { return self._machine.errorInfo } + public var errorInfo: ErrorInfo? { return self._machine.errorInfo.rawValue } public var name: String = "DefaultTask" @@ -424,7 +424,7 @@ public class Task: Cancellable, Printable let selfMachine = self._machine self._then(&canceller) { - let innerTask = thenClosure(selfMachine.value, selfMachine.errorInfo) + let innerTask = thenClosure(selfMachine.value.rawValue, selfMachine.errorInfo.rawValue) _bindInnerTask(innerTask, newMachine, progress, fulfill, _reject, configure) } @@ -484,11 +484,11 @@ public class Task: Cancellable, Printable // NOTE: using `self._then()` + `selfMachine` instead of `self.then()` will reduce Task allocation self._then(&canceller) { - if let value = selfMachine.value { + if let value = selfMachine.value.rawValue { let innerTask = successClosure(value) _bindInnerTask(innerTask, newMachine, progress, fulfill, _reject, configure) } - else if let errorInfo = selfMachine.errorInfo { + else if let errorInfo = selfMachine.errorInfo.rawValue { _reject(errorInfo) } } @@ -534,10 +534,10 @@ public class Task: Cancellable, Printable let selfMachine = self._machine self._then(&canceller) { - if let value = selfMachine.value { + if let value = selfMachine.value.rawValue { fulfill(value) } - else if let errorInfo = selfMachine.errorInfo { + else if let errorInfo = selfMachine.errorInfo.rawValue { let innerTask = failureClosure(errorInfo) _bindInnerTask(innerTask, newMachine, progress, fulfill, _reject, configure) } @@ -617,10 +617,10 @@ internal func _bindInnerTask( configure.cancel = { innerTask.cancel(); return } // pause/cancel innerTask if descendant task is already paused/cancelled - if newMachine.state == .Paused { + if newMachine.state.rawValue == .Paused { innerTask.pause() } - else if newMachine.state == .Cancelled { + else if newMachine.state.rawValue == .Cancelled { innerTask.cancel() } } diff --git a/SwiftTask/_Atomic.swift b/SwiftTask/_Atomic.swift new file mode 100644 index 0000000..d3dd889 --- /dev/null +++ b/SwiftTask/_Atomic.swift @@ -0,0 +1,47 @@ +// +// _Atomic.swift +// SwiftTask +// +// Created by Yasuhiro Inami on 2015/05/18. +// Copyright (c) 2015年 Yasuhiro Inami. All rights reserved. +// + +import Darwin + +internal final class _Atomic +{ + private var spinlock = OS_SPINLOCK_INIT + private var _rawValue: T + + internal var rawValue: T + { + get { + lock() + let rawValue = self._rawValue + unlock() + + return rawValue + } + + set(newValue) { + lock() + self._rawValue = newValue + unlock() + } + } + + init(_ rawValue: T) + { + self._rawValue = rawValue + } + + private func lock() + { + withUnsafeMutablePointer(&self.spinlock, OSSpinLockLock) + } + + private func unlock() + { + withUnsafeMutablePointer(&self.spinlock, OSSpinLockUnlock) + } +} \ No newline at end of file diff --git a/SwiftTask/_StateMachine.swift b/SwiftTask/_StateMachine.swift index cfb4841..c5c6dd8 100644 --- a/SwiftTask/_StateMachine.swift +++ b/SwiftTask/_StateMachine.swift @@ -20,34 +20,34 @@ internal class _StateMachine internal typealias ProgressTupleHandler = Task._ProgressTupleHandler internal let weakified: Bool - internal private(set) var state: TaskState + internal let state: _Atomic - internal private(set) var progress: Progress? // NOTE: always nil if `weakified = true` - internal private(set) var value: Value? - internal private(set) var errorInfo: ErrorInfo? + internal let progress: _Atomic = _Atomic(nil) // NOTE: always nil if `weakified = true` + internal let value: _Atomic = _Atomic(nil) + internal let errorInfo: _Atomic = _Atomic(nil) + + internal let configuration = TaskConfiguration() /// wrapper closure for `_initClosure` to invoke only once when started `.Running`, /// and will be set to `nil` afterward internal var initResumeClosure: (Void -> Void)? - internal private(set) lazy var progressTupleHandlers = _Handlers() - internal private(set) lazy var completionHandlers = _Handlers Void>() - - internal let configuration = TaskConfiguration() + private lazy var _progressTupleHandlers = _Handlers() + private lazy var _completionHandlers = _Handlers Void>() private let _recursiveLock = _RecursiveLock() internal init(weakified: Bool, paused: Bool) { self.weakified = weakified - self.state = paused ? .Paused : .Running + self.state = _Atomic(paused ? .Paused : .Running) } internal func addProgressTupleHandler(inout token: _HandlerToken?, _ progressTupleHandler: ProgressTupleHandler) -> Bool { self._recursiveLock.lock() - if self.state == .Running || self.state == .Paused { - token = self.progressTupleHandlers.append(progressTupleHandler) + if self.state.rawValue == .Running || self.state.rawValue == .Paused { + token = self._progressTupleHandlers.append(progressTupleHandler) self._recursiveLock.unlock() return token != nil } @@ -61,7 +61,7 @@ internal class _StateMachine { self._recursiveLock.lock() if let handlerToken = handlerToken { - let removedHandler = self.progressTupleHandlers.remove(handlerToken) + let removedHandler = self._progressTupleHandlers.remove(handlerToken) self._recursiveLock.unlock() return removedHandler != nil } @@ -74,8 +74,8 @@ internal class _StateMachine internal func addCompletionHandler(inout token: _HandlerToken?, _ completionHandler: Void -> Void) -> Bool { self._recursiveLock.lock() - if self.state == .Running || self.state == .Paused { - token = self.completionHandlers.append(completionHandler) + if self.state.rawValue == .Running || self.state.rawValue == .Paused { + token = self._completionHandlers.append(completionHandler) self._recursiveLock.unlock() return token != nil } @@ -89,7 +89,7 @@ internal class _StateMachine { self._recursiveLock.lock() if let handlerToken = handlerToken { - let removedHandler = self.completionHandlers.remove(handlerToken) + let removedHandler = self._completionHandlers.remove(handlerToken) self._recursiveLock.unlock() return removedHandler != nil } @@ -102,16 +102,16 @@ internal class _StateMachine internal func handleProgress(progress: Progress) { self._recursiveLock.lock() - if self.state == .Running { + if self.state.rawValue == .Running { - let oldProgress = self.progress + let oldProgress = self.progress.rawValue // NOTE: if `weakified = false`, don't store progressValue for less memory footprint if !self.weakified { - self.progress = progress + self.progress.rawValue = progress } - for handler in self.progressTupleHandlers { + for handler in self._progressTupleHandlers { handler(oldProgress: oldProgress, newProgress: progress) } self._recursiveLock.unlock() @@ -124,9 +124,9 @@ internal class _StateMachine internal func handleFulfill(value: Value) { self._recursiveLock.lock() - if self.state == .Running { - self.state = .Fulfilled - self.value = value + if self.state.rawValue == .Running { + self.state.rawValue = .Fulfilled + self.value.rawValue = value self._finish() self._recursiveLock.unlock() } @@ -138,9 +138,9 @@ internal class _StateMachine internal func handleRejectInfo(errorInfo: ErrorInfo) { self._recursiveLock.lock() - if self.state == .Running || self.state == .Paused { - self.state = errorInfo.isCancelled ? .Cancelled : .Rejected - self.errorInfo = errorInfo + if self.state.rawValue == .Running || self.state.rawValue == .Paused { + self.state.rawValue = errorInfo.isCancelled ? .Cancelled : .Rejected + self.errorInfo.rawValue = errorInfo self._finish() self._recursiveLock.unlock() } @@ -152,9 +152,9 @@ internal class _StateMachine internal func handlePause() -> Bool { self._recursiveLock.lock() - if self.state == .Running { + if self.state.rawValue == .Running { self.configuration.pause?() - self.state = .Paused + self.state.rawValue = .Paused self._recursiveLock.unlock() return true } @@ -196,9 +196,9 @@ internal class _StateMachine { if (self.initResumeClosure != nil) { - let isInitPaused = (self.state == .Paused) + let isInitPaused = (self.state.rawValue == .Paused) if isInitPaused { - self.state = .Running // switch `.Paused` => `.Resume` temporarily without invoking `configure.resume()` + self.state.rawValue = .Running // switch `.Paused` => `.Resume` temporarily without invoking `configure.resume()` } // NOTE: performing `initResumeClosure` might change `state` to `.Fulfilled` or `.Rejected` **immediately** @@ -207,17 +207,17 @@ internal class _StateMachine // switch back to `.Paused` if temporary `.Running` has not changed // so that consecutive `_handleResume()` can perform `configure.resume()` - if isInitPaused && self.state == .Running { - self.state = .Paused + if isInitPaused && self.state.rawValue == .Running { + self.state.rawValue = .Paused } } } private func _handleResume() -> Bool { - if self.state == .Paused { + if self.state.rawValue == .Paused { self.configuration.resume?() - self.state = .Running + self.state.rawValue = .Running return true } else { @@ -228,9 +228,9 @@ internal class _StateMachine internal func handleCancel(error: Error? = nil) -> Bool { self._recursiveLock.lock() - if self.state == .Running || self.state == .Paused { - self.state = .Cancelled - self.errorInfo = ErrorInfo(error: error, isCancelled: true) + if self.state.rawValue == .Running || self.state.rawValue == .Paused { + self.state.rawValue = .Cancelled + self.errorInfo.rawValue = ErrorInfo(error: error, isCancelled: true) self._finish() self._recursiveLock.unlock() return true @@ -243,17 +243,17 @@ internal class _StateMachine private func _finish() { - for handler in self.completionHandlers { + for handler in self._completionHandlers { handler() } - self.progressTupleHandlers.removeAll() - self.completionHandlers.removeAll() + self._progressTupleHandlers.removeAll() + self._completionHandlers.removeAll() self.configuration.finish() self.initResumeClosure = nil - self.progress = nil + self.progress.rawValue = nil } }