The way of the Future, Futures in .NET, Java and JavaScript

As demand for efficient concurrency is increasing, the need for simple asynchronous programming is becoming more and more important. In the multithreaded world, the driving factors is the realization that threads are best used for scheduling units of computation rather than units of work from a business logic perspective. In the JavaScript world, even with JavaScript on the server (Node.js), asynchronous IO operations is the only way to achieve any form of multitasking, and in fact the lack of blocking IO operations is often quoted as a strength as it forces programmers to be good.

Asynchronous programming is tightly linked to the concept of continuations, registering a function with surrounding closure as a callback when some operation completes successfully or fails. Although it is possible to do this ad hoc, reinventing the wheel with a slightly different shape for each use, the pattern of providing callbacks in order to handle some future results has become common and uniform enough to deserve its own concept in most programming language. Indeed, the word Future is often used to describe exactly this concept.

So what is the benefit of having a Future? For many years, both .NET, JavaScript and the Java platform survived perfectly well without such a formalized concept. JavaScript used callback functions for AJAX, Java (when asynchronous operations were attempted at all) used various callback interfaces and .NET relied on an unholy mix of .NET events, callback functions, the Begin/End Pattern and the extremely misguided Event Based Asynchronous Pattern. All these approaches were sufficient for simple continuations, but lacked one important quality: composability.

Imagine writing the code for asynchronously downloading a file using callback style asynchronous programming. Not too bad, right? Depending on the language you may end up with some ugly anonymous types and explicit state, or you may be able to use anonymous functions and stateful closures – either way you pretty much know how to get it right. Now imagine downloading n files in parallel, or downloading a file from n locations using only the first one that completed, or retrying a download n times if it failed before giving up completely.  Like the look of the code you had to write?

Requirements for the Future

Having en entity explicitly representing the future allows you to solve patterns like the above once, correctly, then just applying the building blocks you need when you need them. Let us take a look at what other qualities we may want from such an entity before we dig into the way it has been implemented on the various platforms. Our ideal Future should strive to meet the following requirements:

  • Composable – as discussed above, this is the may reason for making the future a thing rather than a pattern. Composable means that our Future should be transformable using chaining, filtering, multiplexing and demultiplexing, with each operation generating a new Future just as generic as the first.
  • Minimal – following the Principle of least privelage, the Future should contain only operations required for its purpose – accessing and transforming a future result (or error). This means, as an example, that the action of completing the Future should be separate from the Future itself.
  • Thread safe – while somewhat irrelevant in a single threaded environment, Futures will be used for concurrent as well as asynchronous programming where multithreading is supported. Thread safe means that all consumers must see the same result of the Future, and that the order in which the Future gets a result and the result being accessed must not affect the semantics. Thread safe also holds for the producing side, there may be a race between for example setting the result of the Future and marking it as Cancelled, our implementation must ensure that, once set, the Future keeps its result.

Let us take a look at some implementations of Futures in .NET, JavaScript and Java and see how well they meet the above.

Futures in JavaScript:

Although there is no concept of a Future in the JavaScript standard library, jQuery – all JavaScript developers best friend, has introduced the concept of a Deferred. The use of Deferred in JavaScript, originally a pretty wrapper around the jQuery ajax call, has been made a lot more predominant in larger scale JavaScript applications. How well does the jQuery deferred meet our requirements?

  • Composable – the deferred allows for chaining and error handling by adding callbacks for successful results, failures or both. The pipe function allows for transformations of resulting values, either using a synchronous or asynchronous function. The built in when function allows for hassle free fork/join patterns where a number of asynchronous operations need to complete before another operation can be initiated. Conclusion: Excellent.
  • Minimal – Although the jQuery Deferred is a read/write object that allows for the result of the Future to be set by anyone, jQuery also provides a read only view called Promise that only allows for the result of the future to be observerd. Conclusion: Excellent.
  • Thread safe – While JavaScript is single threaded, there is still potentially a race between a Future being given a value and callbacks being registered. The jQuery Deferred guarantees that the callback will be invoked regardless of whether the result was set before or after the operation. Completing the Future more than once is also safe, only the last status will be used, as is subscribing multiple times. Conclusion: Excellent.
Futures in .NET:

