forked from johanandren/futiles
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
50c3ffa
commit 3954e75
Showing
2 changed files
with
99 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package markatta.futiles | ||
|
||
import scala.concurrent.ExecutionContext | ||
import scala.concurrent.Future | ||
import scala.concurrent.Promise | ||
import scala.util.Try | ||
import java.util.concurrent.{Callable, FutureTask} | ||
|
||
class Cancellable[T](executionContext: ExecutionContext, block: => T) { | ||
private val promise = Promise[T]() | ||
|
||
def future: Future[T] = promise.future | ||
|
||
private val jf: FutureTask[T] = new FutureTask[T]( | ||
new Callable[T] { | ||
override def call(): T = block | ||
} | ||
) { | ||
override def done() = promise.complete(Try(get())) | ||
} | ||
|
||
/** Attempts to cancel the underlying [[scala.concurrent.Future]]. Note that this is a best effort attempt | ||
*/ | ||
def cancel(): Unit = jf.cancel(true) | ||
|
||
executionContext.execute(jf) | ||
} | ||
|
||
object Cancellable { | ||
|
||
/** Allows you to put a computation inside of a [[scala.concurrent.Future]] which can later be cancelled | ||
* @param block | ||
* The computation to run inside of the [[scala.concurrent.Future]] | ||
* @param executionContext | ||
* The [[scala.concurrent.ExecutionContext]] to run the [[scala.concurrent.Future]] on | ||
* @return | ||
* A [[markatta.futiles]] providing both the [[scala.concurrent.Future]] and a `cancel` method allowing you to | ||
* terminate the [[scala.concurrent.Future]] at any time | ||
* @see | ||
* Adapted from https://stackoverflow.com/a/39986418/1519631 | ||
*/ | ||
def apply[T](block: => T)(implicit executionContext: ExecutionContext): Cancellable[T] = | ||
new Cancellable[T](executionContext, block) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package markatta.futiles | ||
|
||
import java.util.concurrent.atomic.AtomicBoolean | ||
import scala.concurrent.{CancellationException, ExecutionException} | ||
|
||
class CancellableSpec extends Spec { | ||
describe("The cancellable utility") { | ||
|
||
describe("Basic cancel") { | ||
|
||
it("successfully cancels a Future") { | ||
val atomicBoolean = new AtomicBoolean(true) | ||
|
||
val cancellable = Cancellable { | ||
Thread.sleep(100) | ||
atomicBoolean.set(false) | ||
} | ||
|
||
Thread.sleep(50) | ||
cancellable.cancel() | ||
Thread.sleep(100) | ||
atomicBoolean.get() shouldEqual true | ||
} | ||
|
||
it("works as a normal Future when not cancelling") { | ||
val cancellable = Cancellable { | ||
true | ||
} | ||
|
||
cancellable.future.futureValue shouldEqual true | ||
} | ||
|
||
it("works as normal Future when throwing exception") { | ||
val cancellable = Cancellable { | ||
throw new IllegalArgumentException | ||
} | ||
|
||
val exception = cancellable.future.failed.futureValue | ||
exception shouldBe an[ExecutionException] | ||
exception.getCause shouldBe an[IllegalArgumentException] | ||
} | ||
|
||
it("when cancelling the exception should be a CancellationException exception") { | ||
val cancellable = Cancellable { | ||
Thread.sleep(100) | ||
} | ||
cancellable.cancel() | ||
cancellable.future.failed.futureValue shouldBe an[CancellationException] | ||
} | ||
|
||
} | ||
|
||
} | ||
|
||
} |