| 1 |
/** |
|---|
| 2 |
* future -- Simple concurrent execution |
|---|
| 3 |
* Written by Daniel Keep. |
|---|
| 4 |
* Released to the Public Domain. |
|---|
| 5 |
* |
|---|
| 6 |
* Quick Summary |
|---|
| 7 |
* ============= |
|---|
| 8 |
* |
|---|
| 9 |
* auto result = future(some_function, arg1, arg2, more_args); |
|---|
| 10 |
* |
|---|
| 11 |
* This executes "some_function" with the specified arguments |
|---|
| 12 |
* asynchronously, allowing you to do other things while it runs. When you |
|---|
| 13 |
* need the return value of the function call, you can use: |
|---|
| 14 |
* |
|---|
| 15 |
* result.value |
|---|
| 16 |
* |
|---|
| 17 |
* This will BLOCK your current thread until the asynchronous call has |
|---|
| 18 |
* finished. So the idea is to start off asynchronous calls as early as |
|---|
| 19 |
* possible. |
|---|
| 20 |
* |
|---|
| 21 |
* Or, if the function has no return value, but you need to wait until its' |
|---|
| 22 |
* finished, use this: |
|---|
| 23 |
* |
|---|
| 24 |
* result.wait |
|---|
| 25 |
* |
|---|
| 26 |
* The code below should be fairly clear, so if you're wondering what it's |
|---|
| 27 |
* really doing under the hood, read the code :) |
|---|
| 28 |
*/ |
|---|
| 29 |
module future; |
|---|
| 30 |
|
|---|
| 31 |
import std.thread : Thread; |
|---|
| 32 |
|
|---|
| 33 |
/** |
|---|
| 34 |
* This class is what we'll use to store the results of our "future" |
|---|
| 35 |
* calls in. All that nasty private stuff is basically just bookkeeping for |
|---|
| 36 |
* the thread we'll spawn off, which allows us to kick off the thread, and |
|---|
| 37 |
* then store the result when it's finished. |
|---|
| 38 |
*/ |
|---|
| 39 |
class FutureResult(tFn, tArgs...) |
|---|
| 40 |
{ |
|---|
| 41 |
private |
|---|
| 42 |
{ |
|---|
| 43 |
alias tTaskResult!(tFn) tResult; // result type of future call |
|---|
| 44 |
|
|---|
| 45 |
tFn fn; // function to call |
|---|
| 46 |
tArgs args; // arguments to pass to it |
|---|
| 47 |
bool got_result = false; // have we got a result yet? |
|---|
| 48 |
Thread thread; // which thread are we using? |
|---|
| 49 |
|
|---|
| 50 |
static if( !is( tResult == void ) ) |
|---|
| 51 |
tResult result; // what is the result? |
|---|
| 52 |
|
|---|
| 53 |
// start is used as the entry point to the thread. All it's doing |
|---|
| 54 |
// is calling the function, saving the return value (if there is |
|---|
| 55 |
// one), and then setting the "got_result" flag. |
|---|
| 56 |
int start() |
|---|
| 57 |
{ |
|---|
| 58 |
static if( !is( tResult == void ) ) |
|---|
| 59 |
result = fn(args); |
|---|
| 60 |
else |
|---|
| 61 |
fn(args); |
|---|
| 62 |
|
|---|
| 63 |
got_result = true; |
|---|
| 64 |
return 0; |
|---|
| 65 |
} |
|---|
| 66 |
} |
|---|
| 67 |
|
|---|
| 68 |
static if( !is( tResult == void ) ) |
|---|
| 69 |
{ |
|---|
| 70 |
/** |
|---|
| 71 |
* Either returns the value if we already know what it is, or else |
|---|
| 72 |
* block until we do. |
|---|
| 73 |
*/ |
|---|
| 74 |
tResult value() |
|---|
| 75 |
{ |
|---|
| 76 |
while( !got_result ) |
|---|
| 77 |
Thread.yield(); |
|---|
| 78 |
return result; |
|---|
| 79 |
} |
|---|
| 80 |
} |
|---|
| 81 |
else |
|---|
| 82 |
{ |
|---|
| 83 |
/** |
|---|
| 84 |
* Wait until the function call has returned. |
|---|
| 85 |
*/ |
|---|
| 86 |
void wait() |
|---|
| 87 |
{ |
|---|
| 88 |
while( !got_result ) |
|---|
| 89 |
Thread.yield(); |
|---|
| 90 |
} |
|---|
| 91 |
} |
|---|
| 92 |
} |
|---|
| 93 |
|
|---|
| 94 |
/* These two templates are used to get the return type of a function. In a |
|---|
| 95 |
* case of "do as I say, not as I do", YOU should use std.traits.ReturnType |
|---|
| 96 |
* in your code :3 |
|---|
| 97 |
*/ |
|---|
| 98 |
private template tTaskResult(tFn) |
|---|
| 99 |
{ |
|---|
| 100 |
alias tiTaskResult!(tFn).result tTaskResult; |
|---|
| 101 |
} |
|---|
| 102 |
|
|---|
| 103 |
private template tiTaskResult(tFn) |
|---|
| 104 |
{ |
|---|
| 105 |
static if( is( tFn tRealFn == delegate ) ) |
|---|
| 106 |
alias tiTaskResult!(tRealFn).result result; |
|---|
| 107 |
|
|---|
| 108 |
else static if( is( tFn tReturn == return ) ) |
|---|
| 109 |
alias tReturn result; |
|---|
| 110 |
|
|---|
| 111 |
else |
|---|
| 112 |
static assert("Cannot derive result from function type."); |
|---|
| 113 |
} |
|---|
| 114 |
|
|---|
| 115 |
/** |
|---|
| 116 |
* Performs a function call asynchronously. The "future" refers both to the |
|---|
| 117 |
* fact that the result of this function is intended to be used at some time |
|---|
| 118 |
* in the future, and that its' basically what the "future" keyword in C++0x |
|---|
| 119 |
* is supposed to do. |
|---|
| 120 |
* |
|---|
| 121 |
* The way it works is quite simple: when you have some lengthy function |
|---|
| 122 |
* call to make, call the function as early as possible, and store away the |
|---|
| 123 |
* return. Then, when you actually need the result, you ask for it. If the |
|---|
| 124 |
* function call completed in the background, it will return the result |
|---|
| 125 |
* immediately. If it hasn't, then it will block until the result is |
|---|
| 126 |
* available. For example: |
|---|
| 127 |
* |
|---|
| 128 |
* ------------------------------------------------------------------------- |
|---|
| 129 |
* import std.stdio; |
|---|
| 130 |
* import etc.future; |
|---|
| 131 |
* |
|---|
| 132 |
* int meaningOfLife() |
|---|
| 133 |
* { |
|---|
| 134 |
* // Some long, complex calculations. |
|---|
| 135 |
* return 42; |
|---|
| 136 |
* } |
|---|
| 137 |
* |
|---|
| 138 |
* void main() |
|---|
| 139 |
* { |
|---|
| 140 |
* auto meaning = future(meaningOfLife); |
|---|
| 141 |
* |
|---|
| 142 |
* // More complicated computations. |
|---|
| 143 |
* |
|---|
| 144 |
* writefln("Meaning of life is: %d", meaning.value); |
|---|
| 145 |
* } |
|---|
| 146 |
* ------------------------------------------------------------------------- |
|---|
| 147 |
*/ |
|---|
| 148 |
FutureResult!(tFn, tArgs) future(tFn, tArgs...)(tFn fn, tArgs args) |
|---|
| 149 |
{ |
|---|
| 150 |
// Create and fill in the result class instance. |
|---|
| 151 |
auto result = new FutureResult!(tFn, tArgs); |
|---|
| 152 |
|
|---|
| 153 |
result.fn = fn; |
|---|
| 154 |
|
|---|
| 155 |
foreach( i,a ; args ) |
|---|
| 156 |
result.args[i] = a; |
|---|
| 157 |
|
|---|
| 158 |
// Kick off the thread, and return the instance. |
|---|
| 159 |
result.thread = new Thread(&result.start); |
|---|
| 160 |
result.thread.start(); |
|---|
| 161 |
|
|---|
| 162 |
return result; |
|---|
| 163 |
} |
|---|
| 164 |
|
|---|
| 165 |
unittest |
|---|
| 166 |
{ |
|---|
| 167 |
int meaningOfLife(int init) |
|---|
| 168 |
{ |
|---|
| 169 |
int result = init; |
|---|
| 170 |
for( int i=1; i<10_000; i++ ) |
|---|
| 171 |
result = (result * i) + (i << 1) - (result >> 1); |
|---|
| 172 |
|
|---|
| 173 |
// Bah; stuff that! |
|---|
| 174 |
return 42; |
|---|
| 175 |
} |
|---|
| 176 |
|
|---|
| 177 |
void shortPause(int loops) |
|---|
| 178 |
{ |
|---|
| 179 |
while( loops ) |
|---|
| 180 |
loops--; |
|---|
| 181 |
} |
|---|
| 182 |
|
|---|
| 183 |
scope life = future(&meaningOfLife, 0xBeef); |
|---|
| 184 |
assert( life.value == 42 ); |
|---|
| 185 |
|
|---|
| 186 |
scope pause = future(&shortPause, 1_000); |
|---|
| 187 |
pause.wait(); |
|---|
| 188 |
} |
|---|
| 189 |
|
|---|
| 190 |
// Compile with "-version=future_main -unittest" to run this module by |
|---|
| 191 |
// itself. |
|---|
| 192 |
version( future_main ) |
|---|
| 193 |
{ |
|---|
| 194 |
import std.stdio; |
|---|
| 195 |
|
|---|
| 196 |
void main() |
|---|
| 197 |
{ |
|---|
| 198 |
writefln("Tests complete."); |
|---|
| 199 |
} |
|---|
| 200 |
} |
|---|