Skip to content

Commit 16a2e60

Browse files
committed
Add removeProgress() & removeThen()
1 parent b4b6eec commit 16a2e60

File tree

4 files changed

+241
-16
lines changed

4 files changed

+241
-16
lines changed

SwiftTask.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
/* Begin PBXBuildFile section */
1010
1F20250219ADA8FD00DE0495 /* BasicTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F20250119ADA8FD00DE0495 /* BasicTests.swift */; };
11+
1F218D5B1AFC3FFD00C849FF /* RemoveHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F218D5A1AFC3FFD00C849FF /* RemoveHandlerTests.swift */; };
12+
1F218D5C1AFC3FFD00C849FF /* RemoveHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F218D5A1AFC3FFD00C849FF /* RemoveHandlerTests.swift */; };
1113
1F46DEDA199EDF1000F97868 /* SwiftTask.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F46DED9199EDF1000F97868 /* SwiftTask.h */; settings = {ATTRIBUTES = (Public, ); }; };
1214
1F46DEFB199EDF8100F97868 /* SwiftTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F46DEFA199EDF8100F97868 /* SwiftTask.swift */; };
1315
1F46DEFD199EE2C200F97868 /* _TestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F46DEFC199EE2C200F97868 /* _TestCase.swift */; };
@@ -37,6 +39,7 @@
3739

