Implementation of C++0x's "future" syntax

Daniel Keep daniel.keep+lists at gmail.com
Sun Jan 14 08:21:46 PST 2007


I was poking around the stuff on C++0x (which I was led to because I was 
poking around STL (because I was poking around for a good D container 
library)) when I found out about "future".

For those that don't know, the idea is to make asynchronous function 
calls nice and easy.  The syntax on Wikipedia is this:

 > int function( int parameter ) ;
 > // Calls the function and immediately returns.
 > IdThreadType<function> IdThread = future function(parameter) ;
 > // Waits for the function result.
 >int ret = wait IdThread ;

Anyway, I had a similar idea a while back, and figured now would be the 
time to see if I could do it.  So I did.  Here's the equivalent in D:

 > int func( int parameter ); // "function"'s a keyword :P
 > // Calls the function and immediately returns.
 > FutureResult!(func) result = future(&func, parameter); // "auto" helps
 > // Waits for the function result.
 > int ret = result.value;

(It also works for functions that have no return type: you would use 
"result.wait" instead.)

Not too shabby considering that C++0x is set to be released in 2009. 
That makes us at least two years ahead of the game :)

There are only two improvements I can think of off the top of my head:

1) Investigate whether "future!(func)(args)" is more efficient.
2) Use a thread pool (if the standard library ever gets one).

Enjoy.

	-- Daniel

P.S.  Kinda OT: has anyone else ever thought that being able to declare 
void variables would be *really* handy in templates?  I keep having to 
put in special cases for void... *grumble, grumble*

future.d:
--------------------

/**
  * future -- Simple concurrent execution
  * Written by Daniel Keep.
  * Released to the Public Domain.
  */
module future;

import std.thread : Thread;

/**
  * This structure is what we'll use to store the results of our "future"
  * calls in.  All that nasty private stuff is basically just bookkeeping
  * for the thread we'll spawn off, which allows us to kick off the
  * thread, and then store the result when it's finished.
  */
struct FutureResult(tFn, tArgs...)
{
     private
     {
         alias tTaskResult!(tFn) tResult; // result type of future call

         tFn fn; // function to call
         tArgs args; // arguments to pass to it
         bool got_result = false; // have we got a result yet?
         Thread thread; // which thread are we using?

         static if( !is( tResult == void ) )
             tResult result; // what is the result?

         // start is used as the entry point to the thread
         int start()
         {
             static if( !is( tResult == void ) )
                 result = fn(args);
             else
                 fn(args);

             got_result = true;
             return 0;
         }
     }

     static if( !is( tResult == void ) )
     {
         /**
          * Either returns the value if we already know what it is, or
          * else block until we do.
          */
         tResult value()
         {
             while( !got_result )
                 Thread.yield();
             return result;
         }
     }
     else
     {
         /**
          * Wait until the function call has returned.
          */
         void wait()
         {
             while( !got_result )
                 Thread.yield();
         }
     }
}

private template tTaskResult(tFn)
{
     alias tiTaskResult!(tFn).result tTaskResult;
}

private template tiTaskResult(tFn)
{
     static if( is( tFn tRealFn == delegate ) )
         alias tiTaskResult!(tRealFn).result result;

     else static if( is( tFn tReturn == return ) )
         alias tReturn result;

     else
         static assert("Cannot derive result from function type.");
}

/**
  * Performs a function call asynchronously.  The "future" refers both
  * the fact that the result of this function is intended to be used at
  * some time in the future, and that its' basically what the "future"
  * keyword in C++0x is supposed to do.
  *
  * The way it works is quite simple: when you have some lengthy function
  * call to make, call the function as early as possible, and store away
  * the return.  Then, when you actually need the result, you ask for it.
  * If the function call completed in the background, it will return the
  * result immediately.  If it hasn't, then it will block until the
  * result is available.  For example:
  *
  * ---------------------------------------------------------------------
  * import std.stdio;
  * import etc.future;
  *
  * int meaningOfLife()
  * {
  *     // Some long, complex calculations.
  *     return 42;
  * }
  *
  * void main()
  * {
  *     auto meaning = future(meaningOfLife);
  *
  *     // More complicated computations.
  *
  *     writefln("Meaning of life is: %d", meaning.value);
  * }
  * ---------------------------------------------------------------------
  */
FutureResult!(tFn, tArgs) future(tFn, tArgs...)(tFn fn, tArgs args)
{
     FutureResult!(tFn, tArgs) result;

     result.fn = fn;

     foreach( i,a ; args )
         result.args[i] = a;

     result.thread = new Thread(&result.start);
     result.thread.start();

     return result;
}

unittest
{
     int meaningOfLife(int init)
     {
         int result = init;
         for( int i=1; i<10_000; i++ )
             result = (result * i) + (i << 1) - (result >> 1);

         // Bah; stuff that!
         return 42;
     }

     void shortPause(int loops)
     {
         while( loops )
             loops--;
     }

     auto life = future(&meaningOfLife, 0xBeef);
     assert( life.value == 42 );

     auto pause = future(&shortPause, 1_000);
     pause.wait();
}

version( future_main )
{
     import std.stdio;

     void main()
     {
         writefln("Tests complete.");
     }
}



More information about the Digitalmars-d-announce mailing list