]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | [/ |
2 | (C) Copyright 2007-8 Anthony Williams. | |
3 | (C) Copyright 2013 Oliver Kowalke. | |
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:barriers Barriers] | |
10 | ||
11 | A barrier is a concept also known as a __rendezvous__, it is a synchronization | |
12 | point between multiple contexts of execution (fibers). The barrier is | |
13 | configured for a particular number of fibers (`n`), and as fibers reach the | |
14 | barrier they must wait until all `n` fibers have arrived. Once the `n`-th | |
15 | fiber has reached the barrier, all the waiting fibers can proceed, and the | |
16 | barrier is reset. | |
17 | ||
18 | The fact that the barrier automatically resets is significant. Consider a case | |
19 | in which you launch some number of fibers and want to wait only until the | |
20 | first of them has completed. You might be tempted to use a `barrier(2)` as the | |
21 | synchronization mechanism, making each new fiber call its [member_link | |
22 | barrier..wait] method, then calling `wait()` in the launching fiber to wait | |
23 | until the first other fiber completes. | |
24 | ||
25 | That will in fact unblock the launching fiber. The unfortunate part is that it | |
26 | will continue blocking the ['remaining] fibers. | |
27 | ||
28 | Consider the following scenario: | |
29 | ||
30 | # Fiber ["main] launches fibers A, B, C and D, then calls `barrier::wait()`. | |
31 | # Fiber C finishes first and likewise calls `barrier::wait()`. | |
32 | # Fiber ["main] is unblocked, as desired. | |
33 | # Fiber B calls `barrier::wait()`. Fiber B is ['blocked!] | |
34 | # Fiber A calls `barrier::wait()`. Fibers A and B are unblocked. | |
35 | # Fiber D calls `barrier::wait()`. Fiber D is blocked indefinitely. | |
36 | ||
37 | (See also [link wait_first_simple_section when_any, simple completion].) | |
38 | ||
39 | [note It is unwise to tie the lifespan of a barrier to any one of its | |
40 | participating fibers. Although conceptually all waiting fibers awaken | |
41 | ["simultaneously,] because of the nature of fibers, in practice they will | |
42 | awaken one by one in indeterminate order.[footnote The current implementation | |
43 | wakes fibers in FIFO order: the first to call `wait()` wakes first, and so | |
44 | forth. But it is perilous to rely on the order in which the various fibers | |
45 | will reach the `wait()` call.] The rest of the waiting fibers will | |
46 | still be blocked in `wait()`, which must, before returning, access data | |
47 | members in the barrier object.] | |
48 | ||
49 | [class_heading barrier] | |
50 | ||
51 | #include <boost/fiber/barrier.hpp> | |
52 | ||
53 | namespace boost { | |
54 | namespace fibers { | |
55 | ||
56 | class barrier { | |
57 | public: | |
58 | explicit barrier( std::size_t); | |
59 | ||
60 | barrier( barrier const&) = delete; | |
61 | barrier & operator=( barrier const&) = delete; | |
62 | ||
63 | bool wait(); | |
64 | }; | |
65 | ||
66 | }} | |
67 | ||
68 | ||
69 | Instances of __barrier__ are not copyable or movable. | |
70 | ||
71 | [heading Constructor] | |
72 | ||
73 | explicit barrier( std::size_t initial); | |
74 | ||
75 | [variablelist | |
76 | [[Effects:] [Construct a barrier for `initial` fibers.]] | |
77 | [[Throws:] [`fiber_error`]] | |
78 | [[Error Conditions:] [ | |
79 | [*invalid_argument]: if `initial` is zero.]] | |
80 | ] | |
81 | ||
82 | [member_heading barrier..wait] | |
83 | ||
84 | bool wait(); | |
85 | ||
86 | [variablelist | |
87 | [[Effects:] [Block until `initial` fibers have called `wait` on `*this`. When | |
88 | the `initial`-th fiber calls `wait`, all waiting fibers are unblocked, and | |
89 | the barrier is reset. ]] | |
90 | [[Returns:] [`true` for exactly one fiber from each batch of waiting fibers, | |
91 | `false` otherwise.]] | |
92 | [[Throws:] [__fiber_error__]] | |
93 | ] | |
94 | ||
95 | [endsect] |