Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Arrow "cancel" wording for consistency #2050

Closed
wants to merge 2 commits into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ open class Cancellable {
@Param("100")
var size: Int = 0

fun evalCancelable(n: Int): IO<Int> =
IO.concurrent().cancelable<Int> { cb ->
fun evalCancellable(n: Int): IO<Int> =
IO.concurrent().cancellable<Int> { cb ->
cb(Right(n))
IO.unit
}.fix()

fun cancelableLoop(i: Int): IO<Int> =
if (i < size) evalCancelable(i + 1).flatMap { cancelableLoop(it) }
else evalCancelable(i)
fun cancellableLoop(i: Int): IO<Int> =
if (i < size) evalCancellable(i + 1).flatMap { cancellableLoop(it) }
else evalCancellable(i)

@Benchmark
fun io(): Int =
cancelableLoop(0).unsafeRunSync()
cancellableLoop(0).unsafeRunSync()
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ open class Uncancellable {
@Param("100")
var size: Int = 0

fun ioUncancelableLoop(i: Int): IO<Int> =
if (i < size) IO { i + 1 }.uncancelable().flatMap { ioUncancelableLoop(it) }
fun ioUncancellableLoop(i: Int): IO<Int> =
if (i < size) IO { i + 1 }.uncancellable().flatMap { ioUncancellableLoop(it) }
else IO.just(i)

@Benchmark
fun io(): Int = ioUncancelableLoop(0).unsafeRunSync()
fun io(): Int = ioUncancellableLoop(0).unsafeRunSync()
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ object AsyncLaws {

fun <F> Async<F>.bracketReleaseIscalledOnCompletedOrError(EQ: Eq<Kind<F, Int>>) {
forAll(Gen.string().applicativeError(this), Gen.int()) { fa, b ->
Promise.uncancelable<F, Int>(this@bracketReleaseIscalledOnCompletedOrError).flatMap { promise ->
Promise.uncancellable<F, Int>(this@bracketReleaseIscalledOnCompletedOrError).flatMap { promise ->
val br = later { promise }.bracketCase(use = { fa }, release = { r, exitCase ->
when (exitCase) {
is ExitCase.Completed -> r.complete(b)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ object BracketLaws {

return listOf(
Law("Bracket: bracketCase with just Unit is eqv to Map") { BF.bracketCaseWithJustUnitEqvMap(EQ) },
Law("Bracket: bracketCase with just Unit is uncancelable") { BF.bracketCaseWithJustUnitIsUncancelable(EQ) },
Law("Bracket: bracketCase with just Unit is uncancellable") { BF.bracketCaseWithJustUnitIsUncancellable(EQ) },
Law("Bracket: bracketCase failure in acquisition remains failure") { BF.bracketCaseFailureInAcquisitionRemainsFailure(EQ) },
Law("Bracket: bracket is derived from bracketCase") { BF.bracketIsDerivedFromBracketCase(EQ) },
Law("Bracket: uncancelable prevents Canceled case") { BF.uncancelablePreventsCanceledCase(BF.just(Unit), BF.just(Unit), EQ) },
Law("Bracket: acquire and release are uncancelable") { BF.acquireAndReleaseAreUncancelable({ BF.just(Unit) }, EQ) },
Law("Bracket: uncancellable prevents Cancelled case") { BF.uncancellablePreventsCancelledCase(BF.just(Unit), BF.just(Unit), EQ) },
Law("Bracket: acquire and release are uncancellable") { BF.acquireAndReleaseAreUncancellable({ BF.just(Unit) }, EQ) },
Law("Bracket: guarantee is derived from bracket") { BF.guaranteeIsDerivedFromBracket(BF.just(Unit), EQ) },
Law("Bracket: guaranteeCase is derived from bracketCase") { BF.guaranteeCaseIsDerivedFromBracketCase({ BF.just(Unit) }, EQ) },
Law("Bracket: bracket propagates transformer effects") { BF.bracketPropagatesTransformerEffects(EQ) },
Expand Down Expand Up @@ -66,11 +66,11 @@ object BracketLaws {
fa.bracketCase(release = { _, _ -> just<Unit>(Unit) }, use = { a -> just(f(a)) }).equalUnderTheLaw(fa.map(f), EQ)
}

fun <F> Bracket<F, Throwable>.bracketCaseWithJustUnitIsUncancelable(
fun <F> Bracket<F, Throwable>.bracketCaseWithJustUnitIsUncancellable(
EQ: Eq<Kind<F, Int>>
): Unit =
forAll(Gen.int().applicativeError(this)) { fa: Kind<F, Int> ->
fa.bracketCase(release = { _, _ -> just<Unit>(Unit) }, use = { just(it) }).equalUnderTheLaw(fa.uncancelable().flatMap { just(it) }, EQ)
fa.bracketCase(release = { _, _ -> just<Unit>(Unit) }, use = { just(it) }).equalUnderTheLaw(fa.uncancellable().flatMap { just(it) }, EQ)
}

fun <F> Bracket<F, Throwable>.bracketCaseFailureInAcquisitionRemainsFailure(
Expand All @@ -87,23 +87,23 @@ object BracketLaws {
fa.bracket(release = { just<Unit>(Unit) }, use = { just(it) }).equalUnderTheLaw(fa.bracketCase(release = { _, _ -> just<Unit>(Unit) }, use = { just(it) }), EQ)
}

fun <F> Bracket<F, Throwable>.uncancelablePreventsCanceledCase(
fun <F> Bracket<F, Throwable>.uncancellablePreventsCancelledCase(
onCancel: Kind<F, Unit>,
onFinish: Kind<F, Unit>,
EQ: Eq<Kind<F, Int>>
): Unit =
forAll(Gen.int().applicativeError(this)) { fa: Kind<F, Int> ->
just(Unit).bracketCase(use = { fa }, release = { _, b ->
if (b == ExitCase.Canceled) onCancel else onFinish
}).uncancelable().equalUnderTheLaw(fa.guarantee(onFinish), EQ)
if (b == ExitCase.Cancelled) onCancel else onFinish
}).uncancellable().equalUnderTheLaw(fa.guarantee(onFinish), EQ)
}

fun <F> Bracket<F, Throwable>.acquireAndReleaseAreUncancelable(
fun <F> Bracket<F, Throwable>.acquireAndReleaseAreUncancellable(
release: (Int) -> Kind<F, Unit>,
EQ: Eq<Kind<F, Int>>
): Unit =
forAll(Gen.int().applicativeError(this)) { fa: Kind<F, Int> ->
fa.uncancelable().bracket({ a -> release(a).uncancelable() }) { just(it) }.equalUnderTheLaw(fa.bracket(release) { just(it) }, EQ)
fa.uncancellable().bracket({ a -> release(a).uncancellable() }) { just(it) }.equalUnderTheLaw(fa.bracket(release) { just(it) }, EQ)
}

fun <F> Bracket<F, Throwable>.guaranteeIsDerivedFromBracket(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,20 @@ object ConcurrentLaws {

return listOf(
Law("Concurrent Laws: cancel on bracket releases") { CF.cancelOnBracketReleases(EQ, ctx) },
Law("Concurrent Laws: acquire is not cancelable") { CF.acquireBracketIsNotCancelable(EQ, ctx) },
Law("Concurrent Laws: release is not cancelable") { CF.releaseBracketIsNotCancelable(EQ, ctx) },
Law("Concurrent Laws: acquire is not cancellable") { CF.acquireBracketIsNotCancellable(EQ, ctx) },
Law("Concurrent Laws: release is not cancellable") { CF.releaseBracketIsNotCancellable(EQ, ctx) },
Law("Concurrent Laws: cancel on guarantee runs finalizer") { CF.guaranteeFinalizerOnCancel(EQ, ctx) },
Law("Concurrent Laws: release is not cancelable") { CF.guaranteeFinalizerIsNotCancelable(EQ, ctx) },
Law("Concurrent Laws: release is not cancellable") { CF.guaranteeFinalizerIsNotCancellable(EQ, ctx) },
Law("Concurrent Laws: cancel on onCancel runs finalizer") { CF.onCancelFinalizerOnCancel(EQ, ctx) },
Law("Concurrent Laws: async cancelable coherence") { CF.asyncCancelableCoherence(EQ) },
Law("Concurrent Laws: cancelable cancelableF coherence") { CF.cancelableCancelableFCoherence(EQ) },
Law("Concurrent Laws: cancelable should run CancelToken on cancel") { CF.cancelableReceivesCancelSignal(EQ, ctx) },
Law("Concurrent Laws: cancelableF should run CancelToken on cancel") { CF.cancelableFReceivesCancelSignal(EQ, ctx) },
Law("Concurrent Laws: async cancellable coherence") { CF.asyncCancellableCoherence(EQ) },
Law("Concurrent Laws: cancellable cancellableF coherence") { CF.cancellableCancellableFCoherence(EQ) },
Law("Concurrent Laws: cancellable should run CancelToken on cancel") { CF.cancellableReceivesCancelSignal(EQ, ctx) },
Law("Concurrent Laws: cancellableF should run CancelToken on cancel") { CF.cancellableFReceivesCancelSignal(EQ, ctx) },
Law("Concurrent Laws: asyncF register can be cancelled") { CF.asyncFRegisterCanBeCancelled(EQ, ctx) },
Law("Concurrent Laws: start join is identity") { CF.startJoinIsIdentity(EQ, ctx) },
Law("Concurrent Laws: join is idempotent") { CF.joinIsIdempotent(EQ, ctx) },
Law("Concurrent Laws: start cancel is unit") { CF.startCancelIsUnit(EQ_UNIT, ctx) },
Law("Concurrent Laws: uncancelable mirrors source") { CF.uncancelableMirrorsSource(EQ) },
Law("Concurrent Laws: uncancellable mirrors source") { CF.uncancellableMirrorsSource(EQ) },
Law("Concurrent Laws: race pair mirrors left winner") { CF.racePairMirrorsLeftWinner(EQ, ctx) },
Law("Concurrent Laws: race pair mirrors right winner") { CF.racePairMirrorsRightWinner(EQ, ctx) },
Law("Concurrent Laws: race pair can cancel loser") { CF.racePairCanCancelsLoser(EQ, ctx) },
Expand Down Expand Up @@ -124,7 +124,7 @@ object ConcurrentLaws {
use = { a -> startLatch.complete(Unit).flatMap { never<Int>() } },
release = { r, exitCase ->
when (exitCase) {
is ExitCase.Canceled -> exitLatch.complete(r) // Fulfil promise that `release` was executed with Canceled
is ExitCase.Cancelled -> exitLatch.complete(r) // Fulfil promise that `release` was executed with Cancelled
else -> unit()
}
}
Expand All @@ -139,12 +139,12 @@ object ConcurrentLaws {
}
}

fun <F> Concurrent<F>.acquireBracketIsNotCancelable(EQ: Eq<Kind<F, Int>>, ctx: CoroutineContext) =
fun <F> Concurrent<F>.acquireBracketIsNotCancellable(EQ: Eq<Kind<F, Int>>, ctx: CoroutineContext) =
forAll(Gen.int(), Gen.int()) { a, b ->
fx.concurrent {
val mvar = MVar(a, this@acquireBracketIsNotCancelable).bind()
val mvar = MVar(a, this@acquireBracketIsNotCancellable).bind()
mvar.take().bind()
val p = Promise.uncancelable<F, Unit>(this@acquireBracketIsNotCancelable).bind()
val p = Promise.uncancellable<F, Unit>(this@acquireBracketIsNotCancellable).bind()
val task = p.complete(Unit).flatMap { mvar.put(b) }
.bracket(use = { never<Int>() }, release = { unit() })
val (_, cancel) = task.fork(ctx).bind()
Expand All @@ -155,11 +155,11 @@ object ConcurrentLaws {
}.equalUnderTheLaw(just(b), EQ)
}

fun <F> Concurrent<F>.releaseBracketIsNotCancelable(EQ: Eq<Kind<F, Int>>, ctx: CoroutineContext) =
fun <F> Concurrent<F>.releaseBracketIsNotCancellable(EQ: Eq<Kind<F, Int>>, ctx: CoroutineContext) =
forAll(Gen.int(), Gen.int()) { a, b ->
fx.concurrent {
val mvar = MVar(a, this@releaseBracketIsNotCancelable).bind()
val p = Promise.uncancelable<F, Unit>(this@releaseBracketIsNotCancelable).bind()
val mvar = MVar(a, this@releaseBracketIsNotCancellable).bind()
val p = Promise.uncancellable<F, Unit>(this@releaseBracketIsNotCancellable).bind()
val task = p.complete(Unit)
.bracket(use = { never<Int>() }, release = { mvar.put(b) })
val (_, cancel) = task.fork(ctx).bind()
Expand All @@ -180,7 +180,7 @@ object ConcurrentLaws {
val (_, cancel) = startLatch.complete(Unit).flatMap { never<Int>() }
.guaranteeCase { exitCase ->
when (exitCase) {
is ExitCase.Canceled -> exitLatch.complete(i) // Fulfil promise that `release` was executed with Canceled
is ExitCase.Cancelled -> exitLatch.complete(i) // Fulfil promise that `release` was executed with Cancelled
else -> unit()
}
}.fork(ctx).bind() // Fork execution, allowing us to cancel it later
Expand All @@ -199,7 +199,7 @@ object ConcurrentLaws {
val exitLatch = Promise<F, Int>(this@onCancelFinalizerOnCancel).bind() // A promise that `release` was executed

val (_, cancel) = startLatch.complete(Unit).flatMap { never<Int>() }
.onCancel(exitLatch.complete(i)) // Fulfil promise that `release` was executed with Canceled
.onCancel(exitLatch.complete(i)) // Fulfil promise that `release` was executed with Cancelled
.fork(ctx).bind() // Fork execution, allowing us to cancel it later

startLatch.get().bind() // Waits on promise of `use`
Expand All @@ -209,11 +209,11 @@ object ConcurrentLaws {
}.equalUnderTheLaw(just(i), EQ)
}

fun <F> Concurrent<F>.guaranteeFinalizerIsNotCancelable(EQ: Eq<Kind<F, Int>>, ctx: CoroutineContext) =
fun <F> Concurrent<F>.guaranteeFinalizerIsNotCancellable(EQ: Eq<Kind<F, Int>>, ctx: CoroutineContext) =
forAll(Gen.int(), Gen.int()) { a, b ->
fx.concurrent {
val mvar = MVar(a, this@guaranteeFinalizerIsNotCancelable).bind()
val p = Promise.uncancelable<F, Unit>(this@guaranteeFinalizerIsNotCancelable).bind()
val mvar = MVar(a, this@guaranteeFinalizerIsNotCancellable).bind()
val p = Promise.uncancellable<F, Unit>(this@guaranteeFinalizerIsNotCancellable).bind()
val task = p.complete(Unit).followedBy(never<Int>()).guaranteeCase { mvar.put(b) }
val (_, cancel) = task.fork(ctx).bind()
p.get().bind()
Expand All @@ -224,26 +224,26 @@ object ConcurrentLaws {
}.equalUnderTheLaw(just(b), EQ)
}

fun <F> Concurrent<F>.asyncCancelableCoherence(EQ: Eq<Kind<F, Int>>): Unit =
fun <F> Concurrent<F>.asyncCancellableCoherence(EQ: Eq<Kind<F, Int>>): Unit =
forAll(Gen.either(Gen.throwable(), Gen.int())) { eith ->
async<Int> { cb -> cb(eith) }
.equalUnderTheLaw(cancelable { cb -> cb(eith); just<Unit>(Unit) }, EQ)
.equalUnderTheLaw(cancellable { cb -> cb(eith); just<Unit>(Unit) }, EQ)
}

fun <F> Concurrent<F>.cancelableCancelableFCoherence(EQ: Eq<Kind<F, Int>>): Unit =
fun <F> Concurrent<F>.cancellableCancellableFCoherence(EQ: Eq<Kind<F, Int>>): Unit =
forAll(Gen.either(Gen.throwable(), Gen.int())) { eith ->
cancelable<Int> { cb -> cb(eith); just<Unit>(Unit) }
.equalUnderTheLaw(cancelableF { cb -> later { cb(eith); just<Unit>(Unit) } }, EQ)
cancellable<Int> { cb -> cb(eith); just<Unit>(Unit) }
.equalUnderTheLaw(cancellableF { cb -> later { cb(eith); just<Unit>(Unit) } }, EQ)
}

fun <F> Concurrent<F>.cancelableReceivesCancelSignal(EQ: Eq<Kind<F, Int>>, ctx: CoroutineContext) =
fun <F> Concurrent<F>.cancellableReceivesCancelSignal(EQ: Eq<Kind<F, Int>>, ctx: CoroutineContext) =
forAll(Gen.int()) { i ->
fx.concurrent {
val release = Promise.uncancelable<F, Int>(this@cancelableReceivesCancelSignal).bind()
val release = Promise.uncancellable<F, Int>(this@cancellableReceivesCancelSignal).bind()
val cancelToken: CancelToken<F> = release.complete(i)
val latch = CountDownLatch(1)

val (_, cancel) = cancelable<Unit> {
val (_, cancel) = cancellable<Unit> {
latch.countDown()
cancelToken
}.fork(ctx).bind()
Expand All @@ -258,12 +258,12 @@ object ConcurrentLaws {
}.equalUnderTheLaw(just(i), EQ)
}

fun <F> Concurrent<F>.cancelableFReceivesCancelSignal(EQ: Eq<Kind<F, Int>>, ctx: CoroutineContext) =
fun <F> Concurrent<F>.cancellableFReceivesCancelSignal(EQ: Eq<Kind<F, Int>>, ctx: CoroutineContext) =
forAll(Gen.int()) { i ->
fx.concurrent {
val release = Promise<F, Int>(this@cancelableFReceivesCancelSignal).bind()
val latch = Promise<F, Unit>(this@cancelableFReceivesCancelSignal).bind()
val async = cancelableF<Unit> {
val release = Promise<F, Int>(this@cancellableFReceivesCancelSignal).bind()
val latch = Promise<F, Unit>(this@cancellableFReceivesCancelSignal).bind()
val async = cancellableF<Unit> {
latch.complete(Unit)
.map { release.complete(i) }
}
Expand Down Expand Up @@ -309,9 +309,9 @@ object ConcurrentLaws {
.equalUnderTheLaw(just<Unit>(Unit), EQ_UNIT)
}

fun <F> Concurrent<F>.uncancelableMirrorsSource(EQ: Eq<Kind<F, Int>>): Unit =
fun <F> Concurrent<F>.uncancellableMirrorsSource(EQ: Eq<Kind<F, Int>>): Unit =
forAll(Gen.int()) { i ->
just(i).uncancelable().equalUnderTheLaw(just(i), EQ)
just(i).uncancellable().equalUnderTheLaw(just(i), EQ)
}

fun <F> Concurrent<F>.raceMirrorsLeftWinner(EQ: Eq<Kind<F, Int>>, ctx: CoroutineContext): Unit =
Expand All @@ -332,7 +332,7 @@ object ConcurrentLaws {
forAll(Gen.either(Gen.throwable(), Gen.string()), Gen.bool(), Gen.int()) { eith, leftWins, i ->
fx.concurrent {
val s = Semaphore(0L, this@raceCancelsLoser).bind()
val promise = Promise.uncancelable<F, Int>(this@raceCancelsLoser).bind()
val promise = Promise.uncancellable<F, Int>(this@raceCancelsLoser).bind()
val winner = s.acquire().flatMap { async<String> { cb -> cb(eith) } }
val loser = s.release().bracket(use = { never<Int>() }, release = { promise.complete(i) })
val race =
Expand Down Expand Up @@ -387,7 +387,7 @@ object ConcurrentLaws {
forAll(Gen.either(Gen.throwable(), Gen.string()), Gen.bool(), Gen.int()) { eith, leftWinner, i ->
val received = fx.concurrent {
val s = Semaphore(0L, this@racePairCanCancelsLoser).bind()
val p = Promise.uncancelable<F, Int>(this@racePairCanCancelsLoser).bind()
val p = Promise.uncancellable<F, Int>(this@racePairCanCancelsLoser).bind()
val winner = s.acquire().flatMap { async<String> { cb -> cb(eith) } }
val loser = s.release().bracket(use = { never<String>() }, release = { p.complete(i) })
val race = if (leftWinner) ctx.racePair(winner, loser)
Expand Down Expand Up @@ -491,8 +491,8 @@ object ConcurrentLaws {
forAll(Gen.either(Gen.throwable(), Gen.string()), Gen.from(listOf(1, 2, 3)), Gen.int(), Gen.int()) { eith, leftWinner, a, b ->
val received = fx.concurrent {
val s = Semaphore(0L, this@raceTripleCanCancelsLoser).bind()
val pa = Promise.uncancelable<F, Int>(this@raceTripleCanCancelsLoser).bind()
val pb = Promise.uncancelable<F, Int>(this@raceTripleCanCancelsLoser).bind()
val pa = Promise.uncancellable<F, Int>(this@raceTripleCanCancelsLoser).bind()
val pb = Promise.uncancellable<F, Int>(this@raceTripleCanCancelsLoser).bind()

val winner = s.acquireN(2).flatMap { async<String> { cb -> cb(eith) } }
val loser = s.release().bracket(use = { never<String>() }, release = { pa.complete(a) })
Expand Down
4 changes: 2 additions & 2 deletions modules/docs/arrow-docs/docs/docs/arrow-fx/fiber/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ permalink: /docs/effects/fiber/


A `Fiber` is a concurrency primitive for describing parallel operations or multi-tasking.
Concurrently started tasks can either be joined or canceled, and these are the only two operators available on `Fiber`.
Concurrently started tasks can either be joined or cancelled, and these are the only two operators available on `Fiber`.

Using `Fiber`, we can describe parallel operations such as `parallelMap` relatively easily.
**Note** the operation written below does not support proper cancellation.
When the resulting `IO` is canceled, it does not propagate this cancellation back to the underlying `IO`.
When the resulting `IO` is cancelled, it does not propagate this cancellation back to the underlying `IO`.

```kotlin:ank
import arrow.fx.*
Expand Down
Loading