root/trunk/future/future.d

Revision 59, 5.4 kB (checked in by lindquist, 7 years ago)

merged the dsssify branch into trunk noone has complained. some module identifiers have changed.
updated tinyjpeg to version 20070609

Line 
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 }
Note: See TracBrowser for help on using the browser.