It's the future now
For various reasons, I had to implement the Future pattern in two completely different settings in three different ways today.
A future is a place-holder for the undetermined result of a (concurrent) computation. Once the computation delivers a result, the associated future is eliminated by globally replacing it with the result value. That value may be a future on its own.
The last implementation was the explicit one, it was simply:
public class Future<T> { private T value; private bool hasValue; private string error; public void SetValue(T value) { this.value = value; hasValue = true; } public void SetError(string error) { this.error = error; } public bool HasError { get { return error != null; } } public T Value { get { return value; } } public bool HasValue { get { return hasValue; } } public string Error { get { return error; } } }
This is the simplest approach, primitive, you would say. It requires active collaboration from the code that uses it, and it requires that the method would return a future value. It is a good approach for a very small API, but this is often something that you want to handle in a cross cutting fashion. First, we need a more robust future implementation. (This is wasteful in that it doesn't lazily allocate the reset event).
public class Future<T> { private ManualResetEvent resetEvent = new ManualResetEvent(false); private T value; private bool hasValue; private Exception error; public void SetValue(T value) { this.value = value; hasValue = true; resetEvent.Set(); } public void SetError(Exception error) { this.error = error; resetEvent.Set(); } public bool HasError { get { return error != null; } } public T Value { get { resetEvent.WaitOne(); resetEvent.Close(); if (error != null) throw new FutureInvocationException(error); return value; } } public bool HasValue { get { return hasValue; } } public Exception Error { get { resetEvent.WaitOne(); return error; } } }
Now that we have that, we still have to deal with changing the interfaces and an explicitly concurrent programming model. There is a better way, I got the idea from watching a future / active objects implementation in Python, from Ronnie Maor. The ability to subvert the return value there allows for really nice things.
We will start with the easiest version to grasp, we will define the code that we want to work with:
public class WorkFlowController { public virtual int LongRunningTask() { Console.WriteLine("Starting long running"); Thread.Sleep(5000); Console.WriteLine("Completed long running"); return 42; } }
Now, we want to make this into something that will return a future, and we want to do it in a way that wouldn't be too awkward. Here is a simple usage:
WorkFlowController controller = new WorkFlowController(); Future<int> future = InThe.Future<int>(delegate { return controller.LongRunningTask(); }); Console.WriteLine("After calling long running"); Console.WriteLine(future.Value);
Executing this code produces:
After calling long runnning
Starting long running
Completed long running
42
The InThe.Future part is simply:
public class InThe { public delegate T Proc<T>(); public static Future<T> Future<T>(Proc<T> futureAction) { Future<T> future = new Future<T>(); ThreadPool.QueueUserWorkItem(delegate { try { future.SetValue(futureAction()); } catch (Exception e) { future.SetError(e); } }); return future; } }
This is pretty straightforward, but I don't like the syntax that we have here (even with C# 3.0, it is still ugly). What I would like to get is this:
WorkFlowController controller = With.Future<WorkFlowController>(); Future<int> future = InThe.Future(controller.LongRunningTask()); Console.WriteLine("After calling long runnnig"); Console.WriteLine(future.Value);
Suddenly I have a saner model, and the model to support futures is explicit. The implementation is fairly trivial as well:
public class FutureInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { InThe.lastInvocation = invocation; if (invocation.Method.ReturnType.IsValueType) invocation.ReturnValue = Activator.CreateInstance(invocation.Method.ReturnType); } } public static class With { private readonly static ProxyGenerator generator = new ProxyGenerator(); public static T Future<T>(params object[] args) { return (T)generator.CreateClassProxy( typeof (T), new IInterceptor[] {new FutureInterceptor()}, args); } } public class InThe { public static IInvocation lastInvocation; public static Future<T> Future<T>(T ignored) { Future<T> future = new Future<T>(); IInvocation invocation = lastInvocation; lastInvocation = null; ThreadPool.QueueUserWorkItem(delegate { try { invocation.Proceed(); future.SetValue((T)invocation.ReturnValue); } catch (Exception e) { future.SetError(e); } }); return future; } }
I like this best, and it is very easy to implement and work with. Return value woes has been overcome, horray!
Comments
I'm afraid I don't like it, myself. When you call a method as a parameter, normal C# executes the method immediately. I like coding on "solid ground" where the rules of the language don't shift and change.
I guess that's the argument against AOP in general, and normally I'm on AOP's side - this just feels one step too far for my liking.
With C# you could easily have:
Future<int> future = InThe.Future(() => controller.LongRunningTask());
The addition of just 5 characters makes it much obvious that you're passing in a delegate, and that therefore the code may not get executed immediately.
What happens in your code if you do something like:
Future<int> future = InThe.Future(controller.LongRunningTask(GetParameterValue()));
? Does GetParameterValue() get executed immediately, and LongRunningTask get executed "later"? What if I change the value of controller in the next line of code - might it change the invocation if the thread hasn't started yet, or not? What about the thread safety of InThe?
Also bear in mind that futures don't have to have anything to do with multi-threading. I've got some LINQ code I'm working on at the moment which effectively lets you set up a load of data observers, then push some data, then get the values later. For instance:
IDataProducer<string> dp = new DataProducer<string>();
IFuture<int> count = dp.Count();
IFuture<int> maxLength = dp.Max(x => x.Length);
// etc
dp.ProduceAndEnd(someListOfStrings);
int realCount = count.Value;
int realMaxLength = maxLength.Value;
// etc
The code was originally written for a slightly different situation, but I think it's a neat use case - and one which involves futures but explicit timing on a single thread. I wouldn't want each of the aggregates to end up running on a different thread here.
Anyway, time to shut up now.
Jon
I made a half-arsed attempt at something a bit more transparent a while back:
http://thom.org.uk/blog/FutureValuesWithCastlesDynamicProxy.aspx
Given Jon's comment above, this is potentially even more aggravating, as consumers would have no indication at all that something odd is happening, but YMMV.
Oren, as much as I admit that I haven't yet had a chance to look at what I'm about to mention, I'm wondering if you have yet had any time to look at the ParallelFX CTP and its Tasks and Futures?
The codethat you listed doesn't work, you need to explicitly state the T of the method.
I find it ugly.
Future<int> future = InThe.Future<int>(() => controller.LongRunningTask());
I am talking specifically about concurrent futures here, so threading is always involved.
GetParameterValue() will be called immediately, changing the value of controller will not affect the future call.
That is not something that I would push to production without some thought, mind you.
I actully did push a a Future to production yesterday, but it was of the first, explicit variety.
Jeremy, I am afraid not.
Future<int> future = InThe.Future(() => controller.LongRunningTask());
You are incorrect in stating that you have to explicitly declare T for the method. The compiler can in fact correctly determine the type based on the lambda function.
Jon's code was correct. Try it. :-)
James,
Wow, you are correct.
But I tried that, and it didn't work.
Regarding syntax, you could've used:
InThe.Future(controller.LongRunningTask);
Regarding () => controller.LongRunningTask(), that should be fine.
Perhaps you tried the C# 2 "equivalent":
delegate { return controller.LongRunningTask(); }
? That wouldn't work in C# 2, but would work in C# 3 due to the enhanced type inference.
But the answer from Francois is probably the best one :)
Jon
It looks cool, but I'm not sure I get it. Why not just mark LongRunningTask as synchronised? Sorry if it's a dumb question!
Not if I wanted to pass parameters
Tobin,
It is executing in another thread, synchronized is another issue
Thanks Ayende.
It looks like calling clients "block" whilst waiting for the Future to get it's result?
I see that the result is calculated on another thread, but is that critical to this pattern?
A synchronised method would enforce calling threads to queue up so that only one could execute the code at a time.
So, both things appear to achieve the same objective?
(Actually, I know I'm wrong on this, but just want to explain where I don't get it!)
Tobin,
The point is to get other useful work done on the original thread, while something else happens on the LongRunningTask thread. When the original thread can't do any more useful work until the long running task has completed, it can ask for the value - which will block until it's ready.
Francois's solution to simply pass the name of the method in, unfortunately, does not appear to work in this case.
The lambda appears to be the only approach where type inferencing works.
Lambda or (ugly) anonymous methods both work, yes. I must admit I'm surprised that the method group version doesn't work though.
I guess it's something deep inside the type inference bit of the spec which treats anonymous functions differently to method groups. That part of the spec is horrendous to read these days - it's all a lot more complicated for C# 3 than it was for C# 2. A lot more powerful, of course, and pretty much necessary for LINQ to work, but nightmarish to reason about.
Just looking at the spec briefly, I would have expected 7.4.2.6 (second bullet) to work in this case. I'll investigate further when I don't have a screaming 19-month-old on my knee...
I just find it very beautiful once I get my head wrapped around it.
Awesome!
However, InThe should be renamed to BackToThe. That way, I can write this:
Future<int> future = BackToThe.Future(controller.LongRunningTask());
And of course, FutureInterceptor should be renamed to FluxCapacitor.
@Jay - Just imagine how beautiful it would be if Future of T implemented an implicit cast to T. :)
Jeremy,
You probably don't want to do that, and anyway you can't (unless they changed the limit on implicit conversion to interfaces.
Have you looked at the Parallel FX? I think they have a Future.
Btw, futures and perl6's junctions make me wonder if there is a place in a language for things like that -- the X<T> that transparently behaves as T for all operations on it except several special ones. Like 5 + future int => future int.
@Ayende - I hear ya. I was mostly just dreaming a bit and thinking of what it would be like if one could do that. Mind you, I'm not sure the language/compiler would actually limit you from supporting the implicit conversion in a useful number of cases. I have a LazyLoad of T class, for example, kicking around somewhere that supports conversion to T. Perhaps I'll have to dig it up and see what limits it would bump into.
I've just been looking into the method group version (i.e. InThe.Future(controller.LongRunningTask).
It should work.
It doesn't due to a bug in the compiler - it doesn't implement the spec correctly with regards to output type inference and method groups.
It's a rather obscure bug, fortunately - unlikely to be a problem very often!
Jon
Jon,
You have no idea how often I hit into this bug
Jon
Thanks for the explanation, I've got it now :)
Comment preview