From e78dd191e31e0c93495d93ed21351460d1355d2a Mon Sep 17 00:00:00 2001 From: Zachary Waldowski Date: Tue, 31 Oct 2017 00:43:38 -0400 Subject: [PATCH 1/3] Fix annotation on C function --- Sources/Atomics/include/Atomics.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Atomics/include/Atomics.h b/Sources/Atomics/include/Atomics.h index ba11497e..d19f05c0 100755 --- a/Sources/Atomics/include/Atomics.h +++ b/Sources/Atomics/include/Atomics.h @@ -159,7 +159,7 @@ static inline uint_fast8_t bnr_atomic_bitmask_and(volatile bnr_atomic_bitmask_t } OS_ALWAYS_INLINE -static inline bool bnr_atomic_bitmask_test(const bnr_atomic_bitmask_t *_Nonnull target, uint_fast8_t value) { +static inline bool bnr_atomic_bitmask_test(volatile bnr_atomic_bitmask_t *_Nonnull target, uint_fast8_t value) { return (__c11_atomic_load((_Atomic(uint_fast8_t) *)&target->value, __ATOMIC_RELAXED) & value) != 0; } From ae3e20a9031c610efcab0bd18e096a83765cd661 Mon Sep 17 00:00:00 2001 From: Zachary Waldowski Date: Thu, 14 Sep 2017 10:24:56 -0700 Subject: [PATCH 2/3] Convenience initializer to create Task from future that cannot fail --- Sources/Task/ExistentialTask.swift | 25 +++++++++++++++++-- Sources/Task/ResultFuture.swift | 12 +++++++-- Sources/Task/TaskCollections.swift | 2 +- Sources/Task/TaskProgress.swift | 40 +++++++++++++++++------------- 4 files changed, 57 insertions(+), 22 deletions(-) diff --git a/Sources/Task/ExistentialTask.swift b/Sources/Task/ExistentialTask.swift index 67f7e4f6..23bedf08 100644 --- a/Sources/Task/ExistentialTask.swift +++ b/Sources/Task/ExistentialTask.swift @@ -61,7 +61,7 @@ public final class Task: NSObject { /// `cancellation` will be called asynchronously, but not on any specific /// queue. If you must do work on a specific queue, schedule work on it. public convenience init(future base: Future, cancellation: (() -> Void)? = nil) { - let progress = Progress.wrapped(base, cancellation: cancellation) + let progress = Progress.wrappingSuccess(of: base, cancellation: cancellation) self.init(future: base, progress: progress) } @@ -70,6 +70,12 @@ public final class Task: NSObject { where Task.Value: Either, Task.Value.Left == Error, Task.Value.Right == SuccessValue { self.init(future: Future(task: base), progress: progress) } + + /// Creates a task whose `upon(_:execute:)` methods use those of `base`. + public convenience init(success base: OtherFuture, progress: Progress) + where OtherFuture.Value == SuccessValue { + self.init(future: Future(success: base), progress: progress) + } #else fileprivate let cancellation: (() -> Void) fileprivate var rawIsCancelled = UnsafeAtomicBool() @@ -150,7 +156,8 @@ extension Task { public convenience init(_ base: OtherFuture, cancellation: (() -> Void)? = nil) where OtherFuture.Value: Either, OtherFuture.Value.Left == Error, OtherFuture.Value.Right == SuccessValue { #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) - self.init(future: Future(task: base), progress: .wrapped(base, cancellation: cancellation)) + let progress = Progress.wrappingSuccess(of: base, cancellation: cancellation) + self.init(future: Future(task: base), progress: progress) #else let asTask = (base as? Task) @@ -165,6 +172,20 @@ extension Task { #endif } + /// Creates a task whose `upon(_:execute:)` methods use those of `base`. + /// + /// `cancellation` will be called asynchronously, but not on any specific + /// queue. If you must do work on a specific queue, schedule work on it. + public convenience init(success base: OtherFuture, cancellation: (() -> Void)? = nil) + where OtherFuture.Value == SuccessValue { +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + let progress = Progress.wrappingCompletion(of: base, cancellation: cancellation) + self.init(future: Future(success: base), progress: progress) +#else + self.init(future: Future(success: base)) +#endif + } + /// Creates an operation that has already completed with `value`. public convenience init(success value: @autoclosure() throws -> SuccessValue) { let future = Future(value: Result(from: value)) diff --git a/Sources/Task/ResultFuture.swift b/Sources/Task/ResultFuture.swift index 815564cf..97c91507 100644 --- a/Sources/Task/ResultFuture.swift +++ b/Sources/Task/ResultFuture.swift @@ -61,10 +61,10 @@ extension FutureProtocol where Value: Either { } } -extension Future where Value: Either { +extension Future where Value: Either, Value.Left == Error { /// Create a future having the same underlying task as `other`. public init(task other: Other) - where Other.Value: Either, Other.Value.Left == Error, Other.Value.Right == Value.Right { + where Other.Value: Either, Other.Value.Left == Value.Left, Other.Value.Right == Value.Right { if let asSelf = other as? Future { self.init(asSelf) } else { @@ -73,4 +73,12 @@ extension Future where Value: Either { }) } } + + /// Create a future having the same underlying task as `other`. + public init(success other: Other) + where Other.Value == Value.Right { + self.init(other.every { + Value(success: $0) + }) + } } diff --git a/Sources/Task/TaskCollections.swift b/Sources/Task/TaskCollections.swift index 3b646300..2077b2a0 100644 --- a/Sources/Task/TaskCollections.swift +++ b/Sources/Task/TaskCollections.swift @@ -43,7 +43,7 @@ extension Collection where Iterator.Element: FutureProtocol, Iterator.Element.Va if let task = future as? Task { progress.adoptChild(task.progress, orphaned: false, pendingUnitCount: 1) } else { - progress.adoptChild(.wrapped(future, cancellation: nil), orphaned: true, pendingUnitCount: 1) + progress.adoptChild(.wrappingSuccess(of: future, cancellation: nil), orphaned: true, pendingUnitCount: 1) } #else if let task = future as? Task { diff --git a/Sources/Task/TaskProgress.swift b/Sources/Task/TaskProgress.swift index 97ecdf4c..aa0672c3 100644 --- a/Sources/Task/TaskProgress.swift +++ b/Sources/Task/TaskProgress.swift @@ -210,8 +210,28 @@ extension Progress { } /// A simple indeterminate progress with a cancellation function. - @nonobjc static func wrapped(_ future: Future, cancellation: (() -> Void)?) -> Progress where Future.Value: Either { - switch (future as? Task, cancellation) { + @nonobjc static func wrappingCompletion(of base: OtherFuture, cancellation: (() -> Void)?) -> Progress { + let progress = Progress(parent: nil, userInfo: nil) + progress.totalUnitCount = base.wait(until: .now()) != nil ? 0 : -1 + + if let cancellation = cancellation { + progress.cancellationHandler = cancellation + } else { + progress.isCancellable = false + } + + base.upon(.global(qos: .utility)) { _ in + progress.totalUnitCount = 1 + progress.completedUnitCount = 1 + } + + return progress + } + + /// A simple indeterminate progress with a cancellation function. + @nonobjc static func wrappingSuccess(of base: OtherTask, cancellation: (() -> Void)?) -> Progress + where OtherTask.Value: Either { + switch (base as? Task, cancellation) { case (let task?, nil): return task.progress case (let task?, let cancellation?): @@ -221,21 +241,7 @@ extension Progress { progress.adoptChild(task.progress, orphaned: false, pendingUnitCount: 1) return progress default: - let progress = Progress(parent: nil, userInfo: nil) - progress.totalUnitCount = future.wait(until: .now()) != nil ? 0 : -1 - - if let cancellation = cancellation { - progress.cancellationHandler = cancellation - } else { - progress.isCancellable = false - } - - future.upon(.global(qos: .utility)) { _ in - progress.totalUnitCount = 1 - progress.completedUnitCount = 1 - } - - return progress + return .wrappingCompletion(of: base, cancellation: cancellation) } } } From 34890ac206c99946aa162d9b5441a99915620b52 Mon Sep 17 00:00:00 2001 From: Zachary Waldowski Date: Thu, 16 Nov 2017 14:49:22 -0500 Subject: [PATCH 3/3] Test coverage for new convenience initializer --- Tests/TaskTests/TaskTests.swift | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Tests/TaskTests/TaskTests.swift b/Tests/TaskTests/TaskTests.swift index eaf1aa6a..39405c05 100755 --- a/Tests/TaskTests/TaskTests.swift +++ b/Tests/TaskTests/TaskTests.swift @@ -290,4 +290,19 @@ class TaskTests: CustomExecutorTestCase { waitForExpectations() assertExecutorCalled(2) } + + func testSimpleFutureCanBeUpgradedToTask() { + let expectation = self.expectation(description: "original future filled") + let deferred = Deferred() + let task = Task(success: deferred, cancellation: nil) + + task.uponSuccess { (value) in + XCTAssertEqual(value, 42) + expectation.fulfill() + } + + deferred.fill(with: 42) + waitForExpectations() + } + }