BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bslmt_latch.h
Go to the documentation of this file.
1/// @file bslmt_latch.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bslmt_latch.h -*-C++-*-
8#ifndef INCLUDED_BSLMT_LATCH
9#define INCLUDED_BSLMT_LATCH
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bslmt_latch bslmt_latch
15/// @brief Provide a single-use mechanism for synchronizing on an event count.
16/// @addtogroup bsl
17/// @{
18/// @addtogroup bslmt
19/// @{
20/// @addtogroup bslmt_latch
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bslmt_latch-purpose"> Purpose</a>
25/// * <a href="#bslmt_latch-classes"> Classes </a>
26/// * <a href="#bslmt_latch-description"> Description </a>
27/// * <a href="#bslmt_latch-comparison-to-bslmt-barrier"> Comparison to bslmt::Barrier </a>
28/// * <a href="#bslmt_latch-comparison-to-bslmt-semaphore"> Comparison to bslmt::Semaphore </a>
29/// * <a href="#bslmt_latch-undefined-behavior-when-decrementing-the-event-count"> Undefined Behavior When Decrementing the Event Count </a>
30/// * <a href="#bslmt_latch-supported-clock-types"> Supported Clock-Types </a>
31/// * <a href="#bslmt_latch-usage"> Usage </a>
32/// * <a href="#bslmt_latch-example-1-implementing-a-parallelizable-algorithm"> Example 1: Implementing a Parallelizable Algorithm </a>
33///
34/// # Purpose {#bslmt_latch-purpose}
35/// Provide a single-use mechanism for synchronizing on an event count.
36///
37/// # Classes {#bslmt_latch-classes}
38///
39/// - bslmt::Latch: single-use synchronization mechanism on a count of events
40///
41/// @see bslmt_barrier
42///
43/// # Description {#bslmt_latch-description}
44/// This component defines a mechanism, `bslmt::Latch`, that allows
45/// one or more threads to wait until a certain number of operations have been
46/// performed by other threads.
47///
48/// A latch maintains a `currentCount` of operations that must be performed
49/// before threads waiting on the latch are released. The initial operation
50/// count for the latch is supplied at construction, and is decremented by calls
51/// to either `arrive`, `arriveAndWait`, or `countDown`. Threads may wait on a
52/// latch to be released by calling either `wait` or `arriveAndWait`, and
53/// threads calling those methods will block until the `currentCount` is 0.
54///
55/// *WARNING*: `bslmt::Latch` is *not* a reusable synchronization mechanism.
56/// The behavior is undefined when calling `countDown`, `arrive`, and
57/// `arriveAndWait` after the `currentCount` becomes 0 (the latch has been
58/// released).
59///
60/// ## Comparison to bslmt::Barrier {#bslmt_latch-comparison-to-bslmt-barrier}
61///
62///
63/// A latch provides a basic synchronization tool, similar to a barrier. A
64/// latch is distinct from a barrier in that:
65///
66/// * A latch is single-use only, whereas a barrier can be used multiple times.
67/// * Threads waiting on a barrier are blocked, whereas threads that `arrive`
68/// at a latch are not blocked (only waiting threads are blocked).
69/// * `wait` on a barrier always decrements the count of waiting threads by 1,
70/// whereas `countDown` on a latch can indicate multiple events.
71///
72/// An example use of a barrier is to coordinate a set of threads working in
73/// lock-step to complete a multi-step operation. An example use of a latch is
74/// for one thread to coordinate the completion of a multi-step operation being
75/// performed by a set of "child" threads.
76///
77/// ## Comparison to bslmt::Semaphore {#bslmt_latch-comparison-to-bslmt-semaphore}
78///
79///
80/// A latch is conceptually similar to a semaphore with a negative count.
81/// However, typically semaphore implementations (including `bslmt::Semaphore`
82/// and POSIX) do not allow for negative counts. Waiting on a latch configured
83/// for `N` events is cleaner than one thread calling `wait` on a semaphore `N`
84/// times in a loop. Furthermore, if the use case involves multiple threads
85/// waiting on a set of events, using a latch is cleaner than each thread
86/// waiting on a semaphore and then immediately calling `post` (to release the
87/// next waiting thread).
88///
89/// ## Undefined Behavior When Decrementing the Event Count {#bslmt_latch-undefined-behavior-when-decrementing-the-event-count}
90///
91///
92/// The methods `arrive`, `arriveAndWait`, and `countDown` all document that it
93/// is undefined behavior to decrement the event count below 0. Note that it
94/// isn't possible to use a latch's visible state to determine whether it is
95/// safe (i.e., not undefined behavior) to call `arrive`, `arriveAndWait`, or
96/// `countDown`. A limit on the number of times the event count is decremented
97/// must be imposed by the logic of the program. For example, in the usage
98/// example below a latch is created with an event count that matches the number
99/// of threads that will call `arrive` on that latch.
100///
101/// ## Supported Clock-Types {#bslmt_latch-supported-clock-types}
102///
103///
104/// `bsls::SystemClockType` supplies the enumeration indicating the system clock
105/// on which timeouts supplied to other methods should be based. If the clock
106/// type indicated at construction is `bsls::SystemClockType::e_REALTIME`, the
107/// `absTime` argument passed to the `timedWait` method should be expressed as
108/// an *absolute* offset since 00:00:00 UTC, January 1, 1970 (which matches the
109/// epoch used in `bsls::SystemTime::now(bsls::SystemClockType::e_REALTIME)`.
110/// If the clock type indicated at construction is
111/// `bsls::SystemClockType::e_MONOTONIC`, the `absTime` argument passed to the
112/// `timedWait` method should be expressed as an *absolute* offset since the
113/// epoch of this clock (which matches the epoch used in
114/// `bsls::SystemTime::now(bsls::SystemClockType::e_MONOTONIC)`.
115///
116/// ## Usage {#bslmt_latch-usage}
117///
118///
119/// This section illustrates intended use of this component.
120///
121/// ### Example 1: Implementing a Parallelizable Algorithm {#bslmt_latch-example-1-implementing-a-parallelizable-algorithm}
122///
123///
124/// In the following example we use a `bslmt::Latch` object to help implement an
125/// operation that can be parallelized across a series of sub-tasks (or "jobs").
126/// The "parent" operation enqueue's the jobs and blocks on a thread pool, and
127/// uses the latch as a signaling mechanism to indicate when all of the jobs
128/// have been completed and return to the caller.
129///
130/// The use of a `bslmt::Latch`, rather than a `bslmt::Barrier`, is important to
131/// ensure that jobs in the thread pool do not block until the entire task is
132/// completed (preventing the thread pool from processing additional work).
133///
134/// Suppose, for example, we want to provide a C++ type for computing a vector
135/// sum (vector in the mathematical sense). That is, for two input vectors, `A`
136/// and `B`, each of length `N`, the result is a vector, `R`, of length `N`,
137/// where each element at index `i` has the value:
138/// @code
139/// R[i] = A[i] + B[i];
140/// @endcode
141/// This function can easily be computed in parallel because the value for each
142/// result index only depends on the input vectors.
143///
144/// First, assume we have a class, `FixedThreadPool`, providing the following
145/// public interface (for brevity, the details have been elided; see
146/// @ref bdlmt_fixedthreadpool or @ref bdlmt_threadpool for examples of thread pools):
147/// @code
148/// class FixedThreadPool {
149///
150/// public:
151/// // ...
152///
153/// // Enqueue the specified `job` to be executed by the next available
154/// // thread.
155/// void enqueueJob(const bsl::function<void()>& job);
156/// };
157/// @endcode
158/// Next, we declare the signature for our vector sum function,
159/// `parallelVectorSum`:
160/// @code
161/// /// Load the specified `result` array with the vector sum of the
162/// /// specified `inputA` and `inputB`, each having at least the specified
163/// /// `numElements`, using the specified `threadPool` to perform the
164/// /// operation in parallel using the specified `numJobs` parallel jobs.
165/// /// The behavior is undefined unless `numElements > 0`, `numJobs > 0`,
166/// /// and `result`, `inputA`, and `inputB` each contain at least
167/// /// `numElements`.
168/// void parallelVectorSum(double *result,
169/// const double *inputA,
170/// const double *inputB,
171/// int numElements,
172/// FixedThreadPool *threadPool,
173/// int numJobs);
174/// @endcode
175/// Now, we declare a helper function, `vectorSumJob`, that will be used as a
176/// sub-task by `parallelVectorSum`. `vectorSumJob` computes a single-threaded
177/// vector sum and uses a `bslmt::Latch` object, `completionSignal`, to indicate
178/// to the parent task that the computation has been completed:
179/// @code
180/// /// Load the specified `result` array with the vector sum of the
181/// /// specified `inputA` and `inputB`, each having at least the specified
182/// /// `numElements`, and when the operation is complete signal the
183/// /// specified `completionSignal`. The behavior is undefined unless
184/// /// `numElements > 0` and `result`, `inputA`, and `inputB` each contain
185/// /// at least `numElements`.
186/// void vectorSumJob(double *result,
187/// bslmt::Latch *completionSignal,
188/// const double *inputA,
189/// const double *inputB,
190/// int numElements)
191/// {
192/// for (int i = 0; i < numElements; ++i) {
193/// result[i] = inputA[i] + inputB[i];
194/// }
195///
196/// completionSignal->arrive();
197/// }
198/// @endcode
199/// Note that `bslmt::Latch::arrive` does not block the current thread (unlike
200/// `bslmt::Barrier::wait`), and within the context of a thread pool, this job
201/// will complete and the thread will be returned to the pool to accept more
202/// work.
203///
204/// Next, we provide a rudimentary function argument binder (specific to this
205/// usage example) in view of the fact that such a facility is not available at
206/// this level in the BDE hierarchy:
207/// @code
208/// // This class provides an invokable that is tailored to bind the
209/// // `vectorSumJob` (defined above) to its requisite five arguments.
210/// class UsageBinder {
211///
212/// public:
213/// // TYPES
214/// typedef void FREE_FUNCTION(double *,
215/// bslmt::Latch *,
216/// const double *,
217/// const double *,
218/// int );
219///
220/// private:
221/// // DATA
222/// FREE_FUNCTION *d_func_p;
223/// double *d_arg1_p;
224/// bslmt::Latch *d_arg2_p;
225/// const double *d_arg3_p;
226/// const double *d_arg4_p;
227/// int d_arg5;
228///
229/// public:
230/// // CREATORS
231///
232/// /// Create a `UsageBinder` object that binds the specified
233/// /// `functionPtr` to the specified `arg1Ptr`, `arg2Ptr`, `arg3Ptr`,
234/// /// `arg4Ptr`, and `arg5` arguments.
235/// UsageBinder(FREE_FUNCTION *functionPtr,
236/// double *arg1Ptr,
237/// bslmt::Latch *arg2Ptr,
238/// const double *arg3Ptr,
239/// const double *arg4Ptr,
240/// int arg5)
241/// : d_func_p(functionPtr)
242/// , d_arg1_p(arg1Ptr)
243/// , d_arg2_p(arg2Ptr)
244/// , d_arg3_p(arg3Ptr)
245/// , d_arg4_p(arg4Ptr)
246/// , d_arg5(arg5)
247/// {
248/// }
249///
250/// // MANIPULATORS
251///
252/// /// Invoke the function that was supplied at construction on the
253/// /// arguments that were supplied at construction.
254/// void operator()()
255/// {
256/// (*d_func_p)(d_arg1_p, d_arg2_p, d_arg3_p, d_arg4_p, d_arg5);
257/// }
258/// };
259/// @endcode
260/// Then, we define `parallelVectorSum`:
261/// @code
262/// void parallelVectorSum(double *result,
263/// const double *inputA,
264/// const double *inputB,
265/// int numElements,
266/// FixedThreadPool *threadPool,
267/// int numJobs)
268/// {
269/// // Ensure that there is at least 1 element per job.
270///
271/// if (numElements < numJobs) {
272/// numJobs = numElements;
273/// }
274///
275/// const int jobSize = numElements / numJobs;
276/// @endcode
277/// Now, we define a `bslmt::Latch` object, `completionSignal`, that we will
278/// use to track the completion of this work:
279/// @code
280/// bslmt::Latch completionSignal(numJobs);
281///
282/// for (int i = 0; i < numJobs; ++i) {
283/// // If 'numJobs' doesn't evenly divide 'numElements', the last job
284/// // will process the remaining elements. For simplicity, we have
285/// // chosen not distribute the elements between jobs as evenly as is
286/// // possible.
287///
288/// int offset = i * jobSize;
289/// int size = (i == numJobs - 1) ? jobSize + numElements % numJobs
290/// : jobSize;
291/// assert(0 != size);
292///
293/// threadPool->enqueueJob(UsageBinder(vectorSumJob,
294/// result + offset,
295/// &completionSignal,
296/// inputA + offset,
297/// inputB + offset,
298/// size));
299/// }
300/// @endcode
301/// Finally, calling `wait` on the latch will block this function from returning
302/// until all the queued jobs computing the vector sum have been completed:
303/// @code
304/// completionSignal.wait();
305/// }
306/// @endcode
307/// @}
308/** @} */
309/** @} */
310
311/** @addtogroup bsl
312 * @{
313 */
314/** @addtogroup bslmt
315 * @{
316 */
317/** @addtogroup bslmt_latch
318 * @{
319 */
320
321#include <bslscm_version.h>
322
323#include <bslmt_condition.h>
324#include <bslmt_mutex.h>
325
326#include <bsls_assert.h>
327#include <bsls_atomic.h>
328#include <bsls_libraryfeatures.h>
329#include <bsls_systemclocktype.h>
330
331#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
332#include <bslmt_chronoutil.h>
333
334#include <bsl_chrono.h>
335#endif
336
337
338namespace bslmt {
339
340 // ===========
341 // class Latch
342 // ===========
343
344/// This class defines a thread synchronization mechanism that allows one or
345/// more threads to wait until a certain number of operations have been
346/// performed by other threads.
347///
348/// See @ref bslmt_latch
349class Latch {
350
351 // DATA
352 Mutex d_mutex; // mutex used to synchronize threads waiting
353 // for the latch to release
354
355 Condition d_cond; // condition variable used for signaling
356 // waiting threads
357
358 bsls::AtomicInt d_sigCount; // count of number of times this latch has
359 // been "signaled"
360
361 private:
362 // NOT IMPLEMENTED
363 Latch(const Latch&);
364 Latch& operator=(const Latch&);
365
366 public:
367 // TYPES
368
369 /// The value `timedWait` returns when a timeout occurs.
371
372 // CREATORS
373
374 /// Create a latch that will synchronize on the specified `count`
375 /// number of events, and when `count` events have been recorded will
376 /// release any waiting threads. Optionally specify a `clockType`
377 /// indicating the type of the system clock against which the
378 /// `bsls::TimeInterval` `absTime` timeouts passed to the `timedWait`
379 /// method are to be interpreted. If `clockType` is not specified then
380 /// the realtime system clock is used. The behavior is undefined unless
381 /// `0 <= count`.
382 explicit Latch(int count,
385
386#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
387 /// Create a latch that will synchronize on the specified `count` number
388 /// of events, and when `count` events have been recorded will release
389 /// any waiting threads. Use the realtime system clock as the clock
390 /// against which the `absTime` timeouts passed to the `timedWait`
391 /// methods are interpreted (see {Supported Clock-Types} in the
392 /// component-level documentation). The behavior is undefined unless
393 /// `0 <= count`.
394 Latch(int count, const bsl::chrono::system_clock&);
395
396 /// Create a latch that will synchronize on the specified `count` number
397 /// of events, and when `count` events have been recorded will release
398 /// any waiting threads. Use the monotonic system clock as the clock
399 /// against which the `absTime` timeouts passed to the `timedWait`
400 /// methods are interpreted (see {Supported Clock-Types} in the
401 /// component-level documentation). The behavior is undefined unless
402 /// `0 <= count`.
403 Latch(int count, const bsl::chrono::steady_clock&);
404#endif
405
406 /// Destroy this latch. The behavior is undefined if any threads are
407 /// waiting on this latch.
409
410 // MANIPULATORS
411
412 /// Decrement the number of events that this latch is waiting for by 1,
413 /// and if the resulting number of events is 0 release any waiting
414 /// threads. The behavior is undefined unless the sum of all events
415 /// that have arrived at this latch does not exceed the count with which
416 /// it was initialized. Note that the initial count of events is
417 /// supplied at construction.
418 void arrive();
419
420 /// Decrement the number of events that this latch is waiting for by 1,
421 /// and if the resulting number of events is 0 release any waiting
422 /// threads; otherwise, block until the required number of events has
423 /// been reached. The behavior is undefined unless the sum of all
424 /// events that have arrived at this latch does not exceed the count
425 /// with which it was initialized. Note that the initial count of
426 /// events is supplied at construction. Also note that this method is
427 /// equivalent to the following sequence:
428 /// @code
429 /// arrive();
430 /// wait();
431 /// @endcode
433
434 /// Decrement the number of events that this latch is waiting for by the
435 /// specified `numEvents`, and if the resulting number of events is 0
436 /// release any waiting threads. The behavior is undefined unless
437 /// `numEvents > 0` and the sum of all events that have arrived at this
438 /// latch does not exceed the count with which it was initialized. Note
439 /// that the initial count of events is supplied at construction.
440 void countDown(int numEvents);
441
442 /// Block until the number of events that this latch is waiting for
443 /// reaches 0, or until the specified `absTime` timeout expires. Return
444 /// 0 on success, `e_TIMED_OUT` on timeout, and a non-zero value
445 /// different from `e_TIMED_OUT` if an error occurs. Errors are
446 /// unrecoverable. After an error, the latch may be destroyed, but any
447 /// other use has undefined behavior. `absTime` is an *absolute* time
448 /// represented as an interval from some epoch as determined by the
449 /// clock specified at construction (see {Supported Clock-Types} in the
450 /// component-level documentation).
451 int timedWait(const bsls::TimeInterval& absTime);
452
453#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
454 /// Block until the number of events that this latch is waiting for
455 /// reaches 0, or until the specified `absTime` timeout expires. Return
456 /// 0 on success, `e_TIMED_OUT` on timeout, and a non-zero value
457 /// different from `e_TIMED_OUT` if an error occurs. Errors are
458 /// unrecoverable. After an error, the latch may be destroyed, but any
459 /// other use has undefined behavior. `absTime` is an *absolute* time
460 /// represented as an interval from some epoch, which is determined by
461 /// the clock associated with the time point.
462 template <class CLOCK, class DURATION>
463 int timedWait(const bsl::chrono::time_point<CLOCK, DURATION>& absTime);
464#endif
465
466 /// Block until the number of events that this latch is waiting for
467 /// reaches 0.
468 void wait();
469
470 // ACCESSORS
471
472 /// Return the clock type used for timeouts.
474
475 /// Return the current number of events for which this latch is waiting.
476 /// Note that this method is provided primarily for debugging purposes
477 /// (i.e., its intended use is not as a synchronization mechanism), and
478 /// can be used only as an upper bound for the current count without
479 /// other external state information.
480 int currentCount() const;
481
482 /// Return `true` if this latch has already been released (i.e., the
483 /// number of events the latch is waiting on is 0), and `false`
484 /// otherwise. This method does not block. Note that a return value
485 /// of `true` indicates a permanent state change (the latch has released
486 /// and will never be un-released), but a return value of `false` is
487 /// ephemeral and cannot typically be acted upon without additional
488 /// external state information.
489 bool tryWait() const;
490};
491
492// ============================================================================
493// INLINE DEFINITIONS
494// ============================================================================
495
496 // -----------
497 // class Latch
498 // -----------
499
500// CREATORS
501inline
502Latch::Latch(int count, bsls::SystemClockType::Enum clockType)
503: d_mutex()
504, d_cond(clockType)
505, d_sigCount(count)
506{
507 BSLS_ASSERT_SAFE(0 <= count);
508}
509
510#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
511inline
512Latch::Latch(int count, const bsl::chrono::system_clock&)
513: d_mutex()
514, d_cond(bsls::SystemClockType::e_REALTIME)
515, d_sigCount(count)
516{
517 BSLS_ASSERT_SAFE(0 <= count);
518}
519
520inline
521Latch::Latch(int count, const bsl::chrono::steady_clock&)
522: d_mutex()
523, d_cond(bsls::SystemClockType::e_MONOTONIC)
524, d_sigCount(count)
525{
526 BSLS_ASSERT_SAFE(0 <= count);
527}
528
529// MANIPULATORS
530template <class CLOCK, class DURATION>
531inline
532int Latch::timedWait(const bsl::chrono::time_point<CLOCK, DURATION>& absTime)
533{
534 return bslmt::ChronoUtil::timedWait(this, absTime);
535}
536#endif
537
538// ACCESSORS
539inline
541{
542 return d_cond.clockType();
543}
544
545} // close package namespace
546
547
548#endif
549
550// ----------------------------------------------------------------------------
551// Copyright 2015 Bloomberg Finance L.P.
552//
553// Licensed under the Apache License, Version 2.0 (the "License");
554// you may not use this file except in compliance with the License.
555// You may obtain a copy of the License at
556//
557// http://www.apache.org/licenses/LICENSE-2.0
558//
559// Unless required by applicable law or agreed to in writing, software
560// distributed under the License is distributed on an "AS IS" BASIS,
561// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
562// See the License for the specific language governing permissions and
563// limitations under the License.
564// ----------------------------- END-OF-FILE ----------------------------------
565
566/** @} */
567/** @} */
568/** @} */
Definition bslmt_condition.h:220
@ e_TIMED_OUT
Definition bslmt_condition.h:234
bsls::SystemClockType::Enum clockType() const
Return the clock type used for timeouts.
Definition bslmt_condition.h:432
Definition bslmt_latch.h:349
void countDown(int numEvents)
bool tryWait() const
@ e_TIMED_OUT
Definition bslmt_latch.h:370
void arriveAndWait()
int currentCount() const
bsls::SystemClockType::Enum clockType() const
Return the clock type used for timeouts.
Definition bslmt_latch.h:540
int timedWait(const bsls::TimeInterval &absTime)
Definition bslmt_mutex.h:315
Definition bsls_atomic.h:743
Definition bsls_timeinterval.h:301
#define BSLS_ASSERT_SAFE(X)
Definition bsls_assert.h:1762
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
Definition bslmt_barrier.h:344
Definition bdlt_iso8601util.h:691
static int timedWait(PRIMITIVE *primitive, const bsl::chrono::time_point< CLOCK, DURATION > &absTime)
Definition bslmt_chronoutil.h:345
Enum
Definition bsls_systemclocktype.h:117
@ e_REALTIME
Definition bsls_systemclocktype.h:120