Microsoft, true to form, introduced a completely new name for the Future in .NET 4, called a Task. Tasks can be created from operations running on a background thread using a task factory, or any asynchronous operation by using creating a task from a Task Completion Source. Tasks allow for continuation callbacks to be registered and allow for fine grained control over how (same thread or using a scheduler). In addition, it is possible to block until a result is available, optionally providing a timeout. The .NET task implementation has rich support for cancellation, decoupling the ability to trigger cancellation from both the task itself and the operation being cancelled. As for our requirements:

  • Composable – continuations for all the completion modes allow for sane composability. In addition, a Task provides a WaitHandle that allows for Select style wait operations over multiple tasks. Built in support allows for combining multiple tasks and waiting for one out of many tasks to complete. C# 5 takes Tasks to a completely different level with built in language support for writing asynchronous code in a traditional imperative style and having the compiler do the heavy lifting. Conclusion: Excellent.
  • Minimal – the .NET task does suffer from some compatibility issues, having to implement a WaitHandle and carry asynchronous state to support the older IAsyncResult interface used in the Begin/End pattern. Setting the result of the Task is nicely separated from consuming it, but Task also carries two really ugly additions – Start() and RunSynchronously(). While these make some sense for CPU bound operations meant to execute on a background thread, scheduling should be a separate concern from consuming a Future, and the methods are completely meaningless for asynchronous IO operations. Conclusion: Acceptable.
  • Thread safe – the Task is safe to use in a multithreaded environment, all threads are guaranteed to see the same final state and there is no dangerous race between adding continuations and generating a result. The Task Completion Source even provides TrySet method that both safely updates the state of the related Task but also reports back whether or not a state was accepted, allowing for tasks to be cancelled from a thread different from the one producing a result. Conclusion: Excellent.
Futures in Java
The situation in Java, or rather, the JVM, is somewhat more complicated. In addition to the Java Future interface (from java.util.concurrent), a number of frameworks and languages provide their own version. A quick look at the Java Future will tell us why:
  • Composable – not really, the Java Future only allows for blocking until a result is generated or polling for a result, neither which allows operations to compose gracefully. The Java Future is acceptable for fork/join parallelism where a number of results need to be collected at the end of an operation, but not much more, and definitely not asynchronous IO operations. Conclusion: Poor.
  • Minimal – although too minimal in some respects, the Java Future adds cancellation support. Cancellation of Futures can be done well, and has been in the .NET Task and CancellationToken, but allowing everyone with (what should be) a read only view of a future result to cancel the operation (affecting all other consumers), is a clear violation of the Separation of Concerns principle as well as the Principle of least privelage. Conclusion: Poor.
  • Thread safe – java.util.concurrent.Future is an interface and as such can’t guarantee correct behavior of an implementation. The implementation provided by the framework, FutureTask, is however thread safe when it comes to concurrent updates. Conclusion: Acceptable.
As the Java Future implementation is so obviously flawed, everyone has come up with their own version. Indeed, just looking at the Scala standard library we find scala.actors.Future that, although providing a composable representation of a future is weirdly tied into the scala actor concept. Another Scala incarnation is the horribly crippled unloved scala.parallel.Future. Scala based framework such as Twitter and Akka reinvents this wheel again and rolls their own. The only Future that stands a chance to get any wide spread acceptance in the JVM world is the future scala.concurrent.Future of the upcoming Scala 2.10. Let us take look at what it provides:
  • Composability – the new scala Future allows for blocking as well as continuation passing operations and has a rich set of functions for composing futures, treating Future as a monad. The Scala Traits concept allows for a rich set of higher order operations on futures accessible directly on the type without requiring more than a handful of operations to be explicitly implemented by a custom Future. While the traits will not be as nice to use from Java when implementing custom Futures, a Promise can be used to create one and (eventually) give it a result. Conclusion: Excellent.
  • Minimal – although providing a rich set of methods for operating on Futures, there is a clean separation of concern and the Future is a pure read only view of an eventual result. The new Scala Future has no concept of cancellation however, which means that cancellation will be built in a number of different flavours on top of this construct. Conclusion: Good.
  • Thread safe – when tasks are created from an accompanying Promise, the Future as well as the Promise itself is fully thread safe. The Promise allows for tryset operations that allow for multiple threads to attempt to set a status concurrently with predictable safe semantics. The Future being a Trait (or interface when exposed to Java) means however that the consumer of a Future cannot know with full certainty that the Future will follow sane multi threaded semantics. Conclusion: Good.
Conclusion

Futures, though apparently trivial, come in many different flavours and too often in not very tasty ones. JavaScript, ironically, has one of the purest implementations of a Future in the jQuery Deferred/Promise, and the Future of the JVM, if anything, is the upcoming scala.concurrent.Future.

Leave a Reply