-
Notifications
You must be signed in to change notification settings - Fork 20
/
latch_barrier.html
289 lines (245 loc) · 12.7 KB
/
latch_barrier.html
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
<cxx-clause id="coordination">
<h1>Latches and Barriers</h1>
<cxx-section id="coordination.general">
<h1>General</h1>
<p>
This section describes various concepts related to thread coordination, and defines the <code>latch</code>, <code>barrier</code> and <code>flex_barrier</code> classes.
</p>
<cxx-ednote>
This section uses the term 'thread' throughout. Where relevant, it should be updated to refer to execution agents when these are adopted in the standard. See N4231 and N4156.
</cxx-ednote>
</cxx-section>
<cxx-section id="thread.coordination.terminology">
<h1>Terminology</h1>
<p>
In this subclause, a <em>synchronization point</em> represents a point at which a thread may block until a given condition has been reached.
</p>
</cxx-section>
<cxx-section id="thread.coordination.latch">
<h1>Latches</h1>
<p>
Latches are a thread coordination mechanism that allow one or more threads to block until an operation is completed. An individual latch is a single-use object; once the operation has been completed, the latch cannot be reused.
</p>
</cxx-section>
<cxx-section id="thread.coordination.latch.synopsis">
<h1>Header <experimental/latch> synopsis</h1>
<pre><code>
namespace std {
namespace experimental {
inline namespace concurrency_v1 {
class latch {
public:
explicit latch(ptrdiff_t count);
latch(const latch&) = delete;
latch& operator=(const latch&) = delete;
~latch();
void count_down_and_wait();
void count_down(ptrdiff_t n = 1);
bool is_ready() const noexcept;
void wait() const;
private:
ptrdiff_t counter_; // <em>exposition only</em>
};
} // namespace concurrency_v1
} // namespace experimental
} // namespace std
</code></pre>
</cxx-section>
<cxx-section id="coordination.latch.class">
<h1>Class <code>latch</code></h1>
<p>
A latch maintains an internal <code>counter_</code> that is initialized when the latch is created. Threads may block at a synchronization point waiting for <code>counter_</code> to be decremented to <code>0</code>. When <code>counter_</code> reaches <code>0</code>, all such blocked threads are released.
</p>
<p>
Calls to <code>count_down_and_wait()</code>, <code>count_down()</code>, <code>wait()</code>, and <code>is_ready()</code> behave as atomic operations.
</p>
<cxx-function>
<cxx-signature>explicit latch(ptrdiff_t count);</cxx-signature>
<cxx-requires><code>count >= 0.</code></cxx-requires>
<cxx-synchronization>None.</cxx-synchronization>
<cxx-postconditions><code>counter_ == count</code>.</cxx-postconditions>
</cxx-function>
<cxx-function>
<cxx-signature>~latch();</cxx-signature>
<cxx-requires>No threads are blocked at the synchronization point.</cxx-requires>
<cxx-remarks>May be called even if some threads have not yet returned from <code>wait()</code> or <code>count_down_and_wait()</code> provided that <code>counter_</code> is <code>0</code>. <cxx-note>The destructor might not return until all threads have exited <code>wait()</code> or <code>count_down_and_wait()</code>.</cxx-note></cxx-remarks>
</cxx-function>
<cxx-function>
<cxx-signature>void count_down_and_wait();</cxx-signature>
<cxx-requires><code>counter_ > 0.</code></cxx-requires>
<cxx-effects>Decrements <code>counter_</code> by <code>1</code>. Blocks at the synchronization point until <code>counter_</code> reaches <code>0</code>. </cxx-effects>
<cxx-synchronization>Synchronizes with all calls that block on this latch and with all <code>is_ready</code> calls on this latch that return true.</cxx-synchronization>
<cxx-throws>Nothing.</cxx-throws>
</cxx-function>
<cxx-function>
<cxx-signature>void count_down(ptrdiff_t n = 1);</cxx-signature>
<cxx-requires><code>counter_ >= n</code> and <code>n >= 0</code>.</cxx-requires>
<cxx-effects>Decrements <code>counter_</code> by <code>n</code>. Does not block.</cxx-effects>
<cxx-synchronization>Synchronizes with all calls that block on this latch and with all <code>is_ready</code> calls on this latch that return true.</cxx-synchronization>
<cxx-throws>Nothing.</cxx-throws>
</cxx-function>
<cxx-function>
<cxx-signature>void wait() const;</cxx-signature>
<cxx-effects>If <code>counter_</code> is <code>0</code>, returns immediately. Otherwise, blocks the calling thread at the synchronization point until <code>counter_</code> reaches <code>0</code>.</cxx-effects>
<cxx-throws>Nothing.</cxx-throws>
<cxx-ednote>SG1 seems to have a convention that blocking functions are never marked noexcept (e.g. future::wait) even if they never throw. LWG requests that SG1 check whether this pattern is intended, and update the noexcept clauses here accordingly.</cxx-ednote>
</cxx-function>
<cxx-function>
<cxx-signature>is_ready() const noexcept;</cxx-signature>
<cxx-returns><code>counter_ == 0</code>. Does not block.</cxx-returns>
</cxx-function>
</cxx-section>
<cxx-section id="thread.coordination.barrier">
<h1>Barrier types</h1>
<p>
Barriers are a thread coordination mechanism that allow a <em>set of participating threads</em> to block until an operation is completed. Unlike a latch, a barrier is reusable: once the participating threads are released from a barrier's synchronization point, they can re-use the same barrier. It is thus useful for managing repeated tasks, or phases of a larger task, that are handled by multiple threads.
</p>
<p>
The <em>barrier types</em> are the standard library types <code>barrier</code> and <code>flex_barrier</code>. They shall meet the requirements set out in this subclause. In this description, <code>b</code> denotes an object of a barrier type.
</p>
<p>
Each barrier type defines a <em>completion phase</em> as a (possibly empty) set of effects. When the member functions defined in this subclause <em>arrive at the barrier's synchronization point</em>, they have the following effects:
</p>
<ol>
<li>
When all threads in the barrier's set of participating threads are blocked at its synchronization point, one participating thread is unblocked and executes the barrier type's completion phase.
</li>
<li>
When the completion phase is completed, all other participating threads are unblocked. The end of the completion phase synchronizes with the returns from all calls unblocked by its completion.
</li>
</ol>
<p>
The expression <code>b.arrive_and_wait()</code> shall be well-formed and have the following semantics:
</p>
<cxx-function>
<cxx-signature>void arrive_and_wait();</cxx-signature>
<cxx-requires>The current thread is a member of the set of participating threads.</cxx-requires>
<cxx-effects>Blocks and arrives at the barrier's synchronization point. <cxx-note>It is safe for a thread to call <code>arrive_and_wait()</code> or <code>arrive_and_drop()</code> again immediately. It is not necessary to ensure that all blocked threads have exited <code>arrive_and_wait()</code> before one thread calls it again.</cxx-note></cxx-effects>
<cxx-synchronization>The call to <code>arrive_and_wait()</code> synchronizes with the start of the completion phase.</cxx-synchronization>
<cxx-throws>Nothing.</cxx-throws>
</cxx-function>
<p>
The expression <code>b.arrive_and_drop()</code> shall be well-formed and have the following semantics:
</p>
<cxx-function>
<cxx-signature>void arrive_and_drop();</cxx-signature>
<cxx-requires>The current thread is a member of the set of participating threads.</cxx-requires>
<cxx-effects>
Removes the current thread from the set of participating threads.
Arrives at the barrier's synchronization point.
It is unspecified whether the function blocks until
the completion phase has ended. <cxx-note>If the function blocks, the calling
thread may be chosen to execute the completion phase. </cxx-note>
</cxx-effects>
<cxx-synchronization>The call to <code>arrive_and_drop()</code> synchronizes with the start of the completion phase.</cxx-synchronization>
<cxx-throws>Nothing.</cxx-throws>
<cxx-notes>If all participating threads call <code>arrive_and_drop()</code>, any further operations on the barrier are undefined, apart from calling the destructor.
If a thread that has called <code>arrive_and_drop()</code> calls another method on the same barrier, other than the destructor, the results are undefined.</cxx-notes>
</cxx-function>
<p>
Calls to <code>arrive_and_wait()</code> and <code>arrive_and_drop()</code> never introduce data races with themselves or each other.
</p>
</cxx-section>
<cxx-section id="thread.coordination.barrier.synopsis">
<h1>Header <experimental/barrier> synopsis</h1>
<pre><code>
namespace std {
namespace experimental {
inline namespace concurrency_v1 {
class barrier;
class flex_barrier;
} // namespace concurrency_v1
} // namespace experimental
} // namespace std
</pre></code>
</cxx-section>
<cxx-section id="coordination.barrier.class">
<h1>Class <code>barrier</code></h1>
<p>
<code>barrier</code> is a barrier type whose completion phase has no effects. Its constructor takes a parameter representing the initial size of its set of participating threads.
</p>
<pre><code>
class barrier {
public:
explicit barrier(ptrdiff_t num_threads);
barrier(const barrier&) = delete;
barrier& operator=(const barrier&) = delete;
~barrier();
void arrive_and_wait();
void arrive_and_drop();
};
</pre></code>
<cxx-function>
<cxx-signature>explicit barrier(ptrdiff_t num_threads);</cxx-signature>
<cxx-requires><code>num_threads >= 0.</code> <cxx-note>If <code>num_threads</code> is zero, the barrier may only be destroyed.</cxx-note></cxx-requires>
<cxx-effects>Initializes the barrier for <code>num_threads</code> participating threads. <cxx-note>The set of participating threads is the first <code>num_threads</code> threads to arrive at the synchronization point.</cxx-note></cxx-effects>
</cxx-function>
<cxx-function>
<cxx-signature>~barrier();</cxx-signature>
<cxx-requires>No threads are blocked at the synchronization point.</cxx-requires>
<cxx-effects>Destroys the barrier.</cxx-effects>
</cxx-function>
</cxx-section>
<cxx-section id="coordination.flexbarrier.class">
<h1>Class <code>flex_barrier</code></h1>
<p>
<code>flex_barrier</code> is a barrier type whose completion phase can be controlled
by a function object.
</p>
<pre><code>
class flex_barrier {
public:
template <class F>
flex_barrier(ptrdiff_t num_threads, F completion);
explicit flex_barrier(ptrdiff_t num_threads);
flex_barrier(const flex_barrier&) = delete;
flex_barrier& operator=(const flex_barrier&) = delete;
~flex_barrier();
void arrive_and_wait();
void arrive_and_drop();
private:
function<ptrdiff_t()> completion_; // <em>exposition only</em>
};
</pre></code>
<p>
The completion phase calls <code>completion_()</code>. If this returns <code>-1</code>, then the set of participating threads is unchanged. Otherwise, the set of participating threads becomes a new set with a size equal to the returned value. <cxx-note>If <code>completion_()</code> returns <code>0</code> then the set of participating threads becomes empty, and this object may only be destroyed.</cxx-note>
</p>
<cxx-function>
<cxx-signature>
template <class F>
flex_barrier(ptrdiff_t num_threads, F completion);
</cxx-signature>
<cxx-requires>
<ul>
<li>
<code>num_threads >= 0.</code>
</li><li>
<code>F</code> shall be <code>CopyConstructible</code>.
</li><li>
<code>completion</code> shall be Callable (C++14 §[func.wrap.func]) with no arguments and return type <code>ptrdiff_t</code>.
</li><li>
Invoking <code>completion</code> shall return a value greater than or equal to <code>-1</code> and shall not exit via an exception.
</li>
</ul></cxx-requires>
<cxx-effects>
Initializes the <code>flex_barrier</code> for <code>num_threads</code> participating threads,
and initializes <code>completion_</code> with <code>std::move(completion)</code>.
<cxx-note>The set of participating threads consists of the first <code>num_threads</code> threads to arrive at the
synchronization point.</cxx-note>
<cxx-note>If <code>num_threads</code> is <code>0</code> the set of participating threads is empty, and this object may only be destroyed.</cxx-note>
</cxx-effects>
</cxx-function>
<cxx-function>
<cxx-signature>explicit flex_barrier(ptrdiff_t num_threads);</cxx-signature>
</cxx-signature>
<cxx-requires><code>num_threads >= 0.</code></cxx-requires>
<cxx-effects>Has the same effect as creating a <code>flex_barrier</code> with <code>num_threads</code> and with a callable object whose invocation returns <code>-1</code> and has no side effects.</cxx-effects>
</cxx-function>
<cxx-function>
<cxx-signature>~flex_barrier();</cxx-signature>
<cxx-requires>No threads are blocked at the synchronization point.</cxx-requires>
<cxx-effects>Destroys the barrier.</cxx-effects>
</cxx-function>
</cxx-section>
<!-- End clause id="coordination" -->
</cxx-clause>