summaryrefslogtreecommitdiff
path: root/libs/thread/doc/futures.qbk
blob: f05bde5550a401e44dcaf190df9875ff17ae6985 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
[/
  (C) Copyright 2008-11 Anthony Williams.
  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE_1_0.txt or copy at
  http://www.boost.org/LICENSE_1_0.txt).
]

[section:futures Futures]

[template future_state_link[link_text] [link thread.synchronization.futures.reference.future_state [link_text]]]
[def __uninitialized__ [future_state_link `boost::future_state::uninitialized`]]
[def __ready__ [future_state_link `boost::future_state::ready`]]
[def __waiting__ [future_state_link `boost::future_state::waiting`]]

[def __future_uninitialized__ `boost::future_uninitialized`]
[def __broken_promise__ `boost::broken_promise`]
[def __future_already_retrieved__ `boost::future_already_retrieved`]
[def __task_moved__ `boost::task_moved`]
[def __task_already_started__ `boost::task_already_started`]
[def __promise_already_satisfied__ `boost::promise_already_satisfied`]

[def __thread_interrupted__ `boost::thread_interrupted`]


[template unique_future_link[link_text] [link thread.synchronization.futures.reference.unique_future [link_text]]]
[def __unique_future__ [unique_future_link `future`]]
[def __unique_future `future`]

[template unique_future_get_link[link_text] [link thread.synchronization.futures.reference.unique_future.get [link_text]]]
[def __unique_future_get__ [unique_future_get_link `boost::future<R>::get()`]]

[template unique_future_wait_link[link_text] [link thread.synchronization.futures.reference.unique_future.wait [link_text]]]
[def __unique_future_wait__ [unique_future_wait_link `boost::future<R>::wait()`]]

[template unique_future_is_ready_link[link_text] [link thread.synchronization.futures.reference.unique_future.is_ready [link_text]]]
[def __unique_future_is_ready__ [unique_future_is_ready_link `boost::future<R>::is_ready()`]]

[template unique_future_has_value_link[link_text] [link thread.synchronization.futures.reference.unique_future.has_value [link_text]]]
[def __unique_future_has_value__ [unique_future_has_value_link `boost::future<R>::has_value()`]]

[template unique_future_has_exception_link[link_text] [link thread.synchronization.futures.reference.unique_future.has_exception [link_text]]]
[def __unique_future_has_exception__ [unique_future_has_exception_link `boost::future<R>::has_exception()`]]

[template unique_future_get_state_link[link_text] [link thread.synchronization.futures.reference.unique_future.get_state [link_text]]]
[def __unique_future_get_state__ [unique_future_get_state_link `boost::future<R>::get_state()`]]

[template shared_future_link[link_text] [link thread.synchronization.futures.reference.shared_future [link_text]]]
[def __shared_future__ [shared_future_link `boost::shared_future`]]

[template shared_future_get_link[link_text] [link thread.synchronization.futures.reference.shared_future.get [link_text]]]
[def __shared_future_get__ [shared_future_get_link `boost::shared_future<R>::get()`]]

[template shared_future_wait_link[link_text] [link thread.synchronization.futures.reference.shared_future.wait [link_text]]]
[def __shared_future_wait__ [shared_future_wait_link `boost::shared_future<R>::wait()`]]

[template shared_future_is_ready_link[link_text] [link thread.synchronization.futures.reference.shared_future.is_ready [link_text]]]
[def __shared_future_is_ready__ [shared_future_is_ready_link `boost::shared_future<R>::is_ready()`]]

[template shared_future_has_value_link[link_text] [link thread.synchronization.futures.reference.shared_future.has_value [link_text]]]
[def __shared_future_has_value__ [shared_future_has_value_link `boost::shared_future<R>::has_value()`]]

[template shared_future_has_exception_link[link_text] [link thread.synchronization.futures.reference.shared_future.has_exception [link_text]]]
[def __shared_future_has_exception__ [shared_future_has_exception_link `boost::shared_future<R>::has_exception()`]]

[template shared_future_get_state_link[link_text] [link thread.synchronization.futures.reference.shared_future.get_state [link_text]]]
[def __shared_future_get_state__ [shared_future_get_state_link `boost::shared_future<R>::get_state()`]]

[template promise_link[link_text] [link thread.synchronization.futures.reference.promise [link_text]]]
[def __promise__ [promise_link `boost::promise`]]

[template packaged_task_link[link_text] [link thread.synchronization.futures.reference.packaged_task [link_text]]]
[def __packaged_task__ [packaged_task_link `boost::packaged_task`]]
[def __packaged_task [packaged_task_link `boost::packaged_task`]]

