Skip to content
Permalink
Browse files

Cancellation and compile warning fixes (#45)

Cancellation now defaults to forwarding cancellations even if there are
dependent futures waiting.

More swift 2.2 compiler warning fixes
  • Loading branch information...
Michael Gray
Michael Gray committed Jun 14, 2016
1 parent 153c40d commit 31645d3029b7de44dfb796aee71ee1035da32db4
@@ -123,9 +123,12 @@ and than later..
token1.cancel()
```

What happens? Well.. by default.. the cancellation request is actually IGNORED. Why? Because the future will detect that has generated two tokens. And so while the first consumer is no longer intestested in the result.. that doesn't mean the second one is not.
What happens? Well.. by default.. all of these futures will be cancelled. But is this the right behavior?

While the first consumer is no longer intestested in the result.. that doesn't mean the second one is not.

Instead of a dependent "chain" , FutureKit has figured out that there is a dependent "tree" of futures. And while one 'branch' isn't interested in the 'top' of the tree, that doesn't mean other branches still aren't. By default FutureKit 2.0 will now forward this result (FutureKit 1.x used to not forward).

Instead of a dependent "chain" , FutureKit has figured out that there is a dependent "tree" of futures. And while one 'branch' isn't interested in the 'top' of the tree, that doesn't mean other branches still aren't. So it will NOT forward the cancellation in this case. It's 'protecting' token1's interest in the result of 'future'

This sort of thing can happen if you also 'compose' two futures from the same shared future, since deep inside onSuccess() and onComplete() is a call to getCancellationToken()...

@@ -145,9 +148,8 @@ What can you do?
well there is some options on cancel. They all have horribly long names, but that's intentional so people understand whats happening when there are dependent 'trees' vs a single 'chain'

```
token.cancel([.ForwardCancelRequestEvenIfThereAreOtherFuturesWaiting]) // all futures in a dependent tree of futures will be cancelled!
token.cancel([.DoNotForwardCancelRequestIfThereAreOtherFuturesWaiting]) // Don't cancel other futures that still want the depdent future.
```
This will always cause the cancellation to be forwarded, even if it causes other consumers to get a cancellation result, when they didn't perform a cancellation.

There is also a way to NOT forward your cancellation 'up' the chain. Let's say you are using a subFuture, that you want to cancel, without cancelling the whole chain.

@@ -184,6 +184,7 @@
0DA41FE21ADB483C00B58B0B /* SyncWaitHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SyncWaitHandler.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
0DAA4C941B22B3FB0023B02C /* NSOperation+FutureKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSOperation+FutureKit.swift"; sourceTree = "<group>"; };
0DB979A01AE2A21100158386 /* BasicTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = BasicTests.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
0DBB33421D0F973A0092BF7B /* Cancellation.MD */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Cancellation.MD; sourceTree = "<group>"; };
0DBB32EF1D0F91640092BF7B /* FutureKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FutureKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
0DBB32F11D0F91640092BF7B /* FutureKit watchOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FutureKit watchOS.h"; sourceTree = "<group>"; };
0DBB32F31D0F91640092BF7B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -322,6 +323,7 @@
0DA41FA41ADAFE3D00B58B0B = {
isa = PBXGroup;
children = (
0DBB33421D0F973A0092BF7B /* Cancellation.MD */,
09059C431BB23E3C00555E26 /* FutureKit.podspec */,
0D5A14E41B378FC200E69B85 /* convertPerfToCsv.swift */,
0DA41FB01ADAFE3D00B58B0B /* FutureKit */,

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
@@ -234,7 +234,7 @@ internal class CancellationTokenSource {
private func _performCancel(options : CancellationOptions) {

if self.canBeCancelled {
if (options.contains(.ForwardCancelRequestEvenIfThereAreOtherFuturesWaiting)) {
if (!options.contains(.DoNotForwardCancelRequestIfThereAreOtherFuturesWaiting)) {
self.tokens.removeAll()
}
// there are no active tokens remaining, so allow the cancellation
@@ -282,6 +282,10 @@ public struct CancellationOptions : OptionSetType{
public let rawValue : Int
public init(rawValue:Int){ self.rawValue = rawValue}


@available(*, deprecated=1.1, message="depricated, cancellation forwards to all dependent futures by default use onSuccess",renamed="DoNotForwardCancelRequestIfThereAreOtherFuturesWaiting")
public static let ForwardCancelRequestEvenIfThereAreOtherFuturesWaiting = CancellationOptions(rawValue:0)

/**
When the request is forwarded to another future, that future should cancel itself - even if there are other futures waiting for a result.
example:
@@ -295,13 +299,13 @@ public struct CancellationOptions : OptionSetType{
let secondChildofFuture = future.onComplete { (result) in
print("secondChildofFuture result = \(result)")
}
firstChildCancelToken.cancel([.ForwardCancelRequestEvenIfThereAreOtherFuturesWaiting])
firstChildCancelToken.cancel([.DoNotForwardCancelRequestIfThereAreOtherFuturesWaiting])
should result in `future` and `secondChildofFuture` being cancelled.
should result in `future` and `secondChildofFuture` not being cancelled.
otherwise future may ignore the firstChildCancelToken request to cancel, because it is still trying to satisify secondChildofFuture
*/
public static let ForwardCancelRequestEvenIfThereAreOtherFuturesWaiting = CancellationOptions(rawValue:1)
public static let DoNotForwardCancelRequestIfThereAreOtherFuturesWaiting = CancellationOptions(rawValue:1)

/**
If this future is dependent on the result of another future (via onComplete or .CompleteUsing(f))
@@ -317,6 +321,7 @@ public struct CancellationOptions : OptionSetType{
*/
public static let ForceThisFutureToBeCancelledImmediately = CancellationOptions(rawValue:4)



}
@@ -50,18 +50,18 @@ func iMayFailRandomly() -> Future<String> {
}

typealias keepTryingResultType = (tries:Int,result:String)
func iWillKeepTryingTillItWorks(inout attemptNo: Int) -> Future<(tries:Int,result:String)> {
func iWillKeepTryingTillItWorks( attempt: Int) -> Future<(tries:Int,result:String)> {

attemptNo += 1
let attemptNumber = attempt + 1
return iMayFailRandomly().onComplete { (completion) -> Completion<(tries:Int,result:String)> in
NSLog("completion = \(completion)")
switch completion {
case let .Success(yay):
// Success uses Any as a payload type, so we have to convert it here.
let result = (tries:attemptNo,result:yay)
let result = (tries:attemptNumber,result:yay)
return .Success(result)
default: // we didn't succeed!
let nextFuture = iWillKeepTryingTillItWorks(&attemptNo)
let nextFuture = iWillKeepTryingTillItWorks(attemptNumber)
return .CompleteUsing(nextFuture)
}
}
@@ -35,7 +35,7 @@ extension Future {
) -> XCTestExpectation! {

let e = testcase.expectationWithDescription(description)

self.onComplete { (value) -> Void in
let test = assertion(value:value)

0 comments on commit 31645d3

Please sign in to comment.
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.