Last active
June 15, 2016 22:43
-
-
Save starkcoffee/9b7bee938628a2bff783bed1f0634b26 to your computer and use it in GitHub Desktop.
Understanding how futures are executed
This file contains hidden or 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
/* | |
I wanted to write a specs2 unit test that proves a function works concurrently. I tried writing a unit test | |
to prove that Futures run concurrently (not always sequentially), just for fun. I ran the test below, | |
expecting count to equal 1, but it always executed the futures in order, even though C1 takes the longest | |
time. I realised, I need to go back to school and learn how futures are executed. | |
*/ | |
"prove futures run in parallel" in new Context { | |
var count = 0 | |
Await.result(Future.join(Seq( | |
Future { Thread.sleep(2000); println("c1"); count = 1 }, | |
Future { Thread.sleep(1000); println("c2"); count = 2 }, | |
Future { Thread.sleep(0); println("c3"); count = 3 } | |
))) | |
count ==== 1 | |
} | |
/* | |
It was hard to know where to start: What is executing the futures? Is it Await.result? Is it the specs2 | |
framework? Some other lauering implicit executor somewhere? What also made it tricky is that I could | |
only find documentation on how Scala Futures are executed, not Twitter Futures :/ | |
In the end a colleague told me to "You need to build your futures in the context of an ExecutionContext | |
that runs them in parallel. If you replace `Future {` with `FuturePool.unboundedPool {` in your code snippet | |
they should be executed in parallel". Thanks Kristof! | |
And lo and behold, that works! | |
*/ | |
"prove futures run in parallel for realsies" in new Context { | |
var count = 0 | |
Await.result(Future.join(Seq( | |
FuturePool.unboundedPool { Thread.sleep(2000); println("c1"); count = 1 }, | |
FuturePool.unboundedPool { Thread.sleep(1000); println("c2"); count = 2 }, | |
FuturePool.unboundedPool { Thread.sleep(0); println("c3"); count = 3 } | |
))) | |
count ==== 1 | |
} | |
/* | |
Remaining mysteries: how many pools am I using now? What is the default execution context? | |
*/ |
@peterbourgon thanks! Yeh this is just a test. That means it is possible for a count to get lost right? If we use atomic, all counts are guaranteed to be counted. I could write a test to prove that too.
I'm not an expert on the Scala/JVM memory model, but it's probably just undefined behavior. A count could get lost, counts could be applied out of order, the program could just crash, etc.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Your futures are mutating shared state without explicit synchronization. Unless I overlook some atomic guarantees of Scala's memory model, you have a data race :) If you're just using this as an example to understand futures, then no problem, but beware this is dangerous code in prod :)