Skip to content
This repository was archived by the owner on Aug 29, 2022. It is now read-only.
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
4 changes: 3 additions & 1 deletion .jazzy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ custom_categories:
- CFRunLoop
- name: Futures Than Can Fail
children:
- TaskResult
- Task
- TaskProtocol
- Either
- name: Coordinating Access to Data
children:
Expand All @@ -46,7 +48,7 @@ github_url: "https://github.com/bignerdranch/Deferred"
head: |
<meta property="og:site_name" content="Deferred Reference">
<meta property="og:description" content="An implementation of OCaml's Deferred for Swift. It lets you work with values that haven't been determined yet, like an array that's coming later from a web service call.">
<meta property="og:image" content="//www.bignerdranch.com/img/bnr-logo-square.png">
<meta property="og:image" content="//www.bignerdranch.com/assets/img/logos/square-logo.jpeg">
<link rel="shortcut icon" type="image/x-icon" href="//www.bignerdranch.com/favicon.png">
hide_documentation_coverage: true
module: Deferred
Expand Down
2 changes: 1 addition & 1 deletion Sources/Deferred/Locking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public final class NativeLock: Locking {
}
}
#else
public typealias NativeLock = NSLock
typealias NativeLock = NSLock
#endif

/// A readers-writer lock provided by the platform implementation of the
Expand Down
36 changes: 33 additions & 3 deletions Sources/Task/TaskChain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ struct TaskChain {
/// that first task's `Root`.
@objc(BNRTaskRootProgress)
private class Root: Progress {
/// Key for value of type `Root?` in `Thread.threadDictionary`.
static let threadKey = "_BNRTaskCurrentRoot"

/// Propogates the current Task chain for explicit composition during
/// `Task.andThen`.
static var active: Root? {
get {
return Thread.current.threadDictionary[threadKey] as? Root
Expand All @@ -40,6 +43,30 @@ struct TaskChain {
Thread.current.threadDictionary[threadKey] = newValue
}
}

/// Key for value of type `Bool` in `Progress.userInfo`.
static let expandsKey = ProgressUserInfoKey(rawValue: "_BNRTaskExpandChildren")

/// If `true`, Propogates the current Task chain for explicit composition during
/// `Task.andThen`.
var expandsAddedChildren: Bool {
get {
return userInfo[Root.expandsKey] as? Bool == true
}
set {
setUserInfoObject(newValue, forKey: Root.expandsKey)
}
}

@available(macOS 10.11, iOS 9.0, watchOS 2.0, tvOS 9.0, *)
override func addChild(_ child: Progress, withPendingUnitCount unitCount: Int64) {
if expandsAddedChildren, !child.wasGeneratedByTask {
totalUnitCount += TaskChain.explicitChildUnitCount - unitCount
super.addChild(child, withPendingUnitCount: TaskChain.explicitChildUnitCount)
} else {
super.addChild(child, withPendingUnitCount: unitCount)
}
}
}

/// The beginning of this chain. May be the same as `effectiveProgress`.
Expand Down Expand Up @@ -99,16 +126,19 @@ struct TaskChain {

// MARK: -

/// The handler passed to `map` supports explicit progress reporting.
/// Because we must commit to a `pendingUnitCount` in order to
/// `becomeCurrent`, for now it only attaches for a single unit.
/// The handler passed to `map` can use implicit progress reporting.
/// During the handler, the first `Progress` object created using
/// `parent: .current()` will be given a 100x slice of the task chain on
/// macOS 10.11, iOS 9, and above.
func beginMap() {
root.expandsAddedChildren = true
root.becomeCurrent(withPendingUnitCount: TaskChain.singleUnit)
}

/// See `beginMap`.
func commitMap() {
root.resignCurrent()
root.expandsAddedChildren = false
}

// MARK: -
Expand Down
43 changes: 43 additions & 0 deletions Tests/TaskTests/TaskProgressTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import Task
import Deferred
#endif

// swiftlint:disable type_body_length

#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
class TaskProgressTests: CustomExecutorTestCase {
static let allTests: [(String, (TaskProgressTests) -> () throws -> Void)] = [
Expand Down Expand Up @@ -308,5 +310,46 @@ class TaskProgressTests: CustomExecutorTestCase {
XCTAssertEqual(task.progress.totalUnitCount, 103)
XCTAssertEqual(task.progress.fractionCompleted, 1)
}

func testThatMappingWithCustomProgressIsWeighted() {
let promise1 = Task<Int>.Promise()
let expect = expectation(description: "map handler has started executing")

let task = promise1
.map(upon: .any(), transform: { (value) -> Int in
XCTAssertNotNil(Progress.current())

let customProgress = Progress(totalUnitCount: 32)
expect.fulfill()

customProgress.completedUnitCount = 1
customProgress.completedUnitCount = 2
customProgress.completedUnitCount = 4
customProgress.completedUnitCount = 8
customProgress.completedUnitCount = 16
customProgress.completedUnitCount = 32

return value * 2
})
.map(upon: .any(), transform: { "\($0)" })
.map(upon: .any(), transform: { "\($0)\($0)" })

XCTAssertEqual(task.progress.completedUnitCount, 0)
XCTAssertEqual(task.progress.totalUnitCount, 4)
XCTAssertEqual(task.progress.fractionCompleted, 0)

promise1.succeed(with: 9000)

wait(for: [ expect ], timeout: shortTimeout)

XCTAssertEqual(task.progress.totalUnitCount, 103)

wait(for: [
expectation(toFinish: task.progress)
], timeout: longTimeout)

XCTAssertEqual(task.progress.completedUnitCount, 103)
XCTAssertEqual(task.progress.totalUnitCount, 103)
}
}
#endif