Skip to content
This repository has been archived by the owner on Aug 29, 2022. It is now read-only.

Commit

Permalink
Merge pull request #265 from bignerdranch/zwaldowski/and-then-what
Browse files Browse the repository at this point in the history
Revamped progress calculation around Task and andThen
  • Loading branch information
zwaldowski authored Nov 13, 2018
2 parents 0cd7e31 + 6fcfd59 commit c8f8cfa
Show file tree
Hide file tree
Showing 17 changed files with 667 additions and 350 deletions.
2 changes: 1 addition & 1 deletion Configurations/Debug.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ ONLY_ACTIVE_ARCH = YES

// Build Options
DEBUG_INFORMATION_FORMAT = dwarf
ENABLE_TESTABILITY = NO
ENABLE_TESTABILITY = YES
VALIDATE_PRODUCT = NO

// C Compiler - Code Generation
Expand Down
42 changes: 31 additions & 11 deletions Deferred.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

12 changes: 11 additions & 1 deletion Deferred.xcodeproj/xcshareddata/xcschemes/Deferred.xcscheme
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1000"
LastUpgradeVersion = "1010"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down Expand Up @@ -28,7 +28,17 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableThreadSanitizer = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES"
shouldUseLaunchSchemeArgsEnv = "YES">
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DB126C9F1E53685300054E95"
BuildableName = "Deferred.framework"
BlueprintName = "Deferred"
ReferencedContainer = "container:Deferred.xcodeproj">
</BuildableReference>
</CodeCoverageTargets>
<Testables>
<TestableReference
skipped = "NO">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1000"
LastUpgradeVersion = "1010"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand All @@ -27,7 +27,18 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableThreadSanitizer = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES"
shouldUseLaunchSchemeArgsEnv = "YES">
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DB126CBB1E53685C00054E95"
BuildableName = "Deferred.framework"
BlueprintName = "MobileDeferred"
ReferencedContainer = "container:Deferred.xcodeproj">
</BuildableReference>
</CodeCoverageTargets>
<Testables>
<TestableReference
skipped = "NO">
Expand Down
13 changes: 12 additions & 1 deletion Deferred.xcodeproj/xcshareddata/xcschemes/NanoDeferred.xcscheme
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1000"
LastUpgradeVersion = "1010"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand All @@ -26,7 +26,18 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES"
shouldUseLaunchSchemeArgsEnv = "YES">
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DB126CF31E53687300054E95"
BuildableName = "Deferred.framework"
BlueprintName = "NanoDeferred"
ReferencedContainer = "container:Deferred.xcodeproj">
</BuildableReference>
</CodeCoverageTargets>
<Testables>
</Testables>
<AdditionalOptions>
Expand Down
13 changes: 12 additions & 1 deletion Deferred.xcodeproj/xcshareddata/xcschemes/TVDeferred.xcscheme
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1000"
LastUpgradeVersion = "1010"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand All @@ -27,7 +27,18 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableThreadSanitizer = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES"
shouldUseLaunchSchemeArgsEnv = "YES">
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DB126CDF1E53686900054E95"
BuildableName = "TVDeferredTests.xctest"
BlueprintName = "TVDeferredTests"
ReferencedContainer = "container:Deferred.xcodeproj">
</BuildableReference>
</CodeCoverageTargets>
<Testables>
<TestableReference
skipped = "NO">
Expand Down
121 changes: 45 additions & 76 deletions Sources/Task/ExistentialTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -189,16 +189,6 @@ import Deferred.Atomics
/// - seealso: `TaskProtocol`
/// - seealso: `Future`
public final class Task<SuccessValue>: NSObject {
/// A type for returning and propagating recoverable errors.
public typealias Result = TaskResult<SuccessValue>

/// A type for communicating the result of asynchronous work.
///
/// Create an instance of the task's `Promise` to be filled asynchronously.
///
/// - seealso: `Task.async(upon:flags:onCancel:execute:)`
public typealias Promise = Deferred<Result>

private let future: Future<Result>

#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
Expand All @@ -212,60 +202,53 @@ public final class Task<SuccessValue>: NSObject {
/// Creates a task given a `future` and its `progress`.
public init(_ future: Future<Result>, progress: Progress) {
self.future = future
self.progress = .taskRoot(for: progress)
}

private init(never: ()) {
self.future = .never
self.progress = .indefinite()
}

/// Creates a task given a `future` and an optional `cancellation`.
///
/// `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: Future<Result>, uponCancel cancellation: (() -> Void)? = nil) {
let progress = Progress.wrappingSuccess(of: future, uponCancel: cancellation)
self.init(future, progress: progress)
}

/// Creates a task whose `upon(_:execute:)` methods use those of `base`.
public convenience init<Wrapped: TaskProtocol>(_ wrapped: Wrapped, progress: Progress) where Wrapped.SuccessValue == SuccessValue {
let future = Future<Result>(resultFrom: wrapped)
self.init(future, progress: progress)
self.progress = TaskChain(startingWith: future, using: progress).effectiveProgress
}

/// Creates a task whose `upon(_:execute:)` methods use those of `base`.
public convenience init<Wrapped: FutureProtocol>(succeedsFrom wrapped: Wrapped, progress: Progress) where Wrapped.Value == SuccessValue {
let future = Future<Result>(succeedsFrom: wrapped)
self.init(future, progress: progress)
public init<Wrapped: TaskProtocol>(_ wrapped: Wrapped, progress: Progress) where Wrapped.SuccessValue == SuccessValue {
self.future = Future<Result>(resultFrom: wrapped)
self.progress = TaskChain(startingWith: wrapped, using: progress).effectiveProgress
}
#else
private let cancellation: () -> Void
private var rawIsCancelled = false
#endif

/// Creates a task given a `future` and an optional `cancellation`.
///
/// `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 init(_ future: Future<Result>, uponCancel cancellation: (() -> Void)? = nil) {
self.future = future
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
self.progress = TaskChain(startingWith: future, uponCancel: cancellation).effectiveProgress
#else
self.cancellation = cancellation ?? {}
#endif
}

private init(never: ()) {
self.future = .never
self.cancellation = {}
}
#endif

/// Create a task that will never complete.
public static var never: Task<SuccessValue> {
return Task(never: ())
/// 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 init<Wrapped: TaskProtocol>(_ wrapped: Wrapped, uponCancel cancellation: (() -> Void)? = nil) where Wrapped.SuccessValue == SuccessValue {
self.future = Future<Result>(resultFrom: wrapped)
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
self.progress = TaskChain(startingWith: wrapped, uponCancel: cancellation).effectiveProgress
#else
self.cancellation = {
wrapped.cancel()
cancellation?()
}
#endif
}
}

