]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | [/ |
2 | (C) Copyright 2008-11 Anthony Williams. | |
3 | (C) Copyright 2012-2015 Vicente J. Botet Escriba. | |
4 | Distributed under the Boost Software License, Version 1.0. | |
5 | (See accompanying file LICENSE_1_0.txt or copy at | |
6 | http://www.boost.org/LICENSE_1_0.txt). | |
7 | ] | |
8 | ||
9 | [section:futures Futures] | |
10 | ||
11 | [template future_state_link[link_text] [link thread.synchronization.futures.reference.future_state [link_text]]] | |
12 | [def __uninitialized__ [future_state_link `boost::future_state::uninitialized`]] | |
13 | [def __ready__ [future_state_link `boost::future_state::ready`]] | |
14 | [def __waiting__ [future_state_link `boost::future_state::waiting`]] | |
15 | ||
16 | [def __future_uninitialized__ `boost::future_uninitialized`] | |
17 | [def __broken_promise__ `boost::broken_promise`] | |
18 | [def __future_already_retrieved__ `boost::future_already_retrieved`] | |
19 | [def __task_moved__ `boost::task_moved`] | |
20 | [def __task_already_started__ `boost::task_already_started`] | |
21 | [def __promise_already_satisfied__ `boost::promise_already_satisfied`] | |
22 | ||
23 | [def __thread_interrupted__ `boost::thread_interrupted`] | |
24 | ||
25 | ||
26 | [template unique_future_link[link_text] [link thread.synchronization.futures.reference.unique_future [link_text]]] | |
27 | [def __unique_future__ [unique_future_link `future`]] | |
28 | [def __unique_future `future`] | |
29 | ||
30 | [template unique_future_get_link[link_text] [link thread.synchronization.futures.reference.unique_future.get [link_text]]] | |
31 | [def __unique_future_get__ [unique_future_get_link `boost::future<R>::get()`]] | |
32 | ||
33 | [template unique_future_wait_link[link_text] [link thread.synchronization.futures.reference.unique_future.wait [link_text]]] | |
34 | [def __unique_future_wait__ [unique_future_wait_link `boost::future<R>::wait()`]] | |
35 | ||
36 | [template unique_future_is_ready_link[link_text] [link thread.synchronization.futures.reference.unique_future.is_ready [link_text]]] | |
37 | [def __unique_future_is_ready__ [unique_future_is_ready_link `boost::future<R>::is_ready()`]] | |
38 | ||
39 | [template unique_future_has_value_link[link_text] [link thread.synchronization.futures.reference.unique_future.has_value [link_text]]] | |
40 | [def __unique_future_has_value__ [unique_future_has_value_link `boost::future<R>::has_value()`]] | |
41 | ||
42 | [template unique_future_has_exception_link[link_text] [link thread.synchronization.futures.reference.unique_future.has_exception [link_text]]] | |
43 | [def __unique_future_has_exception__ [unique_future_has_exception_link `boost::future<R>::has_exception()`]] | |
44 | ||
45 | [template unique_future_get_state_link[link_text] [link thread.synchronization.futures.reference.unique_future.get_state [link_text]]] | |
46 | [def __unique_future_get_state__ [unique_future_get_state_link `boost::future<R>::get_state()`]] | |
47 | ||
48 | [template shared_future_link[link_text] [link thread.synchronization.futures.reference.shared_future [link_text]]] | |
49 | [def __shared_future__ [shared_future_link `boost::shared_future`]] | |
50 | ||
51 | [template shared_future_get_link[link_text] [link thread.synchronization.futures.reference.shared_future.get [link_text]]] | |
52 | [def __shared_future_get__ [shared_future_get_link `boost::shared_future<R>::get()`]] | |
53 | ||
54 | [template shared_future_wait_link[link_text] [link thread.synchronization.futures.reference.shared_future.wait [link_text]]] | |
55 | [def __shared_future_wait__ [shared_future_wait_link `boost::shared_future<R>::wait()`]] | |
56 | ||
57 | [template shared_future_is_ready_link[link_text] [link thread.synchronization.futures.reference.shared_future.is_ready [link_text]]] | |
58 | [def __shared_future_is_ready__ [shared_future_is_ready_link `boost::shared_future<R>::is_ready()`]] | |
59 | ||
60 | [template shared_future_has_value_link[link_text] [link thread.synchronization.futures.reference.shared_future.has_value [link_text]]] | |
61 | [def __shared_future_has_value__ [shared_future_has_value_link `boost::shared_future<R>::has_value()`]] | |
62 | ||
63 | [template shared_future_has_exception_link[link_text] [link thread.synchronization.futures.reference.shared_future.has_exception [link_text]]] | |
64 | [def __shared_future_has_exception__ [shared_future_has_exception_link `boost::shared_future<R>::has_exception()`]] | |
65 | ||
66 | [template shared_future_get_state_link[link_text] [link thread.synchronization.futures.reference.shared_future.get_state [link_text]]] | |
67 | [def __shared_future_get_state__ [shared_future_get_state_link `boost::shared_future<R>::get_state()`]] | |
68 | ||
69 | [template promise_link[link_text] [link thread.synchronization.futures.reference.promise [link_text]]] | |
70 | [def __promise__ [promise_link `boost::promise`]] | |
71 | ||
72 | [template packaged_task_link[link_text] [link thread.synchronization.futures.reference.packaged_task [link_text]]] | |
73 | [def __packaged_task__ [packaged_task_link `boost::packaged_task`]] | |
74 | [def __packaged_task [packaged_task_link `boost::packaged_task`]] | |
75 | ||
76 | [template wait_for_any_link[link_text] [link thread.synchronization.futures.reference.wait_for_any [link_text]]] | |
77 | [def __wait_for_any__ [wait_for_any_link `boost::wait_for_any()`]] | |
78 | ||
79 | [template wait_for_all_link[link_text] [link thread.synchronization.futures.reference.wait_for_all [link_text]]] | |
80 | [def __wait_for_all__ [wait_for_all_link `boost::wait_for_all()`]] | |
81 | ||
82 | ||
83 | [section:overview Overview] | |
84 | ||
85 | The futures library provides a means of handling synchronous future values, whether those values are generated by another thread, or | |
86 | on a single thread in response to external stimuli, or on-demand. | |
87 | ||
88 | This is done through the provision of four class templates: __unique_future__ and __shared_future__ which are used to retrieve the | |
89 | asynchronous results, and __promise__ and __packaged_task__ which are used to generate the asynchronous results. | |
90 | ||
91 | An instance of __unique_future__ holds the one and only reference to a result. Ownership can be transferred between instances using | |
92 | the move constructor or move-assignment operator, but at most one instance holds a reference to a given asynchronous result. When | |
93 | the result is ready, it is returned from __unique_future_get__ by rvalue-reference to allow the result to be moved or copied as | |
94 | appropriate for the type. | |
95 | ||
96 | On the other hand, many instances of __shared_future__ may reference the same result. Instances can be freely copied and assigned, | |
97 | and __shared_future_get__ returns a `const` reference so that multiple calls to __shared_future_get__ are safe. You can move an | |
98 | instance of __unique_future__ into an instance of __shared_future__, thus transferring ownership of the associated asynchronous | |
99 | result, but not vice-versa. | |
100 | ||
101 | `boost::async` is a simple way of running asynchronous tasks. A call to `boost::async` returns a __unique_future__ that will contain the result of the task. | |
102 | ||
103 | ||
104 | You can wait for futures either individually or with one of the __wait_for_any__ and __wait_for_all__ functions. | |
105 | ||
106 | [endsect] | |
107 | ||
108 | [section:creating Creating asynchronous values] | |
109 | ||
110 | You can set the value in a future with either a __promise__ or a __packaged_task__. A __packaged_task__ is a callable object that | |
111 | wraps a function or callable object. When the packaged task is invoked, it invokes the contained function in turn, and populates a | |
112 | future with the return value. This is an answer to the perennial question: "how do I return a value from a thread?": package the | |
113 | function you wish to run as a __packaged_task__ and pass the packaged task to the thread constructor. The future retrieved from the | |
114 | packaged task can then be used to obtain the return value. If the function throws an exception, that is stored in the future in | |
115 | place of the return value. | |
116 | ||
117 | int calculate_the_answer_to_life_the_universe_and_everything() | |
118 | { | |
119 | return 42; | |
120 | } | |
121 | ||
122 | boost::packaged_task<int> pt(calculate_the_answer_to_life_the_universe_and_everything); | |
123 | boost::__unique_future__<int> fi=pt.get_future(); | |
124 | ||
125 | boost::thread task(boost::move(pt)); // launch task on a thread | |
126 | ||
127 | fi.wait(); // wait for it to finish | |
128 | ||
129 | assert(fi.is_ready()); | |
130 | assert(fi.has_value()); | |
131 | assert(!fi.has_exception()); | |
132 | assert(fi.get_state()==boost::future_state::ready); | |
133 | assert(fi.get()==42); | |
134 | ||
135 | ||
136 | A __promise__ is a bit more low level: it just provides explicit functions to store a value or an exception in the associated | |
137 | future. A promise can therefore be used where the value may come from more than one possible source, or where a single operation may | |
138 | produce multiple values. | |
139 | ||
140 | boost::promise<int> pi; | |
141 | boost::__unique_future__<int> fi; | |
142 | fi=pi.get_future(); | |
143 | ||
144 | pi.set_value(42); | |
145 | ||
146 | assert(fi.is_ready()); | |
147 | assert(fi.has_value()); | |
148 | assert(!fi.has_exception()); | |
149 | assert(fi.get_state()==boost::future_state::ready); | |
150 | assert(fi.get()==42); | |
151 | ||
152 | [endsect] | |
153 | ||
154 | [section:lazy_futures Wait Callbacks and Lazy Futures] | |
155 | ||
156 | Both __promise__ and __packaged_task__ support ['wait callbacks] that are invoked when a thread blocks in a call to `wait()` or | |
157 | `timed_wait()` on a future that is waiting for the result from the __promise__ or __packaged_task__, in the thread that is doing the | |
158 | waiting. These can be set using the `set_wait_callback()` member function on the __promise__ or __packaged_task__ in question. | |
159 | ||
160 | This allows ['lazy futures] where the result is not actually computed until it is needed by some thread. In the example below, the | |
161 | call to `f.get()` invokes the callback `invoke_lazy_task`, which runs the task to set the value. If you remove the call to | |
162 | `f.get()`, the task is not ever run. | |
163 | ||
164 | int calculate_the_answer_to_life_the_universe_and_everything() | |
165 | { | |
166 | return 42; | |
167 | } | |
168 | ||
169 | void invoke_lazy_task(boost::packaged_task<int>& task) | |
170 | { | |
171 | try | |
172 | { | |
173 | task(); | |
174 | } | |
175 | catch(boost::task_already_started&) | |
176 | {} | |
177 | } | |
178 | ||
179 | int main() | |
180 | { | |
181 | boost::packaged_task<int> task(calculate_the_answer_to_life_the_universe_and_everything); | |
182 | task.set_wait_callback(invoke_lazy_task); | |
183 | boost::__unique_future__<int> f(task.get_future()); | |
184 | ||
185 | assert(f.get()==42); | |
186 | } | |
187 | ||
188 | ||
189 | [endsect] | |
190 | ||
191 | [section:at_thread_exit Handling Detached Threads and Thread Specific Variables] | |
192 | ||
193 | Detached threads pose a problem for objects with thread storage duration. | |
194 | If we use a mechanism other than `thread::__join` to wait for a __thread to complete its work - such as waiting for a future to be ready - | |
195 | then the destructors of thread specific variables will still be running after the waiting thread has resumed. | |
196 | This section explain how the standard mechanism can be used to make such synchronization safe by ensuring that the | |
197 | objects with thread storage duration are destroyed prior to the future being made ready. e.g. | |
198 | ||
199 | int find_the_answer(); // uses thread specific objects | |
200 | void thread_func(boost::promise<int>&& p) | |
201 | { | |
202 | p.set_value_at_thread_exit(find_the_answer()); | |
203 | } | |
204 | ||
205 | int main() | |
206 | { | |
207 | boost::promise<int> p; | |
208 | boost::thread t(thread_func,boost::move(p)); | |
209 | t.detach(); // we're going to wait on the future | |
210 | std::cout<<p.get_future().get()<<std::endl; | |
211 | } | |
212 | ||
213 | When the call to `get()` returns, we know that not only is the future value ready, but the thread specific variables | |
214 | on the other thread have also been destroyed. | |
215 | ||
216 | Such mechanisms are provided for `boost::condition_variable`, `boost::promise` and `boost::packaged_task`. e.g. | |
217 | ||
218 | void task_executor(boost::packaged_task<void(int)> task,int param) | |
219 | { | |
220 | task.make_ready_at_thread_exit(param); // execute stored task | |
221 | } // destroy thread specific and wake threads waiting on futures from task | |
222 | ||
223 | Other threads can wait on a future obtained from the task without having to worry about races due to the execution of | |
224 | destructors of the thread specific objects from the task's thread. | |
225 | ||
226 | boost::condition_variable cv; | |
227 | boost::mutex m; | |
228 | complex_type the_data; | |
229 | bool data_ready; | |
230 | ||
231 | void thread_func() | |
232 | { | |
233 | boost::unique_lock<std::mutex> lk(m); | |
234 | the_data=find_the_answer(); | |
235 | data_ready=true; | |
236 | boost::notify_all_at_thread_exit(cv,boost::move(lk)); | |
237 | } // destroy thread specific objects, notify cv, unlock mutex | |
238 | ||
239 | void waiting_thread() | |
240 | { | |
241 | boost::unique_lock<std::mutex> lk(m); | |
242 | while(!data_ready) | |
243 | { | |
244 | cv.wait(lk); | |
245 | } | |
246 | process(the_data); | |
247 | } | |
248 | ||
249 | The waiting thread is guaranteed that the thread specific objects used by `thread_func()` have been destroyed by the time | |
250 | `process(the_data)` is called. If the lock on `m` is released and re-acquired after setting `data_ready` and before calling | |
251 | `boost::notify_all_at_thread_exit()` then this does NOT hold, since the thread may return from the wait due to a | |
252 | spurious wake-up. | |
253 | ||
254 | [endsect] | |
255 | ||
256 | [section:async Executing asynchronously] | |
257 | ||
258 | `boost::async` is a simple way of running asynchronous tasks to make use of the available hardware concurrency. | |
259 | A call to `boost::async` returns a `boost::future` that will contain the result of the task. Depending on | |
260 | the launch policy, the task is either run asynchronously on its own thread or synchronously on whichever thread | |
261 | calls the `wait()` or `get()` member functions on that `future`. | |
262 | ||
263 | A launch policy of either boost::launch::async, which asks the runtime to create an asynchronous thread, | |
264 | or boost::launch::deferred, which indicates you simply want to defer the function call until a later time (lazy evaluation). | |
265 | This argument is optional - if you omit it your function will use the default policy. | |
266 | ||
267 | For example, consider computing the sum of a very large array. The first task is to not compute asynchronously when | |
268 | the overhead would be significant. The second task is to split the work into two pieces, one executed by the host | |
269 | thread and one executed asynchronously. | |
270 | ||
271 | ||
272 | int parallel_sum(int* data, int size) | |
273 | { | |
274 | int sum = 0; | |
275 | if ( size < 1000 ) | |
276 | for ( int i = 0; i < size; ++i ) | |
277 | sum += data[i]; | |
278 | else { | |
279 | auto handle = boost::async(parallel_sum, data+size/2, size-size/2); | |
280 | sum += parallel_sum(data, size/2); | |
281 | sum += handle.get(); | |
282 | } | |
283 | return sum; | |
284 | } | |
285 | ||
286 | ||
287 | ||
288 | [endsect] | |
289 | ||
290 | [section:shared Shared Futures] | |
291 | ||
292 | `shared_future` is designed to be shared between threads, | |
293 | that is to allow multiple concurrent get operations. | |
294 | ||
295 | [heading Multiple get] | |
296 | ||
297 | The second `get()` call in the following example is undefined. | |
298 | ||
299 | void bad_second_use( type arg ) { | |
300 | ||
301 | auto ftr = async( [=]{ return work( arg ); } ); | |
302 | if ( cond1 ) | |
303 | { | |
304 | use1( ftr.get() ); | |
305 | } else | |
306 | { | |
307 | use2( ftr.get() ); | |
308 | } | |
309 | use3( ftr.get() ); // second use is undefined | |
310 | } | |
311 | ||
312 | Using a `shared_future` solves the issue | |
313 | ||
314 | void good_second_use( type arg ) { | |
315 | ||
316 | shared_future<type> ftr = async( [=]{ return work( arg ); } ); | |
317 | if ( cond1 ) | |
318 | { | |
319 | use1( ftr.get() ); | |
320 | } else | |
321 | { | |
322 | use2( ftr.get() ); | |
323 | } | |
324 | use3( ftr.get() ); // second use is defined | |
325 | } | |
326 | ||
327 | [heading share()] | |
328 | ||
329 | Naming the return type when declaring the `shared_future` is needed; auto is not available within template argument lists. | |
330 | Here `share()` could be used to simplify the code | |
331 | ||
332 | void better_second_use( type arg ) { | |
333 | ||
334 | auto ftr = async( [=]{ return work( arg ); } ).share(); | |
335 | if ( cond1 ) | |
336 | { | |
337 | use1( ftr.get() ); | |
338 | } else | |
339 | { | |
340 | use2( ftr.get() ); | |
341 | } | |
342 | use3( ftr.get() ); // second use is defined | |
343 | } | |
344 | ||
345 | [heading Writing on get()] | |
346 | ||
347 | The user can either read or write the future variable. | |
348 | ||
349 | void write_to_get( type arg ) { | |
350 | ||
351 | auto ftr = async( [=]{ return work( arg ); } ).share(); | |
352 | if ( cond1 ) | |
353 | { | |
354 | use1( ftr.get() ); | |
355 | } else | |
356 | { | |
357 | if ( cond2 ) | |
358 | use2( ftr.get() ); | |
359 | else | |
360 | ftr.get() = something(); // assign to non-const reference. | |
361 | } | |
362 | use3( ftr.get() ); // second use is defined | |
363 | } | |
364 | ||
365 | This works because the `shared_future<>::get()` function returns a non-const reference to the appropriate storage. | |
366 | Of course the access to this storage must be ensured by the user. The library doesn't ensure the access to the internal storage is thread safe. | |
367 | ||
368 | There has been some work by the C++ standard committee on an `atomic_future` that behaves as an `atomic` variable, that is thread_safe, | |
369 | and a `shared_future` that can be shared between several threads, but there were not enough consensus and time to get it ready for C++11. | |
370 | ||
371 | [endsect] | |
372 | ||
373 | [section:make_ready_future Making immediate futures easier] | |
374 | ||
375 | Some functions may know the value at the point of construction. In these cases the value is immediately available, | |
376 | but needs to be returned as a future or shared_future. By using make_ready_future a future | |
377 | can be created which holds a pre-computed result in its shared state. | |
378 | ||
379 | Without these features it is non-trivial to create a future directly from a value. | |
380 | First a promise must be created, then the promise is set, and lastly the future is retrieved from the promise. | |
381 | This can now be done with one operation. | |
382 | ||
383 | [heading make_ready_future] | |
384 | ||
385 | This function creates a future for a given value. If no value is given then a future<void> is returned. | |
386 | This function is primarily useful in cases where sometimes, the return value is immediately available, but sometimes | |
387 | it is not. The example below illustrates, that in an error path the value is known immediately, however in other paths | |
388 | the function must return an eventual value represented as a future. | |
389 | ||
390 | ||
391 | boost::future<int> compute(int x) | |
392 | { | |
393 | if (x == 0) return boost::make_ready_future(0); | |
394 | if (x < 0) return boost::make_ready_future<int>(std::logic_error("Error")); | |
395 | boost::future<int> f1 = boost::async([]() { return x+1; }); | |
396 | return f1; | |
397 | } | |
398 | ||
399 | There are two variations of this function. The first takes a value of any type, and returns a future of that type. | |
400 | The input value is passed to the shared state of the returned future. The second version takes no input and returns a future<void>. | |
401 | ||
402 | [endsect] | |
403 | ||
404 | [section:then Associating future continuations] | |
405 | ||
406 | In asynchronous programming, it is very common for one asynchronous operation, on completion, to invoke a second | |
407 | operation and pass data to it. The current C++ standard does not allow one to register a continuation to a future. | |
408 | With `.then`, instead of waiting for the result, a continuation is "attached" to the asynchronous operation, which is | |
409 | invoked when the result is ready. Continuations registered using the `.then` function will help to avoid blocking waits | |
410 | or wasting threads on polling, greatly improving the responsiveness and scalability of an application. | |
411 | ||
412 | `future.then()` provides the ability to sequentially compose two futures by declaring one to be the continuation of another. | |
413 | With `.then()` the antecedent future is ready (has a value or exception stored in the shared state) before the continuation | |
414 | starts as instructed by the lambda function. | |
415 | ||
416 | In the example below the `future<string>` `f2` is registered to be a continuation of `future<int>` `f1` using the `.then()` member | |
417 | function. This operation takes a lambda function which describes how `f2` should proceed after `f1` is ready. | |
418 | ||
419 | ||
420 | #include <boost/thread/future.hpp> | |
421 | using namespace boost; | |
422 | int main() | |
423 | { | |
424 | future<int> f1 = async([]() { return 123; }); | |
425 | future<string> f2 = f1.then([](future<int> f) { return f.get().to_string(); // here .get() won't block }); | |
426 | } | |
427 | ||
428 | One key feature of this function is the ability to chain multiple asynchronous operations. In asynchronous programming, | |
429 | it's common to define a sequence of operations, in which each continuation executes only when the previous one completes. | |
430 | In some cases, the antecedent future produces a value that the continuation accepts as input. By using `future.then()`, | |
431 | creating a chain of continuations becomes straightforward and intuitive: | |
432 | ||
433 | myFuture.then(...).then(...).then(...). | |
434 | ||
435 | Some points to note are: | |
436 | ||
437 | * Each continuation will not begin until the preceding has completed. | |
438 | * If an exception is thrown, the following continuation can handle it in a try-catch block | |
439 | ||
440 | ||
441 | Input Parameters: | |
442 | ||
443 | * Lambda function: One option which can be considered is to take two functions, one for | |
444 | success and one for error handling. However this option has not been retained for the moment. | |
445 | The lambda function takes a future as its input which carries the exception | |
446 | through. This makes propagating exceptions straightforward. This approach also simplifies the chaining of continuations. | |
447 | * Executor: Providing an overload to `.then`, to take an executor reference places great flexibility over the execution | |
448 | of the future in the programmer's hand. As described above, often taking a launch policy is not sufficient for powerful | |
449 | asynchronous operations. The lifetime of the executor must outlive the continuation. | |
450 | * Launch policy: if the additional flexibility that the executor provides is not required. | |
451 | ||
452 | Return values: The decision to return a future was based primarily on the ability to chain multiple continuations using | |
453 | `.then()`. This benefit of composability gives the programmer incredible control and flexibility over their code. Returning | |
454 | a `future` object rather than a `shared_future` is also a much cheaper operation thereby improving performance. A | |
455 | `shared_future` object is not necessary to take advantage of the chaining feature. It is also easy to go from a `future` | |
456 | to a `shared_future` when needed using future::share(). | |
457 | ||
458 | ||
459 | [endsect] | |
460 | ||
461 | ||
462 | [include future_ref.qbk] | |
463 | ||
464 | [endsect] |