BDE 4.14.0 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 // NOT IMPLEMENTED
221
222 public:
223 // PUBLIC CONSTANTS
225 e_SUCCESS = 0, // indicates success
226 e_DISABLED = -1, // indicates semaphore is disabled
227 e_TIMED_OUT = -2, // indicates operation timed out
228 e_WOULD_BLOCK = -3, // indicates operation would block ('tryWait')
229 e_FAILED = -4 // indicates failure reported from condition
230 };
231
232 // The following constants are used to maintain the semaphore`s `d_state`
233 // attribute values for:
234 // * number of threads blocked,
235 // * generation count for tracking enabled/disabled,
236 // * and available count.
237 //
238 // The `k_*_MASK` constants define the layout of the attributes, the
239 // `k_*_INC` constants are used to modify the `d_state` attributes, and the
240 // `k_*_SHIFT` constants allow recovery of the stored value.
241 //
242 // See **Implementation Note** for further details. These are `public` to
243 // ease testing.
244
245 static const Int64 k_BLOCKED_INC = 0x0000000000000001LL;
246 static const Int64 k_BLOCKED_MASK = 0x0000000000ffffffLL;
247 static const Int64 k_DISABLED_GEN_INC = 0x0000000001000000LL;
248 static const Int64 k_DISABLED_GEN_MASK = 0x000000000f000000LL;
249 static const int k_DISABLED_GEN_SHIFT = 24;
250 static const Int64 k_AVAILABLE_INC = 0x0000000010000000LL;
251 static const Int64 k_AVAILABLE_MASK = 0xfffffffff0000000LL;
252 static const int k_AVAILABLE_SHIFT = 28;
253
254 // CREATORS
255
256 /// Create a `FastPostSemaphoreImpl` object initially having a count of 0.
257 /// Optionally specify a `clockType` indicating the type of the system
258 /// clock against which the `bsls::TimeInterval` `absTime` timeouts passed
259 /// to the `timedWait` method are to be interpreted (see
260 /// @ref bslmt_fastpostsemaphoreimpl-supported-clock-types . If `clockType` is not specified then the
261 /// realtime system clock is used.
262 explicit
265
266 /// Create a `FastPostSemaphoreImpl` object initially having the
267 /// specified `count`. Optionally specify a `clockType` indicating the
268 /// type of the system clock against which the `bsls::TimeInterval`
269 /// `absTime` timeouts passed to the `timedWait` method are to be
270 /// interpreted (see @ref bslmt_fastpostsemaphoreimpl-supported-clock-types . If `clockType` is not
271 /// specified then the realtime system clock is used.
272 explicit
274 int count,
276
277 /// Destroy this object.
279
280 // MANIPULATORS
281
282 /// Disable waiting on this semaphore. All subsequent invocations of
283 /// `wait`, `tryWait`, and `timedWait` will fail immediately. All
284 /// blocked invocations of `wait` and `timedWait` will fail immediately.
285 /// If the semaphore is initially disabled, this call has no effect.
286 void disable();
287
288 /// Enable waiting on this semaphore. If the semaphore is initially
289 /// enabled, this call has no effect.
290 void enable();
291
292 /// Atomically increment the count of this semaphore.
293 void post();
294
295 /// Atomically increase the count of this semaphore by the specified
296 /// `value`. The behavior is undefined unless `value > 0`.
297 void post(int value);
298
299 /// Atomically increase the count of this semaphore by the specified
300 /// `value`. If the resources available to this semaphore is greater
301 /// than or equal to the specified `available` and the number of threads
302 /// blocked in this semaphore is greater than or equal to the specified
303 /// `blocked`, always send a signal to potentially wake a waiting thread
304 /// (even if the signal should not be needed). The behavior is
305 /// undefined unless `value > 0`. Note that this method is provided to
306 /// help mitigate issues in the implementation of underlying
307 /// synchronization primitives.
308 void postWithRedundantSignal(int value, int available, int blocked);
309
310 /// If the count of this semaphore is positive, reduce the count by the
311 /// lesser of the count and the specified `maximumToTake` and return the
312 /// magnitude of the change to the count. Otherwise, do nothing and
313 /// return 0.
314 int take(int maximumToTake);
315
316 /// If the count of this semaphore is positive, reduce the count to 0
317 /// and return the original value of the count. Otherwise, do nothing
318 /// and return 0.
319 int takeAll();
320
321 /// If this semaphore is initially disabled, or becomes disabled while
322 /// blocking, return `e_DISABLED` with no effect on the count.
323 /// Otherwise, block until the count of this semaphore is a positive
324 /// value or the specified `absTime` timeout expires. If the count of
325 /// this semaphore is a positive value, return 0 and atomically
326 /// decrement the count. If the `absTime` timeout expires, return
327 /// `e_TIMED_OUT` with no effect on the count. Return `e_FAILED` if an
328 /// error occurs. `absTime` is an *absolute* time represented as an
329 /// interval from some epoch, which is determined by the clock indicated
330 /// at construction (see {Supported Clock-Types} in the component
331 /// documentation).
332 int timedWait(const bsls::TimeInterval& absTime);
333
334 /// If this semaphore is initially disabled, return `e_DISABLED` with no
335 /// effect on the count. Otherwise, if the count of this semaphore is a
336 /// positive value, return 0 and atomically decrement the count. If
337 /// this semaphore is not disabled and the count of this semaphore is
338 /// not a positive value, return `e_WOULD_BLOCK` with no effect on the
339 /// count.
340 int tryWait();
341
342 /// If this semaphore is initially disabled, or becomes disabled while
343 /// blocking, return `e_DISABLED` with no effect on the count.
344 /// Otherwise, block until the count of this semaphore is a positive
345 /// value, return 0 and atomically decrement the count. Return
346 /// `e_FAILED` if an error occurs.
347 int wait();
348
349 // ACCESSORS
350
351 /// Return the clock type used for timeouts.
353
354 /// Return an odd value if this semaphore is wait disabled, and an even
355 /// value otherwise. The returned value can be used to detect a rapid
356 /// short sequence of `disable` and `enable` invocations by comparing
357 /// the value returned by `getDisabledState` before and after the
358 /// sequence. For example, for any initial state of a semphore instance
359 /// `obj`:
360 /// @code
361 /// int state = obj.getDisabledState();
362 /// obj.disable();
363 /// obj.enable();
364 /// ASSERT(state != obj.getDisabledState());
365 /// @endcode
366 /// This functionality is useful in higher-level components to determine
367 /// if this semaphore was disabled during an operation.
368 int getDisabledState() const;
369
370 /// Return the current value (`count > 0 ? count : 0`) of this
371 /// semaphore.
372 int getValue() const;
373
374 /// Return `true` if this semaphore is wait disabled, and `false`
375 /// otherwise. Note that the semaphore is created in the "wait enabled"
376 /// state.
377 bool isDisabled() const;
378};
379
380// ============================================================================
381// INLINE DEFINITIONS
382// ============================================================================
383
384 // -----------------------------------------
385 // class FastPostSemaphoreImplWorkaroundUtil
386 // -----------------------------------------
387
388// PUBLIC CLASS METHODS
389inline
391{
392 s_postAlwaysSignals = false;
393}
394
395inline
397{
398 return s_postAlwaysSignals;
399}
400
401 // ---------------------------
402 // class FastPostSemaphoreImpl
403 // ---------------------------
404
405// PRIVATE CLASS METHODS
406template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
407inline
410 ::disabledGeneration(Int64 state)
411{
412 return state & k_DISABLED_GEN_MASK;
413}
414
415template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
416inline
418 FastPostSemaphoreImpl<ATOMIC_OP, MUTEX, CONDITION, THREADUTIL>
419 ::getValueRaw(Int64 state)
420{
421 return (state >> k_AVAILABLE_SHIFT) - (state & k_BLOCKED_MASK);
422}
423
424template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
425inline
426bool FastPostSemaphoreImpl<ATOMIC_OP, MUTEX, CONDITION, THREADUTIL>
427 ::hasAvailable(Int64 state)
428{
429 return k_AVAILABLE_INC <= state;
430}
431
432template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
433inline
434bool FastPostSemaphoreImpl<ATOMIC_OP, MUTEX, CONDITION, THREADUTIL>
435 ::hasBlockedThread(Int64 state)
436{
437 return 0 != (state & k_BLOCKED_MASK);
438}
439
440template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
441inline
442bool FastPostSemaphoreImpl<ATOMIC_OP, MUTEX, CONDITION, THREADUTIL>
443 ::isDisabled(Int64 state)
444{
445 return 0 != (state & k_DISABLED_GEN_INC);
446}
447
448template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
449inline
450bool FastPostSemaphoreImpl<ATOMIC_OP, MUTEX, CONDITION, THREADUTIL>
451 ::willHaveBlockedThread(Int64 state)
452{
453 return (state >> k_AVAILABLE_SHIFT) < (state & k_BLOCKED_MASK);
454}
455
456// PRIVATE MANIPULATORS
457template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
458int FastPostSemaphoreImpl<ATOMIC_OP, MUTEX, CONDITION, THREADUTIL>
459 ::timedWaitSlowPath(const bsls::TimeInterval& absTime,
460 const bsls::Types::Int64 initialState)
461{
462 int rv = e_SUCCESS;
463
464 const Int64 disabledGen = disabledGeneration(initialState);
465
466 // 'state' currently indicates the thread should block, yield and retest
467 // instead
468
469 THREADUTIL::yield();
470
471 Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
472
473 if (willHaveBlockedThread(state)) {
474 {
475 LockGuard<MUTEX> guard(&d_waitMutex);
476
477 // note that the following operation indicates the thread is
478 // blocked (see *Implementation* *Notes*) and does not affect the
479 // decision for other threads to block
480
481 state = ATOMIC_OP::addInt64NvAcqRel(
482 &d_state,
483 k_AVAILABLE_INC + k_BLOCKED_INC);
484
485 // wait until there is an available resource or this semaphore is
486 // disabled
487
488 while ( !hasAvailable(state)
489 && disabledGen == disabledGeneration(state)) {
490 int rv = d_waitCondition.timedWait(&d_waitMutex, absTime);
491 if (rv) {
492 ATOMIC_OP::addInt64AcqRel(&d_state, -k_BLOCKED_INC);
493 if (-1 == rv) {
494 return e_TIMED_OUT; // RETURN
495 }
496 return e_FAILED; // RETURN
497 }
498 state = ATOMIC_OP::getInt64Acquire(&d_state);
499 }
500
501 if (hasAvailable(state)) {
502 state = ATOMIC_OP::addInt64NvAcqRel(
503 &d_state,
504 -(k_AVAILABLE_INC + k_BLOCKED_INC));
505 }
506 else {
507 ATOMIC_OP::addInt64AcqRel(&d_state, -k_BLOCKED_INC);
508 rv = e_DISABLED;
509 }
510 }
511
512 // signal when 'state' indicates there is an available resource, the
513 // semaphore is not disabled, and there are blocked threads
514
515 if ( hasAvailable(state)
516 && !isDisabled(state)
517 && hasBlockedThread(state)) {
518 d_waitCondition.signal();
519 }
520 }
521 else {
522 // signal when 'state' indicates there is an available resource, the
523 // semaphore is not disabled, and there are blocked threads
524
525 if ( hasAvailable(state)
526 && !isDisabled(state)
527 && hasBlockedThread(state)) {
528 {
529 LockGuard<MUTEX> guard(&d_waitMutex);
530 }
531 d_waitCondition.signal();
532 }
533 }
534
535 return rv;
536}
537
538template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
539int FastPostSemaphoreImpl<ATOMIC_OP, MUTEX, CONDITION, THREADUTIL>
540 ::waitSlowPath(const bsls::Types::Int64 initialState)
541{
542 int rv = e_SUCCESS;
543
544 const Int64 disabledGen = disabledGeneration(initialState);
545
546 // 'state' currently indicates the thread should block, yield and retest
547 // instead
548
549 THREADUTIL::yield();
550
551 Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
552
553 if (willHaveBlockedThread(state)) {
554 {
555 LockGuard<MUTEX> guard(&d_waitMutex);
556
557 // note that the following operation indicates the thread is
558 // blocked (see *Implementation* *Notes*) and does not affect the
559 // decision for other threads to block
560
561 state = ATOMIC_OP::addInt64NvAcqRel(
562 &d_state,
563 k_AVAILABLE_INC + k_BLOCKED_INC);
564
565 // wait until there is an available resource or this semaphore is
566 // disabled
567
568 while ( !hasAvailable(state)
569 && disabledGen == disabledGeneration(state)) {
570 int rv = d_waitCondition.wait(&d_waitMutex);
571 if (rv) {
572 ATOMIC_OP::addInt64AcqRel(&d_state, -k_BLOCKED_INC);
573 return e_FAILED; // RETURN
574 }
575 state = ATOMIC_OP::getInt64Acquire(&d_state);
576 }
577
578 if (hasAvailable(state)) {
579 state = ATOMIC_OP::addInt64NvAcqRel(
580 &d_state,
581 -(k_AVAILABLE_INC + k_BLOCKED_INC));
582 }
583 else {
584 ATOMIC_OP::addInt64AcqRel(&d_state, -k_BLOCKED_INC);
585 rv = e_DISABLED;
586 }
587 }
588
589 // signal when 'state' indicates there is an available resource, the
590 // semaphore is not disabled, and there are blocked threads
591
592 if ( hasAvailable(state)
593 && !isDisabled(state)
594 && hasBlockedThread(state)) {
595 d_waitCondition.signal();
596 }
597 }
598 else {
599 // signal when 'state' indicates there is an available resource, the
600 // semaphore is not disabled, and there are blocked threads
601
602 if ( hasAvailable(state)
603 && !isDisabled(state)
604 && hasBlockedThread(state)) {
605 {
606 LockGuard<MUTEX> guard(&d_waitMutex);
607 }
608 d_waitCondition.signal();
609 }
610 }
611
612 return rv;
613}
614
615// CREATORS
616template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
617inline
618FastPostSemaphoreImpl<ATOMIC_OP, MUTEX, CONDITION, THREADUTIL>
619 ::FastPostSemaphoreImpl(bsls::SystemClockType::Enum clockType)
620: d_waitMutex()
621, d_waitCondition(clockType)
622{
623 ATOMIC_OP::initInt64(&d_state, 0);
624}
625
626template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
627inline
631: d_waitMutex()
632, d_waitCondition(clockType)
633{
634 ATOMIC_OP::initInt64(&d_state, k_AVAILABLE_INC * count);
635}
636
637// MANIPULATORS
638template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
640{
641 Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
642
643 while (false == isDisabled(state)) {
644 const Int64 expState = state;
645
646 // increment, without overflowing, the disabled attribute
647
648 Int64 newState = (state & ~k_DISABLED_GEN_MASK) |
649 ((state + k_DISABLED_GEN_INC) & k_DISABLED_GEN_MASK);
650
651 state = ATOMIC_OP::testAndSwapInt64AcqRel(&d_state,
652 state,
653 newState);
654
655 if (expState == state) {
656 state = newState;
657
658 // note that 'd_waitMutex' must be acquired to ensure a thread in a
659 // wait operation either "sees" the change in state before
660 // determining whether to block using 'd_waitCondition', or has
661 // blocked and will receive a signal sent to 'd_waitCondition'
662
663 {
664 LockGuard<MUTEX> guard(&d_waitMutex);
665 }
666 d_waitCondition.broadcast();
667 }
668 }
669
670 // When threads blocked on 'd_waitCondition' are signalled, they must
671 // prefer consuming available resources and returning success over
672 // returning 'e_DISABLED'. This ensures the signalled thread obtains the
673 // resource when another thread issues a post followed by a disablement
674 // (see DRQS 153332608). Similarly, to ensure a disablement followed by a
675 // post method does not provide a resource to the signalled thread,
676 // 'disable' must wait until no threads will block before returning. Note
677 // that the semaphore may be re-enabled (and re-disabled) during this wait.
678
679 while (isDisabled(state) && willHaveBlockedThread(state)) {
680 THREADUTIL::yield();
681
682 state = ATOMIC_OP::getInt64Acquire(&d_state);
683 }
684}
685
686template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
688{
689 Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
690
691 while (isDisabled(state)) {
692 // When this semaphore is disabled, an 'enable' followed by a post
693 // method must not provide a resource to a signalled thread that should
694 // return 'e_DISABLED' (see note in 'disable'). Hence, wait until no
695 // threads will block before performing the enablement.
696
697 if (willHaveBlockedThread(state)) {
698 THREADUTIL::yield();
699
700 state = ATOMIC_OP::getInt64Acquire(&d_state);
701 }
702 else {
703 const Int64 expState = state;
704
705 // increment, without overflowing, the disabled attribute
706
707 Int64 newState = (state & ~k_DISABLED_GEN_MASK) |
708 ((state + k_DISABLED_GEN_INC) & k_DISABLED_GEN_MASK);
709
710 state = ATOMIC_OP::testAndSwapInt64AcqRel(&d_state,
711 state,
712 newState);
713
714 if (expState == state) {
715 state = newState;
716 }
717 }
718 }
719}
720
721template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
722inline
724{
725 Int64 state = ATOMIC_OP::addInt64NvAcqRel(&d_state, k_AVAILABLE_INC);
726
727 // signal only when 'state' indicates there are no other threads that can
728 // unblock blocked threads
729
731 usePostAlwaysSignalsMitigation()
732 || k_AVAILABLE_INC == (state & k_AVAILABLE_MASK))
733 && !isDisabled(state)
734 && hasBlockedThread(state)) {
735
736 // note that 'd_waitMutex' must be acquired to ensure a thread in a
737 // wait operation either "sees" the change in state before determining
738 // whether to block using 'd_waitCondition', or has blocked and will
739 // receive a signal sent to 'd_waitCondition'
740
741 {
742 LockGuard<MUTEX> guard(&d_waitMutex);
743 }
744 d_waitCondition.signal();
745 }
746}
747
748template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
749inline
751 ::post(int value)
752{
753 Int64 v = k_AVAILABLE_INC * value;
754 Int64 state = ATOMIC_OP::addInt64NvAcqRel(&d_state, v);
755
756 // signal only when 'state' indicates there are no other threads that can
757 // unblock blocked threads
758
760 usePostAlwaysSignalsMitigation()
761 || v == (state & k_AVAILABLE_MASK))
762 && !isDisabled(state)
763 && hasBlockedThread(state)) {
764
765 // note that 'd_waitMutex' must be acquired to ensure a thread in a
766 // wait operation either "sees" the change in state before determining
767 // whether to block using 'd_waitCondition', or has blocked and will
768 // receive a signal sent to 'd_waitCondition'
769
770 {
771 LockGuard<MUTEX> guard(&d_waitMutex);
772 }
773 d_waitCondition.signal();
774 }
775}
776
777template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
778inline
781 int available,
782 int blocked)
783{
784 Int64 v = k_AVAILABLE_INC * value;
785 Int64 state = ATOMIC_OP::addInt64NvAcqRel(&d_state, v);
786
787 // signal only when 'state' indicates there are no other threads that can
788 // unblock blocked threads, or there are 'available' or more resources and
789 // 'blocked' or more threads
790
792 usePostAlwaysSignalsMitigation()
793 || v == (state & k_AVAILABLE_MASK)
794 || ( k_AVAILABLE_INC * available <= (state & k_AVAILABLE_MASK)
795 && blocked <= (state & k_BLOCKED_MASK)))
796 && !isDisabled(state)
797 && hasBlockedThread(state)) {
798
799 // note that 'd_waitMutex' must be acquired to ensure a thread in a
800 // wait operation either "sees" the change in state before determining
801 // whether to block using 'd_waitCondition', or has blocked and will
802 // receive a signal sent to 'd_waitCondition'
803
804 {
805 LockGuard<MUTEX> guard(&d_waitMutex);
806 }
807 d_waitCondition.signal();
808
810 usePostAlwaysSignalsMitigation()
811 || v == (state & k_AVAILABLE_MASK))
812 && "redundant signal sent");
813 }
814}
815
816template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
817inline
819 ::take(int maximumToTake)
820{
821 Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
822 Int64 expState;
823 Int64 count;
824
825 do {
826 // remove all available up to 'maximumToTake'
827
828 expState = state;
829
830 count = getValueRaw(state);
831
832 if (0 >= count) {
833 return 0; // RETURN
834 }
835
836 if (maximumToTake < count) {
837 count = maximumToTake;
838 }
839
840 state = ATOMIC_OP::testAndSwapInt64AcqRel(
841 &d_state,
842 state,
843 state - k_AVAILABLE_INC * count);
844 } while (state != expState);
845
846 return static_cast<int>(count);
847}
848
849template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
850inline
855
856template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
857inline
860{
861 Int64 state = ATOMIC_OP::addInt64NvAcqRel(&d_state, -k_AVAILABLE_INC);
862
863 if (isDisabled(state)) {
864 post();
865 return e_DISABLED; // RETURN
866 }
867
868 if (willHaveBlockedThread(state)) {
869 return timedWaitSlowPath(absTime, state); // RETURN
870 }
871
872 return 0;
873}
874
875template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
876inline
878{
879 Int64 state = ATOMIC_OP::addInt64NvAcqRel(&d_state, -k_AVAILABLE_INC);
880
881 if (isDisabled(state)) {
882 post();
883 return e_DISABLED; // RETURN
884 }
885
886 if (willHaveBlockedThread(state)) {
887 post();
888 return e_WOULD_BLOCK; // RETURN
889 }
890
891 return 0;
892}
893
894template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
895inline
897{
898 Int64 state = ATOMIC_OP::addInt64NvAcqRel(&d_state, -k_AVAILABLE_INC);
899
900 if (isDisabled(state)) {
901 post();
902 return e_DISABLED; // RETURN
903 }
904
905 if (willHaveBlockedThread(state)) {
906 return waitSlowPath(state); // RETURN
907 }
908
909 return 0;
910}
911
912// ACCESSORS
913template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
914inline
918{
919 return d_waitCondition.clockType();
920}
921
922template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
923inline
926{
927 Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
928
929 return static_cast<int>((state & k_DISABLED_GEN_MASK)
930 >> k_DISABLED_GEN_SHIFT);
931}
932
933template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
934inline
937{
938 Int64 count = getValueRaw(ATOMIC_OP::getInt64Acquire(&d_state));
939
940 return static_cast<int>(count > 0 ? count : 0);
941}
942
943template <class ATOMIC_OP, class MUTEX, class CONDITION, class THREADUTIL>
944inline
947{
948 Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
949
950 return isDisabled(state);
951}
952
953} // close package namespace
954
955
956#endif
957
958// ----------------------------------------------------------------------------
959// Copyright 2020 Bloomberg Finance L.P.
960//
961// Licensed under the Apache License, Version 2.0 (the "License");
962// you may not use this file except in compliance with the License.
963// You may obtain a copy of the License at
964//
965// http://www.apache.org/licenses/LICENSE-2.0
966//
967// Unless required by applicable law or agreed to in writing, software
968// distributed under the License is distributed on an "AS IS" BASIS,
969// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
970// See the License for the specific language governing permissions and
971// limitations under the License.
972// ----------------------------- END-OF-FILE ----------------------------------
973
974/** @} */
975/** @} */
976/** @} */
Definition bslmt_fastpostsemaphoreimpl.h:113
static bool usePostAlwaysSignalsMitigation()
Definition bslmt_fastpostsemaphoreimpl.h:396
static void removePostAlwaysSignalsMitigation()
Definition bslmt_fastpostsemaphoreimpl.h:390
Definition bslmt_fastpostsemaphoreimpl.h:145
int getValue() const
Definition bslmt_fastpostsemaphoreimpl.h:936
~FastPostSemaphoreImpl()=default
Destroy this object.
FastPostSemaphoreImpl(bsls::SystemClockType::Enum clockType=bsls::SystemClockType::e_REALTIME)
Definition bslmt_fastpostsemaphoreimpl.h:619
int takeAll()
Definition bslmt_fastpostsemaphoreimpl.h:851
void enable()
Definition bslmt_fastpostsemaphoreimpl.h:687
static const Int64 k_BLOCKED_INC
Definition bslmt_fastpostsemaphoreimpl.h:245
static const int k_DISABLED_GEN_SHIFT
Definition bslmt_fastpostsemaphoreimpl.h:249
static const Int64 k_BLOCKED_MASK
Definition bslmt_fastpostsemaphoreimpl.h:246
void postWithRedundantSignal(int value, int available, int blocked)
Definition bslmt_fastpostsemaphoreimpl.h:780
FastPostSemaphoreImpl(int count, bsls::SystemClockType::Enum clockType=bsls::SystemClockType::e_REALTIME)
Definition bslmt_fastpostsemaphoreimpl.h:629
void disable()
Definition bslmt_fastpostsemaphoreimpl.h:639
bsls::SystemClockType::Enum clockType() const
Return the clock type used for timeouts.
Definition bslmt_fastpostsemaphoreimpl.h:917
int wait()
Definition bslmt_fastpostsemaphoreimpl.h:896
static const int k_AVAILABLE_SHIFT
Definition bslmt_fastpostsemaphoreimpl.h:252
int tryWait()
Definition bslmt_fastpostsemaphoreimpl.h:877
int timedWait(const bsls::TimeInterval &absTime)
Definition bslmt_fastpostsemaphoreimpl.h:859
static const Int64 k_DISABLED_GEN_INC
Definition bslmt_fastpostsemaphoreimpl.h:247
void post()
Atomically increment the count of this semaphore.
Definition bslmt_fastpostsemaphoreimpl.h:723
bool isDisabled() const
Definition bslmt_fastpostsemaphoreimpl.h:946
void post(int value)
Definition bslmt_fastpostsemaphoreimpl.h:751
static const Int64 k_DISABLED_GEN_MASK
Definition bslmt_fastpostsemaphoreimpl.h:248
int take(int maximumToTake)
Definition bslmt_fastpostsemaphoreimpl.h:819
ReturnValue
Definition bslmt_fastpostsemaphoreimpl.h:224
@ e_FAILED
Definition bslmt_fastpostsemaphoreimpl.h:229
@ e_SUCCESS
Definition bslmt_fastpostsemaphoreimpl.h:225
@ e_WOULD_BLOCK
Definition bslmt_fastpostsemaphoreimpl.h:228
@ e_TIMED_OUT
Definition bslmt_fastpostsemaphoreimpl.h:227
@ e_DISABLED
Definition bslmt_fastpostsemaphoreimpl.h:226
int getDisabledState() const
Definition bslmt_fastpostsemaphoreimpl.h:925
static const Int64 k_AVAILABLE_MASK
Definition bslmt_fastpostsemaphoreimpl.h:251
static const Int64 k_AVAILABLE_INC
Definition bslmt_fastpostsemaphoreimpl.h:250
Definition bslmt_lockguard.h:234
Definition bsls_timeinterval.h:301
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
#define BSLS_REVIEW_OPT(X)
Definition bsls_review.h:977
Definition bslmt_barrier.h:344
Enum
Definition bsls_systemclocktype.h:117
@ e_REALTIME
Definition bsls_systemclocktype.h:120
long long Int64
Definition bsls_types.h:132