3840
/* Begin PBXFileReference section */
3941
1F20250119ADA8FD00DE0495 /* BasicTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicTests.swift; sourceTree = "<group>"; };
42+
1F218D5A1AFC3FFD00C849FF /* RemoveHandlerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoveHandlerTests.swift; sourceTree = "<group>"; };
4043
1F46DED4199EDF1000F97868 /* SwiftTask.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftTask.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4144
1F46DED8199EDF1000F97868 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
4245
1F46DED9199EDF1000F97868 /* SwiftTask.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftTask.h; sourceTree = "<group>"; };
@@ -144,6 +147,7 @@
144147
1F46DEE3199EDF1000F97868 /* SwiftTaskTests.swift */,
145148
48511C5A19C17563002FE03C /* RetainCycleTests.swift */,
146149
485C31F01A1D619A00040DA3 /* TypeInferenceTests.swift */,
150+
1F218D5A1AFC3FFD00C849FF /* RemoveHandlerTests.swift */,
147151
1F5FA35619A374E600975FB9 /* AlamofireTests.swift */,
148152
1F46DEE1199EDF1000F97868 /* Supporting Files */,
149153
);
@@ -345,6 +349,7 @@
345349
files = (
346350
1F20250219ADA8FD00DE0495 /* BasicTests.swift in Sources */,
347351
1FF52EB41A4C395A00B4BA28 /* _InterruptableTask.swift in Sources */,
352+
1F218D5B1AFC3FFD00C849FF /* RemoveHandlerTests.swift in Sources */,
348353
1F6A8CA319A4E4F200369A5D /* SwiftTaskTests.swift in Sources */,
349354
1F4C76A41AD8CF40004E47C1 /* AlamofireTests.swift in Sources */,
350355
485C31F11A1D619A00040DA3 /* TypeInferenceTests.swift in Sources */,
@@ -359,6 +364,7 @@
359364
files = (
360365
4822F0DE19D00B2300F5F572 /* SwiftTaskTests.swift in Sources */,
361366
4822F0DD19D00B2300F5F572 /* BasicTests.swift in Sources */,
367+
1F218D5C1AFC3FFD00C849FF /* RemoveHandlerTests.swift in Sources */,
362368
1FF52EB51A4C395A00B4BA28 /* _InterruptableTask.swift in Sources */,
363369
1F4C76A51AD8CF41004E47C1 /* AlamofireTests.swift in Sources */,
364370
485C31F21A1D619A00040DA3 /* TypeInferenceTests.swift in Sources */,

SwiftTask/SwiftTask.swift

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,13 @@ public class Task<Progress, Value, Error>: Printable
355355
///
356356
public func progress(progressClosure: ProgressTuple -> Void) -> Task
357357
{
358-
self._machine.addProgressTupleHandler(progressClosure)
358+
var token: HandlerToken? = nil
359+
return self.progress(&token, progressClosure)
360+
}
361+
362+
public func progress(inout token: HandlerToken?, _ progressClosure: ProgressTuple -> Void) -> Task
363+
{
364+
self._machine.addProgressTupleHandler(&token, progressClosure)
359365

360366
return self
361367
}
@@ -367,7 +373,13 @@ public class Task<Progress, Value, Error>: Printable
367373
///
368374
public func then<Value2>(thenClosure: (Value?, ErrorInfo?) -> Value2) -> Task<Progress, Value2, Error>
369375
{
370-
return self.then { (value: Value?, errorInfo: ErrorInfo?) -> Task<Progress, Value2, Error> in
376+
var token: HandlerToken? = nil
377+
return self.then(&token, thenClosure)
378+
}
379+
380+
public func then<Value2>(inout token: HandlerToken?, _ thenClosure: (Value?, ErrorInfo?) -> Value2) -> Task<Progress, Value2, Error>
381+
{
382+
return self.then(&token) { (value: Value?, errorInfo: ErrorInfo?) -> Task<Progress, Value2, Error> in
371383
return Task<Progress, Value2, Error>(value: thenClosure(value, errorInfo))
372384
}
373385
}
@@ -378,6 +390,12 @@ public class Task<Progress, Value, Error>: Printable
378390
/// - e.g. task.then { value, errorInfo -> NextTaskType in ... }
379391
///
380392
public func then<Progress2, Value2>(thenClosure: (Value?, ErrorInfo?) -> Task<Progress2, Value2, Error>) -> Task<Progress2, Value2, Error>
393+
{
394+
var token: HandlerToken? = nil
395+
return self.then(&token, thenClosure)
396+
}
397+
398+
public func then<Progress2, Value2>(inout token: HandlerToken?, _ thenClosure: (Value?, ErrorInfo?) -> Task<Progress2, Value2, Error>) -> Task<Progress2, Value2, Error>
381399
{
382400
return Task<Progress2, Value2, Error> { [unowned self] newMachine, progress, fulfill, _reject, configure in
383401

@@ -389,8 +407,8 @@ public class Task<Progress, Value, Error>: Printable
389407
// This is especially important for ReactKit's `deinitSignal` behavior.
390408
//
391409
let selfMachine = self._machine
392-
393-
self._then {
410+
411+
self._then(&token) {
394412
let innerTask = thenClosure(selfMachine.value, selfMachine.errorInfo)
395413
_bindInnerTask(innerTask, newMachine, progress, fulfill, _reject, configure)
396414
}
@@ -399,13 +417,13 @@ public class Task<Progress, Value, Error>: Printable
399417
}
400418

401419
/// invokes `completionHandler` "now" or "in the future"
402-
private func _then(completionHandler: Void -> Void)
420+
private func _then(inout token: HandlerToken?, _ completionHandler: Void -> Void)
403421
{
404422
switch self.state {
405423
case .Fulfilled, .Rejected, .Cancelled:
406424
completionHandler()
407425
default:
408-
self._machine.addCompletionHandler(completionHandler)
426+
self._machine.addCompletionHandler(&token, completionHandler)
409427
}
410428
}
411429

@@ -416,7 +434,13 @@ public class Task<Progress, Value, Error>: Printable
416434
///
417435
public func success<Value2>(successClosure: Value -> Value2) -> Task<Progress, Value2, Error>
418436
{
419-
return self.success { (value: Value) -> Task<Progress, Value2, Error> in
437+
var token: HandlerToken? = nil
438+
return self.success(&token, successClosure)
439+
}
440+
441+
public func success<Value2>(inout token: HandlerToken?, _ successClosure: Value -> Value2) -> Task<Progress, Value2, Error>
442+
{
443+
return self.success(&token) { (value: Value) -> Task<Progress, Value2, Error> in
420444
return Task<Progress, Value2, Error>(value: successClosure(value))
421445
}
422446
}
@@ -427,13 +451,19 @@ public class Task<Progress, Value, Error>: Printable
427451
/// - e.g. task.success { value -> NextTaskType in ... }
428452
///
429453
public func success<Progress2, Value2>(successClosure: Value -> Task<Progress2, Value2, Error>) -> Task<Progress2, Value2, Error>
454+
{
455+
var token: HandlerToken? = nil
456+
return self.success(&token, successClosure)
457+
}
458+
459+
public func success<Progress2, Value2>(inout token: HandlerToken?, _ successClosure: Value -> Task<Progress2, Value2, Error>) -> Task<Progress2, Value2, Error>
430460
{
431461
return Task<Progress2, Value2, Error> { [unowned self] newMachine, progress, fulfill, _reject, configure in
432462

433463
let selfMachine = self._machine
434464

435465
// NOTE: using `self._then()` + `selfMachine` instead of `self.then()` will reduce Task allocation
436-
self._then {
466+
self._then(&token) {
437467
if let value = selfMachine.value {
438468
let innerTask = successClosure(value)
439469
_bindInnerTask(innerTask, newMachine, progress, fulfill, _reject, configure)
@@ -454,7 +484,13 @@ public class Task<Progress, Value, Error>: Printable
454484
///
455485
public func failure(failureClosure: ErrorInfo -> Value) -> Task
456486
{
457-
return self.failure { (errorInfo: ErrorInfo) -> Task in
487+
var token: HandlerToken? = nil
488+
return self.failure(&token, failureClosure)
489+
}
490+
491+
public func failure(inout token: HandlerToken?, _ failureClosure: ErrorInfo -> Value) -> Task
492+
{
493+
return self.failure(&token) { (errorInfo: ErrorInfo) -> Task in
458494
return Task(value: failureClosure(errorInfo))
459495
}
460496
}
@@ -466,12 +502,18 @@ public class Task<Progress, Value, Error>: Printable
466502
/// - e.g. task.failure { error, isCancelled -> NextTaskType in ... }
467503
///
468504
public func failure<Progress2>(failureClosure: ErrorInfo -> Task<Progress2, Value, Error>) -> Task<Progress2, Value, Error>
505+
{
506+
var token: HandlerToken? = nil
507+
return self.failure(&token, failureClosure)
508+
}
509+
510+
public func failure<Progress2>(inout token: HandlerToken?, _ failureClosure: ErrorInfo -> Task<Progress2, Value, Error>) -> Task<Progress2, Value, Error>
469511
{
470512
return Task<Progress2, Value, Error> { [unowned self] newMachine, progress, fulfill, _reject, configure in
471513

472514
let selfMachine = self._machine
473515

474-
self._then {
516+
self._then(&token) {
475517
if let value = selfMachine.value {
476518
fulfill(value)
477519
}
@@ -503,6 +545,20 @@ public class Task<Progress, Value, Error>: Printable
503545
{
504546
return self._machine.handleCancel(error: error)
505547
}
548+
549+
// MARK: Remove Handlers
550+
551+
public func removeProgress(handlerToken: HandlerToken)
552+
{
553+
return self._machine.removeProgressTupleHandler(handlerToken)
554+
}
555+
556+
/// NOTE: `task.removeThen(token)` will force `let task2 = task.then(&token)` to deinit immediately and tries cancellation if it is still running.
557+
public func removeThen(handlerToken: HandlerToken)
558+
{
559+
return self._machine.removeCompletionHandler(handlerToken)
560+
}
561+
506562
}
507563

508564
// MARK: - Helper

SwiftTask/_StateMachine.swift

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ internal class _StateMachine<Progress, Value, Error>
3030
/// and will be set to `nil` afterward
3131
internal var initResumeClosure: (Void -> Void)?
3232

33-
internal private(set) lazy var progressTupleHandlers: [ProgressTupleHandler] = []
34-
internal private(set) lazy var completionHandlers: [Void -> Void] = []
33+
internal private(set) lazy var progressTupleHandlers = _Handlers<ProgressTupleHandler>()
34+
internal private(set) lazy var completionHandlers = _Handlers<Void -> Void>()
3535

3636
internal let configuration = TaskConfiguration()
3737

@@ -41,20 +41,30 @@ internal class _StateMachine<Progress, Value, Error>
4141
self.state = paused ? .Paused : .Running
4242
}
4343

44-
internal func addProgressTupleHandler(progressTupleHandler: ProgressTupleHandler)
44+
internal func addProgressTupleHandler(inout token: HandlerToken?, _ progressTupleHandler: ProgressTupleHandler)
4545
{
4646
if self.state == .Running || self.state == .Paused {
47-
self.progressTupleHandlers.append(progressTupleHandler)
47+
token = self.progressTupleHandlers.append(progressTupleHandler)
4848
}
4949
}
5050

51-
internal func addCompletionHandler(completionHandler: Void -> Void)
51+
internal func removeProgressTupleHandler(handlerToken: HandlerToken)
52+
{
53+
self.progressTupleHandlers.remove(handlerToken)
54+
}
55+
56+
internal func addCompletionHandler(inout token: HandlerToken?, _ completionHandler: Void -> Void)
5257
{
5358
if self.state == .Running || self.state == .Paused {
54-
self.completionHandlers.append(completionHandler)
59+
token = self.completionHandlers.append(completionHandler)
5560
}
5661
}
5762

63+
internal func removeCompletionHandler(handlerToken: HandlerToken)
64+
{
65+
self.completionHandlers.remove(handlerToken)
66+
}
67+
5868
internal func handleProgress(progress: Progress)
5969
{
6070
if self.state == .Running {
@@ -185,4 +195,43 @@ internal class _StateMachine<Progress, Value, Error>
185195
self.initResumeClosure = nil
186196
self.progress = nil
187197
}
198+
}
199+
200+
//--------------------------------------------------
201+
// MARK: - Utility
202+
//--------------------------------------------------
203+
204+
public struct HandlerToken
205+
{
206+
internal let key: Int
207+
}
208+
209+
internal struct _Handlers<T>: SequenceType
210+
{
211+
private var currentKey: Int = 0
212+
private var elements = [Int : T]()
213+
214+
internal mutating func append(value: T) -> HandlerToken
215+
{
216+
self.currentKey = self.currentKey &+ 1
217+
218+
self.elements[self.currentKey] = value
219+
220+
return HandlerToken(key: self.currentKey)
221+
}
222+
223+
internal mutating func remove(token: HandlerToken)
224+
{
225+
self.elements.removeValueForKey(token.key)
226+
}
227+
228+
internal mutating func removeAll(keepCapacity: Bool = false)
229+
{
230+
self.elements.removeAll(keepCapacity: keepCapacity)
231+
}
232+
233+
internal func generate() -> GeneratorOf<T>
234+
{
235+
return GeneratorOf(self.elements.values.generate())
236+
}
188237
}

0 commit comments

Comments
 (0)