2013-04-19 13:21:15 +02:00
Dataflow Concurrency
2011-04-09 19:55:46 -06:00
============================
Description
2011-04-10 13:07:57 -06:00
-----------
2011-04-09 19:55:46 -06:00
2012-09-20 16:42:09 +02:00
Akka implements `Oz-style dataflow concurrency <http://www.mozart-oz.org/documentation/tutorial/node8.html#chapter.concurrency> `_
2013-02-11 12:23:14 +13:00
by using a special API for :ref: `futures-scala` that enables a complementary way of writing synchronous-looking code that in reality is asynchronous.
2012-09-20 13:50:25 +02:00
2012-09-20 16:30:48 +02:00
The benefit of Dataflow concurrency is that it is deterministic; that means that it will always behave the same.
If you run it once and it yields output 5 then it will do that **every time** , run it 10 million times - same result.
If it on the other hand deadlocks the first time you run it, then it will deadlock **every single time** you run it.
Also, there is **no difference** between sequential code and concurrent code. These properties makes it very easy to reason about concurrency.
2012-09-21 11:21:55 +02:00
The limitation is that the code needs to be side-effect free, i.e. deterministic.
2012-09-20 16:42:09 +02:00
You can't use exceptions, time, random etc., but need to treat the part of your program that uses dataflow concurrency as a pure function with input and output.
2012-09-20 13:50:25 +02:00
The best way to learn how to program with dataflow variables is to read the fantastic book `Concepts, Techniques, and Models of Computer Programming <http://www.info.ucl.ac.be/%7Epvr/book.html> `_ . By Peter Van Roy and Seif Haridi.
Getting Started (SBT)
---------------------
Scala's Delimited Continuations plugin is required to use the Dataflow API. To enable the plugin when using sbt, your project must inherit the `` AutoCompilerPlugins `` trait and contain a bit of configuration as is seen in this example:
.. code-block :: scala
autoCompilerPlugins := true,
2012-09-21 11:46:22 +02:00
libraryDependencies <+= scalaVersion {
2012-09-24 20:19:37 +02:00
v => compilerPlugin("org.scala-lang.plugins" % "continuations" % "@scalaVersion@")
2012-09-21 11:46:22 +02:00
},
2012-09-20 13:50:25 +02:00
scalacOptions += "-P:continuations:enable",
2012-09-21 11:46:22 +02:00
You will also need to include a dependency on `` akka-dataflow `` :
2012-09-20 13:50:25 +02:00
.. code-block :: scala
2012-09-21 10:47:58 +02:00
"com.typesafe.akka" %% "akka-dataflow" % "@version@" @crossString@
2012-09-20 16:30:48 +02:00
Dataflow variables
------------------
2012-09-20 16:42:09 +02:00
A Dataflow variable can be read any number of times but only be written to once, which maps very well to the concept of Futures/Promises :ref: `futures-scala` .
2012-09-20 16:30:48 +02:00
Conversion from `` Future `` and `` Promise `` to Dataflow Variables is implicit and is invisible to the user (after importing akka.dataflow._).
The mapping from `` Promise `` and `` Future `` is as follows:
2012-09-20 16:42:09 +02:00
- Futures are readable-many, using the `` apply `` method, inside `` flow `` blocks.
- Promises are readable-many, just like Futures.
2012-09-20 16:30:48 +02:00
- Promises are writable-once, using the `` << `` operator, inside `` flow `` blocks.
Writing to an already written Promise throws a `` java.lang.IllegalStateException `` ,
this has the effect that races to write a promise will be deterministic,
only one of the writers will succeed and the others will fail.
2012-09-20 13:50:25 +02:00
The flow
--------
2012-09-20 16:30:48 +02:00
The `` flow `` method acts as the delimiter of dataflow expressions (this also neatly aligns with the concept of delimited continuations),
2012-09-20 13:50:25 +02:00
and flow-expressions compose. At this point you might wonder what the `` flow `` -construct brings to the table that for-comprehensions don't,
2012-09-21 11:46:22 +02:00
and that is the use of the CPS plugin that makes the *look like* it is synchronous, but in reality is asynchronous and non-blocking.
2012-09-20 16:30:48 +02:00
The result of a call to `` flow `` is a Future with the resulting value of the flow.
2012-09-20 13:50:25 +02:00
2012-09-20 16:30:48 +02:00
To be able to use the `` flow `` method, you need to import:
2012-09-20 13:50:25 +02:00
2012-09-20 16:30:48 +02:00
.. includecode :: code/docs/dataflow/DataflowDocSpec.scala
:include: import-akka-dataflow
2012-09-20 13:50:25 +02:00
2012-09-21 11:46:22 +02:00
The `` flow `` method will, just like Futures and Promises, require an implicit `` ExecutionContext `` in scope.
For the examples here we will use:
2012-09-20 16:30:48 +02:00
.. includecode :: code/docs/dataflow/DataflowDocSpec.scala
:include: import-global-implicit
Using flow
~~~~~~~~~~
First off we have the obligatory "Hello world!":
.. includecode :: code/docs/dataflow/DataflowDocSpec.scala
:include: simplest-hello-world
You can also refer to the results of other flows within flows:
.. includecode :: code/docs/dataflow/DataflowDocSpec.scala
:include: nested-hello-world-a
… or:
.. includecode :: code/docs/dataflow/DataflowDocSpec.scala
:include: nested-hello-world-b
Working with variables
~~~~~~~~~~~~~~~~~~~~~~
Inside the flow method you can use Promises as Dataflow variables:
.. includecode :: code/docs/dataflow/DataflowDocSpec.scala
2012-09-21 11:46:22 +02:00
:include: dataflow-variable-a
2012-09-20 16:30:48 +02:00
Flow compared to for
--------------------
Should I use Dataflow or for-comprehensions?
.. includecode :: code/docs/dataflow/DataflowDocSpec.scala
2012-09-21 11:46:22 +02:00
:include: for-vs-flow
2012-09-20 16:30:48 +02:00
Conclusions:
2012-09-20 13:50:25 +02:00
2012-09-20 16:30:48 +02:00
- Dataflow has a smaller code footprint and arguably is easier to reason about.
- For-comprehensions are more general than Dataflow, and can operate on a wide array of types.
2012-09-21 10:47:58 +02:00