[template wait_for_any_link[link_text] [link thread.synchronization.futures.reference.wait_for_any [link_text]]]
[def __wait_for_any__ [wait_for_any_link `boost::wait_for_any()`]]

[template wait_for_all_link[link_text] [link thread.synchronization.futures.reference.wait_for_all [link_text]]]
[def __wait_for_all__ [wait_for_all_link `boost::wait_for_all()`]]


[section:overview Overview]

The futures library provides a means of handling synchronous future values, whether those values are generated by another thread, or
on a single thread in response to external stimuli, or on-demand.

This is done through the provision of four class templates: __unique_future__ and __shared_future__ which are used to retrieve the
asynchronous results, and __promise__ and __packaged_task__ which are used to generate the asynchronous results.

An instance of __unique_future__ holds the one and only reference to a result. Ownership can be transferred between instances using
the move constructor or move-assignment operator, but at most one instance holds a reference to a given asynchronous result. When
the result is ready, it is returned from __unique_future_get__ by rvalue-reference to allow the result to be moved or copied as
appropriate for the type.

On the other hand, many instances of __shared_future__ may reference the same result. Instances can be freely copied and assigned,
and __shared_future_get__ returns a `const` reference so that multiple calls to __shared_future_get__ are safe. You can move an
instance of __unique_future__ into an instance of __shared_future__, thus transferring ownership of the associated asynchronous
result, but not vice-versa.

