BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bsls_spinlock.h
Go to the documentation of this file.
1/// @file bsls_spinlock.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bsls_spinlock.h -*-C++-*-
8#ifndef INCLUDED_BSLS_SPINLOCK
9#define INCLUDED_BSLS_SPINLOCK
10
11#include <bsls_ident.h>
12BSLS_IDENT("$: $")
13
14/// @defgroup bsls_spinlock bsls_spinlock
15/// @brief Provide a spin lock.
16/// @addtogroup bsl
17/// @{
18/// @addtogroup bsls
19/// @{
20/// @addtogroup bsls_spinlock
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bsls_spinlock-purpose"> Purpose</a>
25/// * <a href="#bsls_spinlock-classes"> Classes </a>
26/// * <a href="#bsls_spinlock-description"> Description </a>
27/// * <a href="#bsls_spinlock-usage"> Usage </a>
28/// * <a href="#bsls_spinlock-example-1-maintaining-static-countmax-values"> Example 1: Maintaining Static CountMax Values </a>
29/// * <a href="#bsls_spinlock-example-2-fine-grained-locking"> Example 2: Fine-Grained Locking </a>
30///
31/// # Purpose {#bsls_spinlock-purpose}
32/// Provide a spin lock.
33///
34/// # Classes {#bsls_spinlock-classes}
35///
36/// - bsls::SpinLock: A mutex using "busy waiting" atomic operations
37/// - bsls::SpinLockGuard: Automatic locking-unlocking of SpinLock
38///
39/// # Description {#bsls_spinlock-description}
40/// This component provides a "busy wait" mutual exclusion lock
41/// primitive ("mutex"). A `SpinLock` is small and statically-initializable,
42/// but because it "spins" in a tight loop rather than using system operations
43/// to block the thread of execution, it is unsuited for use cases involving
44/// high contention or long critical regions. Additionally, this component does
45/// not provide any guarantee of fairness when multiple threads are contending
46/// for the same lock. Use `bsls::SpinLockGuard` for automatic
47/// locking-unlocking in a scope.
48///
49/// *WARNING*: A `bsls::SpinLock` *must* be aggregate initialized to
50/// `BSLS_SPINLOCK_UNLOCKED`. For example:
51/// @code
52/// bsls::SpinLock lock = BSLS_SPINLOCK_UNLOCKED;
53/// @endcode
54/// Note that `SpinLock` is a struct requiring aggregate initialization to allow
55/// lock variables to be statically initialized when using a C++03 compiler
56/// (i.e., without using `constexpr`).
57///
58/// ## Usage {#bsls_spinlock-usage}
59///
60///
61/// In this section we show intended use of this component.
62///
63/// ### Example 1: Maintaining Static CountMax Values {#bsls_spinlock-example-1-maintaining-static-countmax-values}
64///
65///
66/// Suppose that we want to determine the maximum number of threads executing a
67/// block of code concurrently. Note that such a use case naturally calls for a
68/// statically initialized lock and the critical region involves a few integer
69/// operations; SpinLock may be suitable.
70///
71/// First, we define a type to manage the count within a scope:
72/// @code
73/// /// This type manages a count and high-water-mark within a scope.
74/// /// It decrements the count in its destructor upon leaving the scope.
75/// class MaxConcurrencyCounter {
76///
77/// // DATA
78/// int *d_count_p;
79/// bsls::SpinLock *d_lock_p;
80///
81/// public:
82/// // CREATORS
83///
84/// /// Acquire the specified `lock` and increment the specified `count`.
85/// /// If the resulting value is larger than the specified `max`,
86/// /// load it into `max`. Release `lock` and create a scoped guard to
87/// /// decrement `count` on destruction.
88/// MaxConcurrencyCounter(int *count, int *max, bsls::SpinLock *lock);
89///
90/// /// Acquire the lock specified at construction, decrement the count
91/// /// variable, and release the lock.
92/// ~MaxConcurrencyCounter();
93/// };
94///
95/// MaxConcurrencyCounter::MaxConcurrencyCounter(int *count,
96/// int *max,
97/// bsls::SpinLock *lock)
98/// : d_count_p(count)
99/// , d_lock_p(lock) {
100/// bsls::SpinLockGuard guard(lock);
101/// int result = ++(*count);
102/// if (result > *max) {
103/// *max = result;
104/// }
105/// }
106///
107/// MaxConcurrencyCounter::~MaxConcurrencyCounter() {
108/// bsls::SpinLockGuard guard(d_lock_p);
109/// --(*d_count_p);
110/// }
111/// @endcode
112/// Next, we declare static variables to track the call count and a SpinLock to
113/// guard them. `SpinLock` may be statically initialized using the
114/// `BSLS_SPINLOCK_UNLOCKED` constant:
115/// @code
116/// {
117/// static int threadCount = 0;
118/// static int maxThreads = 0;
119/// static bsls::SpinLock threadLock = BSLS_SPINLOCK_UNLOCKED;
120/// @endcode
121/// Next, by creating a `MaxConcurrencyCounter` object, each thread entering the
122/// block of code uses the `SpinLock` to synchronize manipulation of the static
123/// count variables:
124/// @code
125/// MaxConcurrencyCounter counter(&threadCount, &maxThreads, &threadLock);
126/// @endcode
127/// Finally, closing the block synchronizes on the `SpinLock` again to decrement
128/// the thread count. Any intervening code can run in parallel.
129/// @code
130/// }
131/// @endcode
132///
133/// ### Example 2: Fine-Grained Locking {#bsls_spinlock-example-2-fine-grained-locking}
134///
135///
136/// Suppose that we have a large array of objects to be manipulated concurrently
137/// by multiple threads, but the size of the array itself does not change.
138/// (This might be because it represents an inherently fixed number of objects
139/// or because changes to the array size are infrequent and controlled by some
140/// other synchronization mechanism like a "reader-writer" lock). Thus one
141/// thread can manipulate a particular object in the array concurrently with a
142/// different thread manipulating another. If the manipulations are short and
143/// contention is likely to be low, SpinLock might be suitable due to its small
144/// size.
145///
146/// In particular, imagine we want a threadsafe "multi-queue". In this case, we
147/// would have an array of queues, each with a SpinLock member for fine-grained
148/// locking. First, we define the type to be held in the array.
149/// @code
150/// /// This type implements a threadsafe queue with a small memory footprint
151/// /// and low initialization costs. It is designed for low-contention use
152/// /// only.
153/// template<typename TYPE>
154/// class LightweightThreadsafeQueue {
155///
156/// // TYPES
157/// struct Node {
158/// TYPE d_item;
159/// Node *d_next_p;
160///
161/// Node(const TYPE& item) : d_item(item), d_next_p(0) {}
162/// };
163///
164/// // DATA
165/// Node *d_front_p; // Front of queue, or 0 if empty
166/// Node *d_back_p; // Back of queue, or 0 if empty
167/// bsls::SpinLock d_lock;
168///
169/// public:
170/// // CREATORS
171///
172/// /// Create an empty queue.
173/// LightweightThreadsafeQueue();
174///
175/// /// Destroy this object.
176/// ~LightweightThreadsafeQueue();
177///
178/// // MANIPULATORS
179///
180/// /// Remove the element at the front of the queue and load it into the
181/// /// specified `value`. Return `0` on success, or a nonzero value if
182/// /// the queue is empty.
183/// int dequeue(TYPE* value);
184///
185/// /// Add the specified `value` to the back of the queue.
186/// void enqueue(const TYPE& value);
187/// };
188/// @endcode
189/// Next, we implement the creators. Note that a different idiom is used to
190/// initialize member variables of `SpinLock` type than is used for static
191/// variables:
192/// @code
193/// template<typename TYPE>
194/// LightweightThreadsafeQueue<TYPE>::LightweightThreadsafeQueue()
195/// : d_front_p(0)
196/// , d_back_p(0)
197/// , d_lock(bsls::SpinLock::s_unlocked)
198/// {}
199///
200/// template<typename TYPE>
201/// LightweightThreadsafeQueue<TYPE>::~LightweightThreadsafeQueue() {
202/// for (Node *node = d_front_p; 0 != node; ) {
203/// Node *next = node->d_next_p;
204/// delete node;
205/// node = next;
206/// }
207/// }
208/// @endcode
209/// Then we implement the manipulator functions using `SpinLockGuard` to ensure
210/// thread safety. Note that we do memory allocation and deallocation outside
211/// the scope of the lock, as these may involve system calls that should be
212/// avoided in the scope of a SpinLock.
213/// @code
214/// template<typename TYPE>
215/// int LightweightThreadsafeQueue<TYPE>::dequeue(TYPE* value) {
216/// Node *front;
217/// {
218/// bsls::SpinLockGuard guard(&d_lock);
219/// front = d_front_p;
220/// if (0 == front) {
221/// return 1;
222/// }
223///
224/// *value = front->d_item;
225///
226/// if (d_back_p == front) {
227/// d_front_p = d_back_p = 0;
228/// } else {
229/// d_front_p = front->d_next_p;
230/// }
231/// }
232/// delete front;
233/// return 0;
234/// }
235///
236/// template<typename TYPE>
237/// void LightweightThreadsafeQueue<TYPE>::enqueue(const TYPE& value) {
238/// Node *node = new Node(value);
239/// bsls::SpinLockGuard guard(&d_lock);
240/// if (0 == d_front_p && 0 == d_back_p) {
241/// d_front_p = d_back_p = node;
242/// } else {
243/// d_back_p->d_next_p = node;
244/// d_back_p = node;
245/// }
246/// }
247/// @endcode
248/// To illustrate fine-grained locking with this queue, we create a thread
249/// function that will manipulate queues out of a large array at random.
250/// Since each element in the array is locked independently, these threads
251/// will rarely contend for the same queue and can run largely in parallel.
252/// @code
253/// const int NUM_QUEUES = 10000;
254/// const int NUM_ITERATIONS = 20000;
255///
256/// struct QueueElement {
257/// int d_threadId;
258/// int d_value;
259/// };
260///
261/// struct ThreadParam {
262/// LightweightThreadsafeQueue<QueueElement> *d_queues_p;
263/// int d_threadId;
264/// };
265///
266/// void *addToRandomQueues(void *paramAddr) {
267/// ThreadParam *param = (ThreadParam*)paramAddr;
268/// LightweightThreadsafeQueue<QueueElement> *queues = param->d_queues_p;
269/// int threadId = param->d_threadId;
270/// unsigned seed = threadId;
271/// for (int i = 0; i < NUM_ITERATIONS; ++i) {
272/// int queueIndex = rand_r(&seed) % NUM_QUEUES;
273/// LightweightThreadsafeQueue<QueueElement> *queue = queues + queueIndex;
274/// QueueElement value = { threadId, i };
275/// queue->enqueue(value);
276/// }
277/// return 0;
278/// }
279/// @endcode
280/// Finally, we create the "multi-queue" and several of these threads to
281/// manipulate it. We assume the existence of a createThread() function that
282/// starts a new thread of execution with a parameter, and we omit details of
283/// "joining" these threads.
284/// @code
285/// enum { NUM_THREADS = 3};
286/// LightweightThreadsafeQueue<QueueElement> multiQueue[NUM_QUEUES];
287/// ThreadParam threadParams[NUM_THREADS];
288/// for (int i = 0; i < NUM_THREADS; ++i) {
289/// threadParams[i].d_queues_p = multiQueue;
290/// threadParams[i].d_threadId = i + 1;
291/// createThread(addToRandomQueues, threadParams + i);
292/// }
293/// @endcode
294///
295/// @}
296/** @} */
297/** @} */
298
299/** @addtogroup bsl
300 * @{
301 */
302/** @addtogroup bsls
303 * @{
304 */
305/** @addtogroup bsls_spinlock
306 * @{
307 */
308
309#include <bsls_assert.h>
312#include <bsls_keyword.h>
313#include <bsls_platform.h>
314#include <bsls_performancehint.h>
315
316#if (BSLS_COMPILERFEATURES_CPLUSPLUS < 201703L)
317#define BSLS_SPINLOCK_USES_AGGREGATE_INITIALIZATION
318#endif
319
320/// Use this macro as the value for initializing an object of type
321/// `SpinLock`. For example:
322/// @code
323/// SpinLock lock = BSLS_SPINLOCK_UNLOCKED;
324/// @endcode
325#ifdef BSLS_SPINLOCK_USES_AGGREGATE_INITIALIZATION
326#define BSLS_SPINLOCK_UNLOCKED { {0} }
327#else
328#define BSLS_SPINLOCK_UNLOCKED (::BloombergLP::bsls::SpinLock::s_unlocked)
329#endif
330
331#ifdef BSLS_PLATFORM_OS_WINDOWS
332typedef unsigned long DWORD;
333typedef int BOOL;
334
335extern "C" {
336 __declspec(dllimport) void __stdcall Sleep(
337 DWORD dwMilliseconds);
338
339 __declspec(dllimport) DWORD __stdcall SleepEx(
340 DWORD dwMilliseconds,
341 BOOL bAlertable);
342};
343#else
344#include <pthread.h>
345#include <sched.h>
346#include <time.h>
347#endif
348
349
350namespace bsls {
351
352 // ================================
353 // class SpinLock_MemberInitializer
354 // ================================
355
356/// This component-private class is an empty type to work around legacy
357/// initialization syntax. The only object of this type should be
358/// `Spinlock::s_unlocked`, which should only ever be used to initialize
359/// member variables of type `SpinLock`.
360///
361/// See @ref bsls_spinlock
364
365 // ==============
366 // class SpinLock
367 // ==============
368
369/// A statically-initializable synchronization primitive that "spins" (i.e.,
370/// executes user instructions in a tight loop) rather than blocking waiting
371/// threads using system calls. The following idiom is used to initialize
372/// `SpinLock` variables:
373/// @code
374/// SpinLock lock = BSLS_SPINLOCK_UNLOCKED;
375/// @endcode
376/// A class member `d_lock` of type `SpinLock` may be initialized using the
377/// following idiom:
378/// @code
379/// , d_lock(SpinLock::s_unlocked)
380/// @endcode
381///
382/// See @ref bsls_spinlock
383struct SpinLock {
384
385 private:
386 // NOT IMPLEMENTED
387#ifdef BSLS_SPINLOCK_USES_AGGREGATE_INITIALIZATION
388 SpinLock& operator=(const SpinLock&) BSLS_KEYWORD_DELETED;
389
390 ///
391 /// \note Note that we would like to prohibit copy construction, but then this
392 /// class would not be an aggregate and could not be initialized
393 /// statically in C++03 mode.
394 SpinLock(const SpinLock&) = delete;
395#else
396 SpinLock& operator=(const SpinLock&) = delete;
397 SpinLock(const SpinLock&) = delete;
398#endif
399
400 // PRIVATE TYPES
401 enum {
402 e_UNLOCKED = 0, // unlocked state value
403 e_LOCKED = 1 // locked state value
404 };
405
406 // PRIVATE CLASS METHODS
407
408 /// This function provides a backoff mechanism for `lock` and `tryLock`,
409 /// using the specified `count` as the backoff counter. `count` is
410 /// incremented within this method.
411 static void doBackoff(int *count);
412
413 /// If available, invoke a pause operation (e.g., Intel's `pause`
414 /// instruction); otherwise do nothing. (i.e., this method is a no-op).
415 static void pause();
416
417 /// Sleep the specified `milliseconds`.
418 /// \note Note that this partially
419 /// reimplements `bslmt::ThreadUtil::microSleep` locally, as `bslmt` is
420 /// above `bsls`.
421 static void sleepMillis(int milliseconds);
422
423 /// Move the current thread to the end of the scheduler's queue and schedule another thread to run.
424 ///
425 /// \note Note that this allows cooperating
426 /// threads of the same priority to share CPU resources equally. Also
427 /// note that this reimplements `bslmt::ThreadUtil::yield()` locally, as
428 /// `bslmt` is above `bsls`.
429 static void yield();
430
431#ifdef BSLS_SPINLOCK_USES_AGGREGATE_INITIALIZATION
432 public:
433 // PUBLIC DATA
434
435 /// 0 when unlocked. 'd_state is public to allow aggregate
436 /// initialization where constexpr constructors are not available. It
437 /// is semantically private, and should not be used directly.
438 AtomicOperations::AtomicTypes::Int d_state;
439#else
440 private:
441 // DATA
442
443 // 0 when unlocked.
444 AtomicOperations::AtomicTypes::Int d_state;
445#endif
446
447 public:
448 // CREATORS
449#ifndef BSLS_SPINLOCK_USES_AGGREGATE_INITIALIZATION
450 /// Create a `SpinLock` object in the unlocked state.
451 constexpr SpinLock();
452
453 /// Create a `SpinLock` object in the unlocked state.
454 /// \note Note that this is
455 /// provided to allow for a syntax that was necessary in C++03, but is
456 /// no longer necessary.
457 constexpr SpinLock(const SpinLock_MemberInitializer&); // IMPLICIT
458#endif
459
460 // PUBLIC CLASS DATA
461#ifdef BSLS_SPINLOCK_USES_AGGREGATE_INITIALIZATION
462 static const SpinLock s_unlocked;
463#else
464 static constexpr SpinLock_MemberInitializer s_unlocked = {};
465#endif
466 // This constant SpinLock is always unlocked. It is suitable for use
467 // initializing class members of SpinLock type.
468
469 // MANIPULATORS
470
471 /// Spin (repeat a loop continuously without using the system to pause
472 /// or reschedule the thread) until this object is unlocked, then
473 /// atomically acquire the lock.
474 void lock();
475
476 /// Repeat a loop continuously, potentially using the system to pause or
477 /// reschedule the thread, until this object is unlocked, then
478 /// atomically acquire the lock. The spinning has backoff logic.
479 ///
480 /// \note Note that this method is recommended when system calls are permissible,
481 /// significant contention is expected, and no better mutual exclusion
482 /// primitive is available.
483 void lockWithBackoff();
484
485 /// Spin (repeat a loop continuously without using the system to pause
486 /// or reschedule the thread) until this object is unlocked, then
487 /// atomically acquire the lock. The spinning does not perform a
488 /// backoff.
489 void lockWithoutBackoff();
490
491 /// Attempt to acquire the lock; optionally specify the `numRetries`
492 /// times to attempt again if this object is already locked. Return 0
493 /// on success, and a non-zero value if the lock was not successfully acquired.
494 ///
495 /// \pre The behavior is undefined unless `0 <= numRetries`.
496 int tryLock(int numRetries = 0);
497
498 /// Release the lock.
499 /// \pre The behavior is undefined unless the current
500 /// thread holds the lock.
501 void unlock();
502};
503
504 // ===================
505 // class SpinLockGuard
506 // ===================
507
508/// This type implements a scoped guard for `SpinLock`.
509///
510/// See @ref bsls_spinlock
512
513 // DATA
514 SpinLock *d_lock_p; // lock proctored by this object
515
516 private:
517 // NOT IMPLEMENTED
519 SpinLockGuard& operator=(const SpinLockGuard&);
520
521 public:
522
523 // CREATORS
524
525 /// Create a proctor object that manages the specified `lock`. Invoke
526 /// `lock->lock()`.
527 explicit SpinLockGuard(SpinLock *lock);
528
529 /// Destroy this proctor object and invoke `unlock()` on the lock
530 /// managed by this object.
532
533 // MANIPULATORS
534
535 /// Return the lock pointer that was provided at construction and stop
536 /// managing it. (Subsequent calls to `release()` will return null and
537 /// the destruction of this object will not affect the lock.) The lock
538 /// status is not changed by this call.
539 SpinLock *release();
540};
541
542// ============================================================================
543// INLINE DEFINITIONS
544// ============================================================================
545
546 // --------------
547 // class SpinLock
548 // --------------
549// CREATORS
550#ifndef BSLS_SPINLOCK_USES_AGGREGATE_INITIALIZATION
551constexpr SpinLock::SpinLock()
552: d_state()
553{
554}
555
556constexpr SpinLock::SpinLock(const SpinLock_MemberInitializer&)
557: d_state()
558{
559}
560#endif
561
562// PRIVATE CLASS METHODS
563inline
564void SpinLock::doBackoff(int *count) {
565 if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(*count < 10)) {
566 pause();
567 } else if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(*count < 25)) {
568 yield();
569 } else {
570 sleepMillis(10);
571 }
572 ++(*count);
573}
574
575
576inline
577void SpinLock::sleepMillis(int milliseconds)
578{
579#ifdef BSLS_PLATFORM_OS_WINDOWS
580 ::Sleep(milliseconds);
581#else
582 timespec naptime;
583 naptime.tv_sec = 0;
584 naptime.tv_nsec = 1000 * 1000 * milliseconds;
585 nanosleep(&naptime, 0);
586#endif
587}
588
589inline
590void SpinLock::yield() {
591#ifdef BSLS_PLATFORM_OS_WINDOWS
592 ::SleepEx(0, 0);
593#else
594 sched_yield();
595#endif
596}
597
598// MANIPULATORS
599inline
603
604inline
606 int count = 0;
607 do {
608 // Implementation note: the outer 'if' block is not logically necessary
609 // but may reduce memory barrier costs when spinning.
610 if (e_UNLOCKED == AtomicOperations::getIntAcquire(&d_state)) {
611 if (e_UNLOCKED == AtomicOperations::swapIntAcqRel(&d_state,
612 e_LOCKED))
613 {
614 break;
615 }
616 }
617 doBackoff(&count);
618 } while (1);
619}
620
621inline
623 do {
624 // Implementation note: the outer 'if' block is not logically necessary
625 // but may reduce memory barrier costs when spinning.
626 if (e_UNLOCKED == AtomicOperations::getIntAcquire(&d_state)) {
627 if (e_UNLOCKED == AtomicOperations::swapIntAcqRel(&d_state,
628 e_LOCKED))
629 {
630 break;
631 }
632 }
633 } while (1);
634}
635
636inline
637int SpinLock::tryLock(int numRetries) {
638 int count = 0;
639 do {
640 // See lock() for implementation note.
641 if (e_UNLOCKED == AtomicOperations::getIntAcquire(&d_state)) {
642 if (e_UNLOCKED == AtomicOperations::swapIntAcqRel(&d_state,
643 e_LOCKED))
644 {
645 return 0; // RETURN
646 }
647 }
648 doBackoff(&count);
649 } while (numRetries--);
650 return -1;
651}
652
653inline
659
660 // -------------------
661 // class SpinLockGuard
662 // -------------------
663inline
664SpinLockGuard::SpinLockGuard(SpinLock *lock)
665: d_lock_p(lock) {
666 BSLS_ASSERT_SAFE(0 != lock);
667 lock->lock();
668}
669
670inline
672{
673 if (0 != d_lock_p) {
674 d_lock_p->unlock();
675 }
676}
677
678// MANIPULATORS
679inline
681 SpinLock *lock = d_lock_p;
682 d_lock_p = 0;
683 return lock;
684}
685
686} // close package namespace
687
688
689#undef BSLS_SPINLOCK_USES_AGGREGATE_INITIALIZATION
690
691#endif
692
693// ----------------------------------------------------------------------------
694// Copyright 2020 Bloomberg Finance L.P.
695//
696// Licensed under the Apache License, Version 2.0 (the "License");
697// you may not use this file except in compliance with the License.
698// You may obtain a copy of the License at
699//
700// http://www.apache.org/licenses/LICENSE-2.0
701//
702// Unless required by applicable law or agreed to in writing, software
703// distributed under the License is distributed on an "AS IS" BASIS,
704// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
705// See the License for the specific language governing permissions and
706// limitations under the License.
707// ----------------------------- END-OF-FILE ----------------------------------
708
709/** @} */
710/** @} */
711/** @} */
Definition bsls_spinlock.h:511
SpinLock * release()
Definition bsls_spinlock.h:680
~SpinLockGuard()
Definition bsls_spinlock.h:671
Definition bsls_spinlock.h:362
#define BSLS_ASSERT_SAFE(X)
Definition bsls_assert.h:1917
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
#define BSLS_KEYWORD_DELETED
Definition bsls_keyword.h:651
#define BSLS_PERFORMANCEHINT_PREDICT_LIKELY(expr)
Definition bsls_performancehint.h:451
Definition bdlt_iso8601util.h:707
static int getIntAcquire(AtomicTypes::Int const *atomicInt)
Definition bsls_atomicoperations.h:1530
static void setIntRelease(AtomicTypes::Int *atomicInt, int value)
Definition bsls_atomicoperations.h:1560
static int getInt(AtomicTypes::Int const *atomicInt)
Definition bsls_atomicoperations.h:1524
static int swapIntAcqRel(AtomicTypes::Int *atomicInt, int swapValue)
Definition bsls_atomicoperations.h:1572
Definition bsls_spinlock.h:383
void lockWithBackoff()
Definition bsls_spinlock.h:605
int tryLock(int numRetries=0)
Definition bsls_spinlock.h:637
void lockWithoutBackoff()
Definition bsls_spinlock.h:622
static const SpinLock s_unlocked
Definition bsls_spinlock.h:462
void lock()
Definition bsls_spinlock.h:600
void unlock()
Definition bsls_spinlock.h:654
AtomicOperations::AtomicTypes::Int d_state
Definition bsls_spinlock.h:438