BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bslmt_fastpostsemaphoreimpl.h
Go to the documentation of this file.
1/// @file bslmt_fastpostsemaphoreimpl.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bslmt_fastpostsemaphoreimpl.h -*-C++-*-
8
9#ifndef INCLUDED_BSLMT_FASTPOSTSEMAPHOREIMPL
10#define INCLUDED_BSLMT_FASTPOSTSEMAPHOREIMPL
11
12#include <bsls_ident.h>
13BSLS_IDENT("$Id: $")
14
15/// @defgroup bslmt_fastpostsemaphoreimpl bslmt_fastpostsemaphoreimpl
16/// @brief Provide a testable semaphore class optimizing `post`.
17/// @addtogroup bsl
18/// @{
19/// @addtogroup bslmt
20/// @{
21/// @addtogroup bslmt_fastpostsemaphoreimpl
22/// @{
23///
24/// <h1> Outline </h1>
25/// * <a href="#bslmt_fastpostsemaphoreimpl-purpose"> Purpose</a>
26/// * <a href="#bslmt_fastpostsemaphoreimpl-classes"> Classes </a>
27/// * <a href="#bslmt_fastpostsemaphoreimpl-description"> Description </a>
28/// * <a href="#bslmt_fastpostsemaphoreimpl-supported-clock-types"> Supported Clock-Types </a>
29/// * <a href="#bslmt_fastpostsemaphoreimpl-usage"> Usage </a>
30///
31/// # Purpose {#bslmt_fastpostsemaphoreimpl-purpose}
32/// Provide a testable semaphore class optimizing `post`.
33///
34/// # Classes {#bslmt_fastpostsemaphoreimpl-classes}
35///
36/// - bslmt::FastPostSemaphoreImpl: testable semaphore class optimizing `post`
37///
38/// @see bslmt_fastpostsemaphore, bslmt_semaphore
39///
40/// # Description {#bslmt_fastpostsemaphoreimpl-description}
41/// This component defines a testable semaphore,
42/// `bslmt::FastPostSemaphoreImpl`, with the `post` operation being optimized at
43/// the potential expense of other operations. In particular,
44/// `bslmt::FastPostSemaphoreImpl` is an efficient synchronization primitive
45/// that enables sharing of a counted number of resources.
46/// `bslmt::FastPostSemaphoreImpl` supports the methods `timedWait`, `enable`,
47/// and `disable` in addition to the standard semaphore methods.
48///
49/// Commonly, during periods of time when the protected resource is scarce (the
50/// semaphore count is frequently zero) and threads are frequently blocked on
51/// wait methods, pessimizing the performance of the threads that block will
52/// have little effect on overall performance. In this case, optimizing `post`
53/// *may* be a performance improvement. Note that when the resource is
54/// plentiful, there are no blocked threads and we expect the differences
55/// between semaphore implementations to be trivial.
56///
57/// ## Supported Clock-Types {#bslmt_fastpostsemaphoreimpl-supported-clock-types}
58///
59///
60/// `bsls::SystemClockType` supplies the enumeration indicating the system clock
61/// on which timeouts supplied to other methods should be based. If the clock
62/// type indicated at construction is `bsls::SystemClockType::e_REALTIME`, the
63/// `absTime` argument passed to the `timedWait` method should be expressed as
64/// an *absolute* offset since 00:00:00 UTC, January 1, 1970 (which matches the
65/// epoch used in `bsls::SystemTime::now(bsls::SystemClockType::e_REALTIME)`.
66/// If the clock type indicated at construction is
67/// `bsls::SystemClockType::e_MONOTONIC`, the `absTime` argument passed to the
68/// `timedWait` method should be expressed as an *absolute* offset since the
69/// epoch of this clock (which matches the epoch used in
70/// `bsls::SystemTime::now(bsls::SystemClockType::e_MONOTONIC)`.
71///
72/// ## Usage {#bslmt_fastpostsemaphoreimpl-usage}
73///
74///
75/// There is no usage example for this component since it is not meant for
76/// direct client use.
77/// @}
78/** @} */
79/** @} */
80
81/** @addtogroup bsl
82 * @{
83 */
84/** @addtogroup bslmt
85 * @{
86 */
87/** @addtogroup bslmt_fastpostsemaphoreimpl
88 * @{
89 */
90
91#include <bslscm_version.h>
92
93#include <bslmt_lockguard.h>
94
95#include <bsls_review.h>
97#include <bsls_timeinterval.h>
98#include <bsls_types.h>
99
100#include <bsl_climits.h>
101
102
103namespace bslmt {
104
105 // =========================================
106 // class FastPostSemaphoreImplWorkaroundUtil
107 // =========================================
108
109/// This class provides utility functions for workarounds to system level
110/// issues for `FastPostSemaphoreImpl`.
111///
112/// See @ref bslmt_fastpostsemaphoreimpl
114
115 private:
116 // CLASS DATA
117 static bool s_postAlwaysSignals;
118
119 public:
120 // PUBLIC CLASS METHODS
121
122 /// Remove the mitigation of `post` always signalling the condition
123 /// variable. Note this mitigation was introduced as a work around for
124 /// a lost signal bug in the underlying implementation of condition
125 /// variable (e.g.,
126 /// https://sourceware.org/bugzilla/show_bug.cgi?id=25847). The
127 /// availability of the pthread correction is tracked in DRQS 172614796,
128 /// and the reversion of this workaround in DRQS 174079882.
130
131 /// Return `true` if the mitigation of `post` always signalling the
132 /// condition variable should be used, and `false` otherwise.
133 static bool usePostAlwaysSignalsMitigation();
134};
135
136 // ===========================
137 // class FastPostSemaphoreImpl
138 // ===========================
139
140/// This class implements a semaphore type, optimized for `post`, for thread
141/// synchronization.
142///
143/// See @ref bslmt_fastpostsemaphoreimpl
144template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
146
147 // PRIVATE TYPES
148 typedef typename ATOMIC_OP::AtomicTypes::Int64 AtomicInt64;
149 typedef bsls::Types::Int64 Int64;
150
151 // DATA
152 AtomicInt64 d_state; // bit pattern representing the state of the
153 // semaphore (see *Implementation* *Note*)
154
155 MUTEX d_waitMutex; // mutex used with 'd_waitCondition', does
156 // not protect any values
157
158 CONDITION d_waitCondition; // condition variable for blocking/signalling
159 // threads in the wait methods
160
161 // PRIVATE CLASS METHODS
162
163 /// Return a value suitable for detecting a rapid short sequence of
164 /// `disable` and `enable` invocations, by comparing the value returned
165 /// by `disabledGeneration` before and after the sequence, for the
166 /// specified `state`.
167 static bsls::Types::Int64 disabledGeneration(Int64 state);
168
169 /// Return the semaphore value, without enforcing a zero minimum value,
170 /// implied by the specified `state`.
171 static bsls::Types::Int64 getValueRaw(Int64 state);
172
173 /// Return `true` if the specified `state` implies the associated
174 /// semaphore has available count.
175 static bool hasAvailable(Int64 state);
176
177 /// Return `true` if the specified `state` implies the associated
178 /// semaphore has one or more threads blocked in a wait operation.
179 static bool hasBlockedThread(Int64 state);
180
181 /// Return `true` if the specified `state` implies the associated
182 /// semaphore is "wait disabled".
183 static bool isDisabled(Int64 state);
184
185 /// Return `true` if the specified `state` implies the associated
186 /// semaphore does not have sufficient resources to meet the demand upon
187 /// the resources, implying there will be one or more threads blocked in
188 /// wait operations (without further `post` invocations).
189 static bool willHaveBlockedThread(Int64 state);
190
191 // PRIVATE MANIPULATORS
192
193 /// If this semaphore becomes disabled as detected from the disabled
194 /// generation encoded in the specified `initialState` (see
195 /// *Implementation* *Note*), return `e_DISABLED` with no effect on the
196 /// count. Otherwise, block until the count of this semaphore is a
197 /// positive value or the specified `absTime` timeout expires. If the
198 /// count of this timed semaphore is a positive value, return 0 and
199 /// atomically decrement the count. If the `absTime` timeout expires,
200 /// return `e_TIMED_OUT` with no effect on the count. `absTime` is an
201 /// absolute time represented as an interval from some epoch, which is
202 /// determined by the clock indicated at construction (see {Supported
203 /// Clock-Types} in the component-level documentation). This method is
204 /// invoked from `timedWait` when the invoking thread may have to be
205 /// blocked.
206 int timedWaitSlowPath(const bsls::TimeInterval& absTime,
207 const bsls::Types::Int64 initialState);
208
209 /// If this semaphore becomes disabled as detected from the disabled
210 /// generation encoded in the specified `initialState` (see
211 /// *Implementation* *Note*), return `e_DISABLED` with no effect on the
212 /// count. Otherwise, block until the count of this semaphore is a
213 /// positive value, return 0 and atomically decrement the count. This
214 /// method is invoked from `wait` when the invoking thread may have to
215 /// be blocked.
216 int waitSlowPath(const bsls::Types::Int64 initialState);
217
218 private:
219 // NOT IMPLEMENTED
222
223 public:
224 // PUBLIC CONSTANTS
226 e_SUCCESS = 0, // indicates success
227 e_DISABLED = -1, // indicates semaphore is disabled
228 e_TIMED_OUT = -2, // indicates operation timed out
229 e_WOULD_BLOCK = -3, // indicates operation would block ('tryWait')
230 e_FAILED = -4 // indicates failure reported from condition
231 };
232
233 // The following constants are used to maintain the semaphore`s `d_state`
234 // attribute values for:
235 // * number of threads blocked,
236 // * generation count for tracking enabled/disabled,
237 // * and available count.
238 //
239 // The `k_*_MASK` constants define the layout of the attributes, the
240 // `k_*_INC` constants are used to modify the `d_state` attributes, and the
241 // `k_*_SHIFT` constants allow recovery of the stored value.
242 //
243 // See **Implementation Note** for further details. These are `public` to
244 // ease testing.
245
246 static const Int64 k_BLOCKED_INC = 0x0000000000000001LL;
247 static const Int64 k_BLOCKED_MASK = 0x0000000000ffffffLL;
248 static const Int64 k_DISABLED_GEN_INC = 0x0000000001000000LL;
249 static const Int64 k_DISABLED_GEN_MASK = 0x000000000f000000LL;
250 static const int k_DISABLED_GEN_SHIFT = 24;
251 static const Int64 k_AVAILABLE_INC = 0x0000000010000000LL;
252 static const Int64 k_AVAILABLE_MASK = 0xfffffffff0000000LL;
253 static const int k_AVAILABLE_SHIFT = 28;
254
255 // CREATORS
256
257 /// Create a `FastPostSemaphoreImpl` object initially having a count of 0.
258 /// Optionally specify a `clockType` indicating the type of the system
259 /// clock against which the `bsls::TimeInterval` `absTime` timeouts passed
260 /// to the `timedWait` method are to be interpreted (see
261 /// @ref bslmt_fastpostsemaphoreimpl-supported-clock-types ). If `clockType` is not specified then the
262 /// realtime system clock is used.
263 explicit
266
267 /// Create a `FastPostSemaphoreImpl` object initially having the
268 /// specified `count`. Optionally specify a `clockType` indicating the
269 /// type of the system clock against which the `bsls::TimeInterval`
270 /// `absTime` timeouts passed to the `timedWait` method are to be
271 /// interpreted (see @ref bslmt_fastpostsemaphoreimpl-supported-clock-types ). If `clockType` is not
272 /// specified then the realtime system clock is used.
273 explicit
275 int count,
277
278 /// Destroy this object.
280
281 // MANIPULATORS
282
283 /// Disable waiting on this semaphore. All subsequent invocations of
284 /// `wait`, `tryWait`, and `timedWait` will fail immediately. All
285 /// blocked invocations of `wait` and `timedWait` will fail immediately.
286 /// If the semaphore is initially disabled, this call has no effect.
287 void disable();
288
289 /// Enable waiting on this semaphore. If the semaphore is initially
290 /// enabled, this call has no effect.
291 void enable();
292
293 /// Atomically increment the count of this semaphore.
294 void post();
295
296 /// Atomically increase the count of this semaphore by the specified `value`.
297 ///
298 /// \pre The behavior is undefined unless `value > 0`.
299 void post(int value);
300
301 /// Atomically increase the count of this semaphore by the specified
302 /// `value`. If the resources available to this semaphore is greater
303 /// than or equal to the specified `available` and the number of threads
304 /// blocked in this semaphore is greater than or equal to the specified
305 /// `blocked`, always send a signal to potentially wake a waiting thread
306 /// (even if the signal should not be needed).
307 ///
308 /// \pre The behavior is undefined unless `value > 0`.
309 /// \note Note that this method is provided to
310 /// help mitigate issues in the implementation of underlying
311 /// synchronization primitives.
312 void postWithRedundantSignal(int value, int available, int blocked);
313
314 /// If the count of this semaphore is positive, reduce the count by the
315 /// lesser of the count and the specified `maximumToTake` and return the
316 /// magnitude of the change to the count. Otherwise, do nothing and
317 /// return 0.
318 int take(int maximumToTake);
319
320 /// If the count of this semaphore is positive, reduce the count to 0
321 /// and return the original value of the count. Otherwise, do nothing
322 /// and return 0.
323 int takeAll();
324
325 /// If this semaphore is initially disabled, or becomes disabled while
326 /// blocking, return `e_DISABLED` with no effect on the count.
327 /// Otherwise, block until the count of this semaphore is a positive
328 /// value or the specified `absTime` timeout expires. If the count of
329 /// this semaphore is a positive value, return 0 and atomically
330 /// decrement the count. If the `absTime` timeout expires, return
331 /// `e_TIMED_OUT` with no effect on the count. Return `e_FAILED` if an
332 /// error occurs. `absTime` is an *absolute* time represented as an
333 /// interval from some epoch, which is determined by the clock indicated
334 /// at construction (see {Supported Clock-Types} in the component
335 /// documentation).
336 int timedWait(const bsls::TimeInterval& absTime);
337
338 /// If this semaphore is initially disabled, return `e_DISABLED` with no
339 /// effect on the count. Otherwise, if the count of this semaphore is a
340 /// positive value, return 0 and atomically decrement the count. If
341 /// this semaphore is not disabled and the count of this semaphore is
342 /// not a positive value, return `e_WOULD_BLOCK` with no effect on the
343 /// count.
344 int tryWait();
345
346 /// If this semaphore is initially disabled, or becomes disabled while
347 /// blocking, return `e_DISABLED` with no effect on the count.
348 /// Otherwise, block until the count of this semaphore is a positive
349 /// value, return 0 and atomically decrement the count. Return
350 /// `e_FAILED` if an error occurs.
351 int wait();
352
353 // ACCESSORS
354
355 /// Return the clock type used for timeouts.
357
358 /// Return an odd value if this semaphore is wait disabled, and an even
359 /// value otherwise. The returned value can be used to detect a rapid
360 /// short sequence of `disable` and `enable` invocations by comparing
361 /// the value returned by `getDisabledState` before and after the
362 /// sequence. For example, for any initial state of a semphore instance
363 /// `obj`:
364 /// @code
365 /// int state = obj.getDisabledState();
366 /// obj.disable();
367 /// obj.enable();
368 /// ASSERT(state != obj.getDisabledState());
369 /// @endcode
370 /// This functionality is useful in higher-level components to determine
371 /// if this semaphore was disabled during an operation.
372 int getDisabledState() const;
373
374 /// Return the current value (`count > 0 ? count : 0`) of this
375 /// semaphore.
376 int getValue() const;
377
378 /// Return the current value (`count`) of this semaphore.
379 ///
380 /// \note Note that, unlike `getValue`, this method can return a negative value. Also note
381 /// that this method is principally intended for use in testing.
382 int getValueRaw() const;
383
384 /// Return `true` if this semaphore is wait disabled, and `false` otherwise.
385 ///
386 /// \note Note that the semaphore is created in the "wait enabled"
387 /// state.
388 bool isDisabled() const;
389};
390
391// ============================================================================
392// INLINE DEFINITIONS
393// ============================================================================
394
395 // -----------------------------------------
396 // class FastPostSemaphoreImplWorkaroundUtil
397 // -----------------------------------------
398
399// PUBLIC CLASS METHODS
400inline
402{
403 s_postAlwaysSignals = false;
404}
405
406inline
408{
409 return s_postAlwaysSignals;
410}
411
412 // ---------------------------
413 // class FastPostSemaphoreImpl
414 // ---------------------------
415
416// PRIVATE CLASS METHODS
417template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
418inline
421 ::disabledGeneration(Int64 state)
422{
423 return state & k_DISABLED_GEN_MASK;
424}
425
426template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
427inline
429 FastPostSemaphoreImpl<ATOMIC_OP, MUTEX, CONDITION, THREADUTIL>
430 ::getValueRaw(Int64 state)
431{
432 return (state >> k_AVAILABLE_SHIFT) - (state & k_BLOCKED_MASK);
433}
434
435template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
436inline
437bool FastPostSemaphoreImpl<ATOMIC_OP, MUTEX, CONDITION, THREADUTIL>
438 ::hasAvailable(Int64 state)
439{
440 return k_AVAILABLE_INC <= state;
441}
442
443template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
444inline
445bool FastPostSemaphoreImpl<ATOMIC_OP, MUTEX, CONDITION, THREADUTIL>
446 ::hasBlockedThread(Int64 state)
447{
448 return 0 != (state & k_BLOCKED_MASK);
449}
450
451template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
452inline
453bool FastPostSemaphoreImpl<ATOMIC_OP, MUTEX, CONDITION, THREADUTIL>
454 ::isDisabled(Int64 state)
455{
456 return 0 != (state & k_DISABLED_GEN_INC);
457}
458
459template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
460inline
461bool FastPostSemaphoreImpl<ATOMIC_OP, MUTEX, CONDITION, THREADUTIL>
462 ::willHaveBlockedThread(Int64 state)
463{
464 return (state >> k_AVAILABLE_SHIFT) < (state & k_BLOCKED_MASK);
465}
466
467// PRIVATE MANIPULATORS
468template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
469int FastPostSemaphoreImpl<ATOMIC_OP, MUTEX, CONDITION, THREADUTIL>
470 ::timedWaitSlowPath(const bsls::TimeInterval& absTime,
471 const bsls::Types::Int64 initialState)
472{
473 int rv = e_SUCCESS;
474
475 const Int64 disabledGen = disabledGeneration(initialState);
476
477 // 'state' currently indicates the thread should block, yield and retest
478 // instead
479
480 THREADUTIL::yield();
481
482 Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
483
484 if (willHaveBlockedThread(state)) {
485 {
486 LockGuard<MUTEX> guard(&d_waitMutex);
487
488 // note that the following operation indicates the thread is
489 // blocked (see *Implementation* *Notes*) and does not affect the
490 // decision for other threads to block
491
492 state = ATOMIC_OP::addInt64NvAcqRel(
493 &d_state,
494 k_AVAILABLE_INC + k_BLOCKED_INC);
495
496 // wait until there is an available resource or this semaphore is
497 // disabled
498
499 while ( !hasAvailable(state)
500 && disabledGen == disabledGeneration(state)) {
501 int rv = d_waitCondition.timedWait(&d_waitMutex, absTime);
502 if (rv) {
503 ATOMIC_OP::addInt64AcqRel(&d_state, -k_BLOCKED_INC);
504 if (-1 == rv) {
505 return e_TIMED_OUT; // RETURN
506 }
507 return e_FAILED; // RETURN
508 }
509 state = ATOMIC_OP::getInt64Acquire(&d_state);
510 }
511
512 if (hasAvailable(state)) {
513 state = ATOMIC_OP::addInt64NvAcqRel(
514 &d_state,
515 -(k_AVAILABLE_INC + k_BLOCKED_INC));
516 }
517 else {
518 ATOMIC_OP::addInt64AcqRel(&d_state, -k_BLOCKED_INC);
519 rv = e_DISABLED;
520 }
521 }
522
523 // signal when 'state' indicates there is an available resource, the
524 // semaphore is not disabled, and there are blocked threads
525
526 if ( hasAvailable(state)
527 && !isDisabled(state)
528 && hasBlockedThread(state)) {
529 d_waitCondition.signal();
530 }
531 }
532 else {
533 // signal when 'state' indicates there is an available resource, the
534 // semaphore is not disabled, and there are blocked threads
535
536 if ( hasAvailable(state)
537 && !isDisabled(state)
538 && hasBlockedThread(state)) {
539 {
540 LockGuard<MUTEX> guard(&d_waitMutex);
541 }
542 d_waitCondition.signal();
543 }
544 }
545
546 return rv;
547}
548
549template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
550int FastPostSemaphoreImpl<ATOMIC_OP, MUTEX, CONDITION, THREADUTIL>
551 ::waitSlowPath(const bsls::Types::Int64 initialState)
552{
553 int rv = e_SUCCESS;
554
555 const Int64 disabledGen = disabledGeneration(initialState);
556
557 // 'state' currently indicates the thread should block, yield and retest
558 // instead
559
560 THREADUTIL::yield();
561
562 Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
563
564 if (willHaveBlockedThread(state)) {
565 {
566 LockGuard<MUTEX> guard(&d_waitMutex);
567
568 // note that the following operation indicates the thread is
569 // blocked (see *Implementation* *Notes*) and does not affect the
570 // decision for other threads to block
571
572 state = ATOMIC_OP::addInt64NvAcqRel(
573 &d_state,
574 k_AVAILABLE_INC + k_BLOCKED_INC);
575
576 // wait until there is an available resource or this semaphore is
577 // disabled
578
579 while ( !hasAvailable(state)
580 && disabledGen == disabledGeneration(state)) {
581 int rv = d_waitCondition.wait(&d_waitMutex);
582 if (rv) {
583 ATOMIC_OP::addInt64AcqRel(&d_state, -k_BLOCKED_INC);
584 return e_FAILED; // RETURN
585 }
586 state = ATOMIC_OP::getInt64Acquire(&d_state);
587 }
588
589 if (hasAvailable(state)) {
590 state = ATOMIC_OP::addInt64NvAcqRel(
591 &d_state,
592 -(k_AVAILABLE_INC + k_BLOCKED_INC));
593 }
594 else {
595 ATOMIC_OP::addInt64AcqRel(&d_state, -k_BLOCKED_INC);
596 rv = e_DISABLED;
597 }
598 }
599
600 // signal when 'state' indicates there is an available resource, the
601 // semaphore is not disabled, and there are blocked threads
602
603 if ( hasAvailable(state)
604 && !isDisabled(state)
605 && hasBlockedThread(state)) {
606 d_waitCondition.signal();
607 }
608 }
609 else {
610 // signal when 'state' indicates there is an available resource, the
611 // semaphore is not disabled, and there are blocked threads
612
613 if ( hasAvailable(state)
614 && !isDisabled(state)
615 && hasBlockedThread(state)) {
616 {
617 LockGuard<MUTEX> guard(&d_waitMutex);
618 }
619 d_waitCondition.signal();
620 }
621 }
622
623 return rv;
624}
625
626// CREATORS
627template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
628inline
629FastPostSemaphoreImpl<ATOMIC_OP, MUTEX, CONDITION, THREADUTIL>
630 ::FastPostSemaphoreImpl(bsls::SystemClockType::Enum clockType)
631: d_waitMutex()
632, d_waitCondition(clockType)
633{
634 ATOMIC_OP::initInt64(&d_state, 0);
635}
636
637template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
638inline
642: d_waitMutex()
643, d_waitCondition(clockType)
644{
645 ATOMIC_OP::initInt64(&d_state, k_AVAILABLE_INC * count);
646}
647
648// MANIPULATORS
649template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
651{
652 Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
653
654 while (false == isDisabled(state)) {
655 const Int64 expState = state;
656
657 // increment, without overflowing, the disabled attribute
658
659 Int64 newState = (state & ~k_DISABLED_GEN_MASK) |
660 ((state + k_DISABLED_GEN_INC) & k_DISABLED_GEN_MASK);
661
662 state = ATOMIC_OP::testAndSwapInt64AcqRel(&d_state,
663 state,
664 newState);
665
666 if (expState == state) {
667 state = newState;
668
669 // note that 'd_waitMutex' must be acquired to ensure a thread in a
670 // wait operation either "sees" the change in state before
671 // determining whether to block using 'd_waitCondition', or has
672 // blocked and will receive a signal sent to 'd_waitCondition'
673
674 {
675 LockGuard<MUTEX> guard(&d_waitMutex);
676 }
677 d_waitCondition.broadcast();
678 }
679 }
680
681 // When threads blocked on 'd_waitCondition' are signalled, they must
682 // prefer consuming available resources and returning success over
683 // returning 'e_DISABLED'. This ensures the signalled thread obtains the
684 // resource when another thread issues a post followed by a disablement
685 // (see DRQS 153332608). Similarly, to ensure a disablement followed by a
686 // post method does not provide a resource to the signalled thread,
687 // 'disable' must wait until no threads will block before returning. Note
688 // that the semaphore may be re-enabled (and re-disabled) during this wait.
689
690 while (isDisabled(state) && willHaveBlockedThread(state)) {
691 THREADUTIL::yield();
692
693 state = ATOMIC_OP::getInt64Acquire(&d_state);
694 }
695}
696
697template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
699{
700 Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
701
702 while (isDisabled(state)) {
703 // When this semaphore is disabled, an 'enable' followed by a post
704 // method must not provide a resource to a signalled thread that should
705 // return 'e_DISABLED' (see note in 'disable'). Hence, wait until no
706 // threads will block before performing the enablement.
707
708 if (willHaveBlockedThread(state)) {
709 THREADUTIL::yield();
710
711 state = ATOMIC_OP::getInt64Acquire(&d_state);
712 }
713 else {
714 const Int64 expState = state;
715
716 // increment, without overflowing, the disabled attribute
717
718 Int64 newState = (state & ~k_DISABLED_GEN_MASK) |
719 ((state + k_DISABLED_GEN_INC) & k_DISABLED_GEN_MASK);
720
721 state = ATOMIC_OP::testAndSwapInt64AcqRel(&d_state,
722 state,
723 newState);
724
725 if (expState == state) {
726 state = newState;
727 }
728 }
729 }
730}
731
732template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
733inline
735{
736 Int64 state = ATOMIC_OP::addInt64NvAcqRel(&d_state, k_AVAILABLE_INC);
737
738 // signal only when 'state' indicates there are no other threads that can
739 // unblock blocked threads
740
742 usePostAlwaysSignalsMitigation()
743 || k_AVAILABLE_INC == (state & k_AVAILABLE_MASK))
744 && !isDisabled(state)
745 && hasBlockedThread(state)) {
746
747 // note that 'd_waitMutex' must be acquired to ensure a thread in a
748 // wait operation either "sees" the change in state before determining
749 // whether to block using 'd_waitCondition', or has blocked and will
750 // receive a signal sent to 'd_waitCondition'
751
752 {
753 LockGuard<MUTEX> guard(&d_waitMutex);
754 }
755 d_waitCondition.signal();
756 }
757}
758
759template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
760inline
762 ::post(int value)
763{
764 Int64 v = k_AVAILABLE_INC * value;
765 Int64 state = ATOMIC_OP::addInt64NvAcqRel(&d_state, v);
766
767 // signal only when 'state' indicates there are no other threads that can
768 // unblock blocked threads
769
771 usePostAlwaysSignalsMitigation()
772 || v == (state & k_AVAILABLE_MASK))
773 && !isDisabled(state)
774 && hasBlockedThread(state)) {
775
776 // note that 'd_waitMutex' must be acquired to ensure a thread in a
777 // wait operation either "sees" the change in state before determining
778 // whether to block using 'd_waitCondition', or has blocked and will
779 // receive a signal sent to 'd_waitCondition'
780
781 {
782 LockGuard<MUTEX> guard(&d_waitMutex);
783 }
784 d_waitCondition.signal();
785 }
786}
787
788template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
789inline
792 int available,
793 int blocked)
794{
795 Int64 v = k_AVAILABLE_INC * value;
796 Int64 state = ATOMIC_OP::addInt64NvAcqRel(&d_state, v);
797
798 // signal only when 'state' indicates there are no other threads that can
799 // unblock blocked threads, or there are 'available' or more resources and
800 // 'blocked' or more threads
801
803 usePostAlwaysSignalsMitigation()
804 || v == (state & k_AVAILABLE_MASK)
805 || ( k_AVAILABLE_INC * available <= (state & k_AVAILABLE_MASK)
806 && blocked <= (state & k_BLOCKED_MASK)))
807 && !isDisabled(state)
808 && hasBlockedThread(state)) {
809
810 // note that 'd_waitMutex' must be acquired to ensure a thread in a
811 // wait operation either "sees" the change in state before determining
812 // whether to block using 'd_waitCondition', or has blocked and will
813 // receive a signal sent to 'd_waitCondition'
814
815 {
816 LockGuard<MUTEX> guard(&d_waitMutex);
817 }
818 d_waitCondition.signal();
819
821 usePostAlwaysSignalsMitigation()
822 || v == (state & k_AVAILABLE_MASK))
823 && "redundant signal sent");
824 }
825}
826
827template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
828inline
830 ::take(int maximumToTake)
831{
832 Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
833 Int64 expState;
834 Int64 count;
835
836 do {
837 // remove all available up to 'maximumToTake'
838
839 expState = state;
840
841 count = getValueRaw(state);
842
843 if (0 >= count) {
844 return 0; // RETURN
845 }
846
847 if (maximumToTake < count) {
848 count = maximumToTake;
849 }
850
851 state = ATOMIC_OP::testAndSwapInt64AcqRel(
852 &d_state,
853 state,
854 state - k_AVAILABLE_INC * count);
855 } while (state != expState);
856
857 return static_cast<int>(count);
858}
859
860template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
861inline
866
867template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
868inline
871{
872 Int64 state = ATOMIC_OP::addInt64NvAcqRel(&d_state, -k_AVAILABLE_INC);
873
874 if (isDisabled(state)) {
875 post();
876 return e_DISABLED; // RETURN
877 }
878
879 if (willHaveBlockedThread(state)) {
880 return timedWaitSlowPath(absTime, state); // RETURN
881 }
882
883 return 0;
884}
885
886template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
887inline
889{
890 Int64 state = ATOMIC_OP::addInt64NvAcqRel(&d_state, -k_AVAILABLE_INC);
891
892 if (isDisabled(state)) {
893 post();
894 return e_DISABLED; // RETURN
895 }
896
897 if (willHaveBlockedThread(state)) {
898 post();
899 return e_WOULD_BLOCK; // RETURN
900 }
901
902 return 0;
903}
904
905template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
906inline
908{
909 Int64 state = ATOMIC_OP::addInt64NvAcqRel(&d_state, -k_AVAILABLE_INC);
910
911 if (isDisabled(state)) {
912 post();
913 return e_DISABLED; // RETURN
914 }
915
916 if (willHaveBlockedThread(state)) {
917 return waitSlowPath(state); // RETURN
918 }
919
920 return 0;
921}
922
923// ACCESSORS
924template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
925inline
929{
930 return d_waitCondition.clockType();
931}
932
933template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
934inline
937{
938 Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
939
940 return static_cast<int>((state & k_DISABLED_GEN_MASK)
941 >> k_DISABLED_GEN_SHIFT);
942}
943
944template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
945inline
948{
949 Int64 count = getValueRaw(ATOMIC_OP::getInt64Acquire(&d_state));
950
951 return static_cast<int>(count > 0 ? count : 0);
952}
953
954template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
955inline
958{
959 Int64 count = getValueRaw(ATOMIC_OP::getInt64Acquire(&d_state));
960
961 return static_cast<int>(count);
962}
963
964template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
965inline
968{
969 Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
970
971 return isDisabled(state);
972}
973
974} // close package namespace
975
976
977#endif
978
979// ----------------------------------------------------------------------------
980// Copyright 2020 Bloomberg Finance L.P.
981//
982// Licensed under the Apache License, Version 2.0 (the "License");
983// you may not use this file except in compliance with the License.
984// You may obtain a copy of the License at
985//
986// http://www.apache.org/licenses/LICENSE-2.0
987//
988// Unless required by applicable law or agreed to in writing, software
989// distributed under the License is distributed on an "AS IS" BASIS,
990// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
991// See the License for the specific language governing permissions and
992// limitations under the License.
993// ----------------------------- END-OF-FILE ----------------------------------
994
995/** @} */
996/** @} */
997/** @} */
Definition bslmt_fastpostsemaphoreimpl.h:113
static bool usePostAlwaysSignalsMitigation()
Definition bslmt_fastpostsemaphoreimpl.h:407
static void removePostAlwaysSignalsMitigation()
Definition bslmt_fastpostsemaphoreimpl.h:401
Definition bslmt_fastpostsemaphoreimpl.h:145
int getValue() const
Definition bslmt_fastpostsemaphoreimpl.h:947
~FastPostSemaphoreImpl()=default
Destroy this object.
FastPostSemaphoreImpl(bsls::SystemClockType::Enum clockType=bsls::SystemClockType::e_REALTIME)
Definition bslmt_fastpostsemaphoreimpl.h:630
int getValueRaw() const
Definition bslmt_fastpostsemaphoreimpl.h:957
int takeAll()
Definition bslmt_fastpostsemaphoreimpl.h:862
void enable()
Definition bslmt_fastpostsemaphoreimpl.h:698
static const Int64 k_BLOCKED_INC
Definition bslmt_fastpostsemaphoreimpl.h:246
static const int k_DISABLED_GEN_SHIFT
Definition bslmt_fastpostsemaphoreimpl.h:250
static const Int64 k_BLOCKED_MASK
Definition bslmt_fastpostsemaphoreimpl.h:247
void postWithRedundantSignal(int value, int available, int blocked)
Definition bslmt_fastpostsemaphoreimpl.h:791
FastPostSemaphoreImpl(int count, bsls::SystemClockType::Enum clockType=bsls::SystemClockType::e_REALTIME)
Definition bslmt_fastpostsemaphoreimpl.h:640
void disable()
Definition bslmt_fastpostsemaphoreimpl.h:650
bsls::SystemClockType::Enum clockType() const
Return the clock type used for timeouts.
Definition bslmt_fastpostsemaphoreimpl.h:928
int wait()
Definition bslmt_fastpostsemaphoreimpl.h:907
static const int k_AVAILABLE_SHIFT
Definition bslmt_fastpostsemaphoreimpl.h:253
int tryWait()
Definition bslmt_fastpostsemaphoreimpl.h:888
int timedWait(const bsls::TimeInterval &absTime)
Definition bslmt_fastpostsemaphoreimpl.h:870
static const Int64 k_DISABLED_GEN_INC
Definition bslmt_fastpostsemaphoreimpl.h:248
void post()
Atomically increment the count of this semaphore.
Definition bslmt_fastpostsemaphoreimpl.h:734
bool isDisabled() const
Definition bslmt_fastpostsemaphoreimpl.h:967
void post(int value)
Definition bslmt_fastpostsemaphoreimpl.h:762
static const Int64 k_DISABLED_GEN_MASK
Definition bslmt_fastpostsemaphoreimpl.h:249
int take(int maximumToTake)
Definition bslmt_fastpostsemaphoreimpl.h:830
ReturnValue
Definition bslmt_fastpostsemaphoreimpl.h:225
@ e_FAILED
Definition bslmt_fastpostsemaphoreimpl.h:230
@ e_SUCCESS
Definition bslmt_fastpostsemaphoreimpl.h:226
@ e_WOULD_BLOCK
Definition bslmt_fastpostsemaphoreimpl.h:229
@ e_TIMED_OUT
Definition bslmt_fastpostsemaphoreimpl.h:228
@ e_DISABLED
Definition bslmt_fastpostsemaphoreimpl.h:227
int getDisabledState() const
Definition bslmt_fastpostsemaphoreimpl.h:936
static const Int64 k_AVAILABLE_MASK
Definition bslmt_fastpostsemaphoreimpl.h:252
static const Int64 k_AVAILABLE_INC
Definition bslmt_fastpostsemaphoreimpl.h:251
Definition bslmt_lockguard.h:234
Definition bsls_timeinterval.h:307
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
#define BSLS_REVIEW_OPT(X)
Definition bsls_review.h:1060
Definition bslmt_barrier.h:344
Enum
Definition bsls_systemclocktype.h:119
@ e_REALTIME
Definition bsls_systemclocktype.h:122
long long Int64
Definition bsls_types.h:134