`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.


You can wait for futures either individually or with one of the __wait_for_any__ and __wait_for_all__ functions.

[endsect]

[section:creating Creating asynchronous values]

You can set the value in a future with either a __promise__ or a __packaged_task__. A __packaged_task__ is a callable object that
wraps a function or callable object. When the packaged task is invoked, it invokes the contained function in turn, and populates a
future with the return value. This is an answer to the perennial question: "how do I return a value from a thread?": package the
function you wish to run as a __packaged_task__ and pass the packaged task to the thread constructor. The future retrieved from the
packaged task can then be used to obtain the return value. If the function throws an exception, that is stored in the future in
place of the return value.

    int calculate_the_answer_to_life_the_universe_and_everything()
    {
        return 42;
    }

    boost::packaged_task<int> pt(calculate_the_answer_to_life_the_universe_and_everything);
    boost::__unique_future__<int> fi=pt.get_future();

    boost::thread task(boost::move(pt)); // launch task on a thread

    fi.wait(); // wait for it to finish
        
    assert(fi.is_ready());
    assert(fi.has_value());
    assert(!fi.has_exception());
    assert(fi.get_state()==boost::future_state::ready);
    assert(fi.get()==42);


A __promise__ is a bit more low level: it just provides explicit functions to store a value or an exception in the associated
future. A promise can therefore be used where the value may come from more than one possible source, or where a single operation may
produce multiple values.

    boost::promise<int> pi;
    boost::__unique_future__<int> fi;
    fi=pi.get_future();

    pi.set_value(42);
    
    assert(fi.is_ready());
    assert(fi.has_value());
    assert(!fi.has_exception());
    assert(fi.get_state()==boost::future_state::ready);
    assert(fi.get()==42);

[endsect]

[section:lazy_futures Wait Callbacks and Lazy Futures]

Both __promise__ and __packaged_task__ support ['wait callbacks] that are invoked when a thread blocks in a call to `wait()` or
`timed_wait()` on a future that is waiting for the result from the __promise__ or __packaged_task__, in the thread that is doing the
waiting. These can be set using the `set_wait_callback()` member function on the __promise__ or __packaged_task__ in question.

This allows ['lazy futures] where the result is not actually computed until it is needed by some thread. In the example below, the
call to `f.get()` invokes the callback `invoke_lazy_task`, which runs the task to set the value. If you remove the call to
`f.get()`, the task is not ever run.

    int calculate_the_answer_to_life_the_universe_and_everything()
    {
        return 42;
    }

    void invoke_lazy_task(boost::packaged_task<int>& task)
    {
        try
        {
            task();
        }
        catch(boost::task_already_started&)
        {}
    }

    int main()
    {
        boost::packaged_task<int> task(calculate_the_answer_to_life_the_universe_and_everything);
        task.set_wait_callback(invoke_lazy_task);
        boost::__unique_future__<int> f(task.get_future());

        assert(f.get()==42);
    }


[endsect]

[section:at_thread_exit Handling Detached Threads and Thread Specific Variables]

Detached threads pose a problem for objects with thread storage duration. 
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 - 
then the destructors of thread specific variables will still be running after the waiting thread has resumed. 
This section explain how the standard mechanism can be used to make such synchronization safe by ensuring that the 
objects with thread storage duration are destroyed prior to the future being made ready. e.g.

  int find_the_answer(); // uses thread specific objects
  void thread_func(boost::promise<int>&& p)
  {
      p.set_value_at_thread_exit(find_the_answer());
  }
  
  int main()
  {
      boost::promise<int> p;
      boost::thread t(thread_func,boost::move(p));
      t.detach(); // we're going to wait on the future
      std::cout<<p.get_future().get()<<std::endl;
  }

When the call to `get()` returns, we know that not only is the future value ready, but the thread specific variables 
on the other thread have also been destroyed.

Such mechanisms are provided for `boost::condition_variable`, `boost::promise` and `boost::packaged_task`. e.g.

  void task_executor(boost::packaged_task<void(int)> task,int param)
  {
      task.make_ready_at_thread_exit(param); // execute stored task
  } // destroy thread specific and wake threads waiting on futures from task

Other threads can wait on a future obtained from the task without having to worry about races due to the execution of 
destructors of the thread specific objects from the task's thread.

  boost::condition_variable cv;
  boost::mutex m;
  complex_type the_data;
  bool data_ready;
  
  void thread_func()
  {
      boost::unique_lock<std::mutex> lk(m);
      the_data=find_the_answer();
      data_ready=true;
      boost::notify_all_at_thread_exit(cv,boost::move(lk));
  } // destroy thread specific objects, notify cv, unlock mutex
  
  void waiting_thread()
  {
      boost::unique_lock<std::mutex> lk(m);
      while(!data_ready)
      {
          cv.wait(lk);
      }
      process(the_data);
  }

The waiting thread is guaranteed that the thread specific objects used by `thread_func()` have been destroyed by the time 
`process(the_data)` is called. If the lock on `m` is released and re-acquired after setting `data_ready` and before calling 
`boost::notify_all_at_thread_exit()` then this does NOT hold, since the thread may return from the wait due to a 
spurious wake-up.

[endsect]

[section:async Executing asynchronously]

`boost::async` is a simple way of running asynchronous tasks to make use of the available hardware concurrency. 
A call to `boost::async` returns a `boost::future` that will contain the result of the task. Depending on 
the launch policy, the task is either run asynchronously on its own thread or synchronously on whichever thread
calls the `wait()` or `get()` member functions on that `future`.

A launch policy of either boost::launch::async, which asks the runtime to create an asynchronous thread,
or boost::launch::deferred, which indicates you simply want to defer the function call until a later time (lazy evaluation). 
This argument is optional - if you omit it your function will use the default policy.

For example, consider computing the sum of a very large array. The first task is to not compute asynchronously when 
the overhead would be significant. The second task is to split the work into two pieces, one executed by the host 
thread and one executed asynchronously.


    int parallel_sum(int* data, int size)
    {
      int sum = 0;
      if ( size < 1000 )
        for ( int i = 0; i < size; ++i )
          sum += data[i];
      else {
        auto handle = boost::async(parallel_sum, data+size/2, size-size/2);
        sum += parallel_sum(data, size/2);
        sum += handle.get();
      }
      return sum;
    }



[endsect]

[section:shared Shared Futures]

`shared_future` is designed to be shared between threads, 
that is to allow multiple concurrent get operations.

[heading Multiple get]

The second `get()` call in the following example is undefined.  

  void bad_second_use( type arg ) {
      
    auto ftr = async( [=]{ return work( arg ); } );
      if ( cond1 ) 
      {
          use1( ftr.get() );
      } else 
      {
          use2( ftr.get() );
      }
      use3( ftr.get() ); // second use is undefined
  }
    
Using a `shared_future` solves the issue

  void good_second_use( type arg ) {
      
     shared_future<type> ftr = async( [=]{ return work( arg ); } );
      if ( cond1 ) 
      {
          use1( ftr.get() );
      } else 
      {
          use2(  ftr.get() );
      }
      use3( ftr.get() ); // second use is defined
  }

[heading share()]
  
Namming the return type when declaring the `shared_future` is needed; auto is not available within template argument lists. 
Here `share()` could be used to simplify the code

  void better_second_use( type arg ) {
      
     auto ftr = async( [=]{ return work( arg ); } ).share();
      if ( cond1 ) 
      {
          use1( ftr.get() );
      } else 
      {
          use2(  ftr.get() );
      }
      use3( ftr.get() ); // second use is defined
  }
 
[heading Writing on get()]

The user can either read or write the future avariable. 

  void write_to_get( type arg ) {
      
     auto ftr = async( [=]{ return work( arg ); } ).share();
      if ( cond1 ) 
      {
          use1( ftr.get() );
      } else 
      {
        if ( cond2 ) 
          use2(  ftr.get() );
        else
          ftr.get() = something(); // assign to non-const reference.  
      }
      use3( ftr.get() ); // second use is defined
  }
     
This works because the `shared_future<>::get()` function returns a non-const reference to the appropriate storage.
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.

There has been some work by the C++ standard committe on an `atomic_future` that behaves as an `atomic` variable, that is is thread_safe, 
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.       

[endsect]

[section:make_ready_future Making immediate futures easier]

Some functions may know the value at the point of construction. In these cases the value is immediately available, 
but needs to be returned as a future or shared_future. By using make_ready_future a future  
can be created which holds a pre-computed result in its shared state.
 
Without these features it is non-trivial to create a future directly from a value. 
First a promise must be created, then the promise is set, and lastly the future is retrieved from the promise. 
This can now be done with one operation.

[heading make_ready_future]

This function creates a future for a given value. If no value is given then a future<void> is returned. 
This function is primarily useful in cases where sometimes, the return value is immediately available, but sometimes 
it is not. The example below illustrates, that in an error path the value is known immediately, however in other paths 
the function must return an eventual value represented as a future.


  boost::future<int> compute(int x)
  {
    if (x == 0) return boost::make_ready_future(0);
    if (x < 0) return boost::make_ready_future<int>(std::logic_error("Error"));
    boost::future<int> f1 = boost::async([]() { return x+1; });
    return f1;
  }
  
There are two variations of this function. The first takes a value of any type, and returns a future of that type. 
The input value is passed to the shared state of the returned future. The second version takes no input and returns a future<void>. 

[endsect]

[section:then Associating future continuations]

In asynchronous programming, it is very common for one asynchronous operation, on completion, to invoke a second 
operation and pass data to it. The current C++ standard does not allow one to register a continuation to a future. 
With `.then`, instead of waiting for the result, a continuation is "attached" to the asynchronous operation, which is 
invoked when the result is ready. Continuations registered using the `.then` function will help to avoid blocking waits 
or wasting threads on polling, greatly improving the responsiveness and scalability of an application.

`future.then()` provides the ability to sequentially compose two futures by declaring one to be the continuation of another. 
With `.then()` the antecedent future is ready (has a value or exception stored in the shared state) before the continuation 
starts as instructed by the lambda function.

In the example below the `future<string>` `f2` is registered to be a continuation of `future<int>` `f1` using the `.then()` member 
function. This operation takes a lambda function which describes how `f2` should proceed after `f1` is ready.


  #include <boost/thread/future.hpp>
  using namespace boost;
  int main() 
  {
    future<int> f1 = async([]() { return 123; });
    future<string> f2 = f1.then([](future<int> f) { return f.get().to_string(); // here .get() won't block });
  }

One key feature of this function is the ability to chain multiple asynchronous operations. In asynchronous programming, 
it's common to define a sequence of operations, in which each continuation executes only when the previous one completes. 
In some cases, the antecedent future produces a value that the continuation accepts as input. By using `future.then()`, 
creating a chain of continuations becomes straightforward and intuitive: 

  myFuture.then(...).then(...).then(...). 
  
Some points to note are:

* Each continuation will not begin until the preceding has completed.
* If an exception is thrown, the following continuation can handle it in a try-catch block


Input Parameters:

* Lambda function: One option which can be considered is to take two functions, one for 
success and one for error handling. However this option has not been retained for the moment. 
The lambda function takes a future as its input which carries the exception 
through. This makes propagating exceptions straightforward. This approach also simplifies the chaining of continuations.
* Scheduler: Providing an overload to `.then`, to take a scheduler reference places great flexibility over the execution 
of the future in the programmer's hand. As described above, often taking a launch policy is not sufficient for powerful 
asynchronous operations. The lifetime of the scheduler must outlive the continuation.
* Launch policy: if the additional flexibility that the scheduler provides is not required.

Return values: The decision to return a future was based primarily on the ability to chain multiple continuations using
`.then()`. This benefit of composability gives the programmer incredible control and flexibility over their code. Returning 
a `future` object rather than a `shared_future` is also a much cheaper operation thereby improving performance. A 
`shared_future` object is not necessary to take advantage of the chaining feature. It is also easy to go from a `future` 
to a `shared_future` when needed using future::share().


[endsect]


[include future_ref.qbk]

[endsect]