@@ -19,9 +19,12 @@ struct TaskChain {
1919 /// The default work unit count for a single call to a `Task` initializer or
2020 /// chaining method.
2121 private static let singleUnit = Int64 ( 1 )
22+ /// The work unit count applied when a chaining method may later determine
23+ /// a higher or lower unit count.
24+ private static let undeterminedUnit = Int64 ( 8 )
2225 /// The work unit count when a `Task` initializer or chaining method accepts
2326 /// an user-provided `Progress` instance.
24- private static let explicitChildUnitCount = Int64 ( 100 )
27+ private static let explicitChildUnitCount = Int64 ( 20 )
2528
2629 /// Marker class representing the start of a task chain.
2730 ///
@@ -30,14 +33,14 @@ struct TaskChain {
3033 /// that first task's `Root`.
3134 @objc ( BNRTaskRootProgress)
3235 private class Root : Progress {
33- /// Key for value of type `Root?` in `Thread.threadDictionary`.
34- static let threadKey = " _BNRTaskCurrentRoot "
36+ /// Key for value of type `[ Root] ?` in `Thread.threadDictionary`.
37+ static let threadKey = " _BNRTaskProgressStack "
3538
3639 /// Propogates the current Task chain for explicit composition during
3740 /// `Task.andThen`.
38- static var active : Root ? {
41+ static var activeStack : [ Root ] {
3942 get {
40- return Thread . current. threadDictionary [ threadKey] as? Root
43+ return Thread . current. threadDictionary [ threadKey] as? [ Root ] ?? [ ]
4144 }
4245 set {
4346 Thread . current. threadDictionary [ threadKey] = newValue
@@ -81,7 +84,7 @@ struct TaskChain {
8184 /// Locates or creates the root of a task chain, then generates any
8285 /// progress objects needed to represent `wrapped` in that chain.
8386 init < Wrapped: TaskProtocol > ( startingWith wrapped: Wrapped , using customProgress: Progress ? = nil , uponCancel cancellation: ( ( ) -> Void ) ? = nil ) {
84- if let root = Root . active {
87+ if let root = Root . activeStack . last {
8588 // We're inside `andThen` — `commitAndThen(with:)` will compose instead.
8689 self . root = root
8790 self . effectiveProgress = customProgress ?? . basicProgress( parent: nil , for: wrapped, uponCancel: cancellation)
@@ -90,8 +93,8 @@ struct TaskChain {
9093 self . root = root
9194 self . effectiveProgress = root
9295 } else {
93- // Create a "root" progress for the task and its follow-up steps.
94- // If the initial operation provides progress, give it a 100x slice.
96+ // Create a "root" progress for the task and its follow-up steps. If
97+ // the initial operation provides progress, give it a larger slice.
9598 let unitCount = customProgress == nil ? TaskChain . singleUnit : TaskChain . explicitChildUnitCount
9699 self . root = Root ( )
97100 self . root. totalUnitCount = unitCount
@@ -107,17 +110,17 @@ struct TaskChain {
107110
108111 /// Locates or creates the root of a task chain, then increments its
109112 /// total units in preparation for a follow-up operation to be performed.
110- init < Wrapped: TaskProtocol > ( continuingWith wrapped: Wrapped ) {
113+ private init < Wrapped: TaskProtocol > ( continuingFrom wrapped: Wrapped , pendingUnitCount : Int64 ) {
111114 if let task = wrapped as? Task < Wrapped . Success > , let root = task. progress as? Root {
112115 // If `wrapped` is a Task created normally, reuse the progress root;
113116 // this `map` or `andThen` builds on that progress.
117+ root. totalUnitCount += pendingUnitCount
114118 self . root = root
115- self . root. totalUnitCount += TaskChain . singleUnit
116119 self . effectiveProgress = root
117120 } else {
118121 // If `wrapped` is a `Future` or something else, start a new chain.
119122 self . root = Root ( )
120- self . root. totalUnitCount = TaskChain . singleUnit * 2
123+ self . root. totalUnitCount = TaskChain . singleUnit + pendingUnitCount
121124 self . effectiveProgress = root
122125
123126 root. monitorCompletion ( of: wrapped, withPendingUnitCount: TaskChain . singleUnit)
@@ -126,9 +129,15 @@ struct TaskChain {
126129
127130 // MARK: -
128131
132+ /// Locates or creates the root of a task chain, then increments its
133+ /// total units in preparation for a follow-up operation to be performed.
134+ init < Wrapped: TaskProtocol > ( mapFrom wrapped: Wrapped ) {
135+ self . init ( continuingFrom: wrapped, pendingUnitCount: TaskChain . singleUnit)
136+ }
137+
129138 /// The handler passed to `map` can use implicit progress reporting.
130139 /// During the handler, the first `Progress` object created using
131- /// `parent: .current()` will be given a 100x slice of the task chain on
140+ /// `parent: .current()` will be given a larger slice of the task chain on
132141 /// macOS 10.11, iOS 9, and above.
133142 func beginMap( ) {
134143 root. expandsAddedChildren = true
@@ -143,32 +152,40 @@ struct TaskChain {
143152
144153 // MARK: -
145154
155+ /// Locates or creates the root of a task chain, then increments its
156+ /// total units in preparation for a follow-up operation to be performed.
157+ init < Wrapped: TaskProtocol > ( andThenFrom wrapped: Wrapped ) {
158+ self . init ( continuingFrom: wrapped, pendingUnitCount: TaskChain . undeterminedUnit)
159+ }
160+
146161 /// The handler passed to `andThen` uses explicit progress reporting.
147162 /// After returning a from the handler, locate or create a representative
148163 /// progress and attach it to the root. If this next step provides custom
149- /// progress, give it a 100x slice.
164+ /// progress, give it a larger slice.
150165 func beginAndThen( ) {
151- Root . active = root
166+ Root . activeStack . append ( root)
152167 }
153168
154169 /// See `beginAndThen`.
155170 func commitAndThen< Wrapped: TaskProtocol > ( with wrapped: Wrapped ) {
156- if let task = wrapped as? Task < Wrapped . Success > , !( task. progress is Root ) {
157- let pendingUnitCount = task. progress. wasGeneratedByTask ? TaskChain . singleUnit : TaskChain . explicitChildUnitCount
158- root. totalUnitCount += pendingUnitCount - TaskChain. singleUnit
159- root. adoptChild ( task. progress, withPendingUnitCount: pendingUnitCount)
171+ let wrappedProgress = ( wrapped as? Task < Wrapped . Success > ) ? . progress
172+ let pendingUnitCount = wrappedProgress? . wasGeneratedByTask == false ? TaskChain . explicitChildUnitCount : TaskChain . singleUnit
173+ root. totalUnitCount += pendingUnitCount - TaskChain. undeterminedUnit
174+ if let wrappedProgress = wrappedProgress {
175+ root. monitorChild ( wrappedProgress, withPendingUnitCount: pendingUnitCount)
160176 } else {
161- root. monitorCompletion ( of: wrapped, uponCancel: wrapped. cancel, withPendingUnitCount: TaskChain . singleUnit )
177+ root. monitorCompletion ( of: wrapped, uponCancel: wrapped. cancel, withPendingUnitCount: pendingUnitCount )
162178 }
163- Root . active = nil
179+ Root . activeStack . removeLast ( )
164180 }
165181
166182 /// When the `andThen` handler can't be run at all, mark the enqueued unit
167183 /// as complete anyway.
168184 func flushAndThen( ) {
169185 root. becomeCurrent ( withPendingUnitCount: TaskChain . singleUnit)
170186 root. resignCurrent ( )
171- Root . active = nil
187+ root. totalUnitCount += TaskChain . singleUnit - TaskChain. undeterminedUnit
188+ Root . activeStack. removeLast ( )
172189 }
173190}
174191#endif
0 commit comments