extension Task: TaskProtocol {
/// A type for returning and propagating recoverable errors.
public typealias Result = TaskResult<SuccessValue>

public func upon(_ executor: Executor, execute body: @escaping(Result) -> Void) {
future.upon(executor, execute: body)
}
Expand Down Expand Up @@ -306,70 +289,56 @@ extension Task: TaskProtocol {
}

extension Task {
/// Creates a task whose `upon(_:execute:)` methods use those of `base`.
/// A type for communicating the result of asynchronous work.
///
/// `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<Wrapped: TaskProtocol>(_ wrapped: Wrapped, uponCancel cancellation: (() -> Void)? = nil) where Wrapped.SuccessValue == SuccessValue {
let future = Future<Result>(resultFrom: wrapped)
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let progress = Progress.wrappingSuccess(of: wrapped, uponCancel: cancellation)
self.init(future, progress: progress)
#else
self.init(future) {
wrapped.cancel()
cancellation?()
}
/// Create an instance of the task's `Promise` to be filled asynchronously.
///
/// - seealso: `Task.async(upon:flags:onCancel:execute:)`
public typealias Promise = Deferred<Result>

if wrapped.isCancelled {
markCancelled(using: cancellation)
}
#endif
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
/// Creates a task whose `upon(_:execute:)` methods use those of `base`.
public convenience init<Wrapped: FutureProtocol>(succeedsFrom wrapped: Wrapped, progress: Progress) where Wrapped.Value == SuccessValue {
let future = Future<Result>(succeedsFrom: wrapped)
self.init(future, progress: progress)
}
#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<Wrapped: FutureProtocol>(succeedsFrom wrapped: Wrapped, uponCancel cancellation: (() -> Void)? = nil) where Wrapped.Value == SuccessValue {
let future = Future<Result>(succeedsFrom: wrapped)
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
let progress = Progress.wrappingCompletion(of: wrapped, uponCancel: cancellation)
self.init(future, progress: progress)
#else
self.init(future, uponCancel: cancellation)
#endif
}

/// Creates an operation that has already completed with `value`.
public convenience init(success value: @autoclosure() throws -> SuccessValue) {
let future = Future<Result>(success: value)
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
self.init(future, progress: .noWork())
#else
self.init(future)
#endif
}

/// Creates an operation that has already failed with `error`.
public convenience init(failure error: Error) {
let future = Future<Result>(failure: error)
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
self.init(future, progress: .noWork())
#else
self.init(future)
#endif
}

/// Creates a task having the same underlying operation as the `other` task.
public convenience init(_ task: Task<SuccessValue>) {
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
self.init(task.future, progress: task.progress)
#else
#else
self.init(task.future, uponCancel: task.cancellation)
if task.isCancelled {
markCancelled()
}
#endif
#endif
}

/// Create a task that will never complete.
public static var never: Task<SuccessValue> {
return Task(Future<Result>.never)
}
}
Loading

0 comments on commit c8f8cfa

Please sign in to comment.