BDE 4.14.0 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
381struct SpinLock {
382
383 private:
384 // NOT IMPLEMENTED
385#ifdef BSLS_SPINLOCK_USES_AGGREGATE_INITIALIZATION
386 SpinLock& operator=(const SpinLock&) BSLS_KEYWORD_DELETED;
387
388 /// Note that we would like to prohibit copy construction, but then this
389 /// class would not be an aggregate and could not be initialized
390 /// statically in C++03 mode.
391 SpinLock(const SpinLock&) = delete;
392#else
393 SpinLock& operator=(const SpinLock&) = delete;
394 SpinLock(const SpinLock&) = delete;
395#endif
396
397 // PRIVATE TYPES
398 enum {
399 e_UNLOCKED = 0, // unlocked state value
400 e_LOCKED = 1 // locked state value
401 };
402
403 // PRIVATE CLASS METHODS
404
405 /// This function provides a backoff mechanism for `lock` and `tryLock`,
406 /// using the specified `count` as the backoff counter. `count` is
407 /// incremented within this method.
408 static void doBackoff(int *count);
409
410 /// If available, invoke a pause operation (e.g., Intel's `pause`
411 /// instruction); otherwise do nothing. (i.e., this method is a no-op).
412 static void pause();
413
414 /// Sleep the specified `milliseconds`. Note that this partially
415 /// reimplements `bslmt::ThreadUtil::microSleep` locally, as `bslmt` is
416 /// above `bsls`.
417 static void sleepMillis(int milliseconds);
418
419 /// Move the current thread to the end of the scheduler's queue and
420 /// schedule another thread to run. Note that this allows cooperating
421 /// threads of the same priority to share CPU resources equally. Also
422 /// note that this reimplements `bslmt::ThreadUtil::yield()` locally, as
423 /// `bslmt` is above `bsls`.
424 static void yield();
425
426#ifdef BSLS_SPINLOCK_USES_AGGREGATE_INITIALIZATION
427 public:
428 // PUBLIC DATA
429
430 /// 0 when unlocked. 'd_state is public to allow aggregate
431 /// initialization where constexpr constructors are not available. It
432 /// is semantically private, and should not be used directly.
433 AtomicOperations::AtomicTypes::Int d_state;
434#else
435 private:
436 // DATA
437
438 // 0 when unlocked.
439 AtomicOperations::AtomicTypes::Int d_state;
440#endif
441
442 public:
443 // CREATORS
444#ifndef BSLS_SPINLOCK_USES_AGGREGATE_INITIALIZATION
445 /// Create a `SpinLock` object in the unlocked state.
446 constexpr SpinLock();
447
448 /// Create a `SpinLock` object in the unlocked state. Note that this is
449 /// provided to allow for a syntax that was necessary in C++03, but is
450 /// no longer necessary.
451 constexpr SpinLock(const SpinLock_MemberInitializer&); // IMPLICIT
452#endif
453
454 // PUBLIC CLASS DATA
455#ifdef BSLS_SPINLOCK_USES_AGGREGATE_INITIALIZATION
456 static const SpinLock s_unlocked;
457#else
458 static constexpr SpinLock_MemberInitializer s_unlocked = {};
459#endif
460 // This constant SpinLock is always unlocked. It is suitable for use
461 // initializing class members of SpinLock type.
462
463 // MANIPULATORS
464
465 /// Spin (repeat a loop continuously without using the system to pause
466 /// or reschedule the thread) until this object is unlocked, then
467 /// atomically acquire the lock.
468 void lock();
469
470 /// Repeat a loop continuously, potentially using the system to pause or
471 /// reschedule the thread, until this object is unlocked, then
472 /// atomically acquire the lock. The spinning has backoff logic. Note
473 /// that this method is recommended when system calls are permissible,
474 /// significant contention is expected, and no better mutual exclusion
475 /// primitive is available.
476 void lockWithBackoff();
477
478 /// Spin (repeat a loop continuously without using the system to pause
479 /// or reschedule the thread) until this object is unlocked, then
480 /// atomically acquire the lock. The spinning does not perform a
481 /// backoff.
482 void lockWithoutBackoff();
483
484 /// Attempt to acquire the lock; optionally specify the `numRetries`
485 /// times to attempt again if this object is already locked. Return 0
486 /// on success, and a non-zero value if the lock was not successfully
487 /// acquired. The behavior is undefined unless `0 <= numRetries`.
488 int tryLock(int numRetries = 0);
489
490 /// Release the lock. The behavior is undefined unless the current
491 /// thread holds the lock.
492 void unlock();
493};
494
495 // ===================
496 // class SpinLockGuard
497 // ===================
498
499/// This type implements a scoped guard for `SpinLock`.
500///
501/// See @ref bsls_spinlock
503
504 // DATA
505 SpinLock *d_lock_p; // lock proctored by this object
506
507 private:
508 // NOT IMPLEMENTED
510 SpinLockGuard& operator=(const SpinLockGuard&);
511
512 public:
513
514 // CREATORS
515
516 /// Create a proctor object that manages the specified `lock`. Invoke
517 /// `lock->lock()`.
518 explicit SpinLockGuard(SpinLock *lock);
519
520 /// Destroy this proctor object and invoke `unlock()` on the lock
521 /// managed by this object.
523
524 // MANIPULATORS
525
526 /// Return the lock pointer that was provided at construction and stop
527 /// managing it. (Subsequent calls to `release()` will return null and
528 /// the destruction of this object will not affect the lock.) The lock
529 /// status is not changed by this call.
530 SpinLock *release();
531};
532
533// ============================================================================
534// INLINE DEFINITIONS
535// ============================================================================
536
537 // --------------
538 // class SpinLock
539 // --------------
540// CREATORS
541#ifndef BSLS_SPINLOCK_USES_AGGREGATE_INITIALIZATION
542constexpr SpinLock::SpinLock()
543: d_state()
544{
545}
546
547constexpr SpinLock::SpinLock(const SpinLock_MemberInitializer&)
548: d_state()
549{
550}
551#endif
552
553// PRIVATE CLASS METHODS
554inline
555void SpinLock::doBackoff(int *count) {
556 if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(*count < 10)) {
557 pause();
558 } else if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(*count < 25)) {
559 yield();
560 } else {
561 sleepMillis(10);
562 }
563 ++(*count);
564}
565
566
567inline
568void SpinLock::sleepMillis(int milliseconds)
569{
570#ifdef BSLS_PLATFORM_OS_WINDOWS
571 ::Sleep(milliseconds);
572#else
573 timespec naptime;
574 naptime.tv_sec = 0;
575 naptime.tv_nsec = 1000 * 1000 * milliseconds;
576 nanosleep(&naptime, 0);
577#endif
578}
579
580inline
581void SpinLock::yield() {
582#ifdef BSLS_PLATFORM_OS_WINDOWS
583 ::SleepEx(0, 0);
584#else
585 sched_yield();
586#endif
587}
588
589// MANIPULATORS
590inline
594
595inline
597 int count = 0;
598 do {
599 // Implementation note: the outer 'if' block is not logically necessary
600 // but may reduce memory barrier costs when spinning.
601 if (e_UNLOCKED == AtomicOperations::getIntAcquire(&d_state)) {
602 if (e_UNLOCKED == AtomicOperations::swapIntAcqRel(&d_state,
603 e_LOCKED))
604 {
605 break;
606 }
607 }
608 doBackoff(&count);
609 } while (1);
610}
611
612inline
614 do {
615 // Implementation note: the outer 'if' block is not logically necessary
616 // but may reduce memory barrier costs when spinning.
617 if (e_UNLOCKED == AtomicOperations::getIntAcquire(&d_state)) {
618 if (e_UNLOCKED == AtomicOperations::swapIntAcqRel(&d_state,
619 e_LOCKED))
620 {
621 break;
622 }
623 }
624 } while (1);
625}
626
627inline
628int SpinLock::tryLock(int numRetries) {
629 int count = 0;
630 do {
631 // See lock() for implementation note.
632 if (e_UNLOCKED == AtomicOperations::getIntAcquire(&d_state)) {
633 if (e_UNLOCKED == AtomicOperations::swapIntAcqRel(&d_state,
634 e_LOCKED))
635 {
636 return 0; // RETURN
637 }
638 }
639 doBackoff(&count);
640 } while (numRetries--);
641 return -1;
642}
643
644inline
650
651 // -------------------
652 // class SpinLockGuard
653 // -------------------
654inline
655SpinLockGuard::SpinLockGuard(SpinLock *lock)
656: d_lock_p(lock) {
657 BSLS_ASSERT_SAFE(0 != lock);
658 lock->lock();
659}
660
661inline
663{
664 if (0 != d_lock_p) {
665 d_lock_p->unlock();
666 }
667}
668
669// MANIPULATORS
670inline
672 SpinLock *lock = d_lock_p;
673 d_lock_p = 0;
674 return lock;
675}
676
677} // close package namespace
678
679
680#undef BSLS_SPINLOCK_USES_AGGREGATE_INITIALIZATION
681
682#endif
683
684// ----------------------------------------------------------------------------
685// Copyright 2020 Bloomberg Finance L.P.
686//
687// Licensed under the Apache License, Version 2.0 (the "License");
688// you may not use this file except in compliance with the License.
689// You may obtain a copy of the License at
690//
691// http://www.apache.org/licenses/LICENSE-2.0
692//
693// Unless required by applicable law or agreed to in writing, software
694// distributed under the License is distributed on an "AS IS" BASIS,
695// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
696// See the License for the specific language governing permissions and
697// limitations under the License.
698// ----------------------------- END-OF-FILE ----------------------------------
699
700/** @} */
701/** @} */
702/** @} */
Definition bsls_spinlock.h:502
SpinLock * release()
Definition bsls_spinlock.h:671
~SpinLockGuard()
Definition bsls_spinlock.h:662
Definition bsls_spinlock.h:362
#define BSLS_ASSERT_SAFE(X)
Definition bsls_assert.h:1762
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
#define BSLS_KEYWORD_DELETED
Definition bsls_keyword.h:609
#define BSLS_PERFORMANCEHINT_PREDICT_LIKELY(expr)
Definition bsls_performancehint.h:451
Definition bdlt_iso8601util.h:691
static int getIntAcquire(AtomicTypes::Int const *atomicInt)
Definition bsls_atomicoperations.h:1528
static void setIntRelease(AtomicTypes::Int *atomicInt, int value)
Definition bsls_atomicoperations.h:1558
static int getInt(AtomicTypes::Int const *atomicInt)
Definition bsls_atomicoperations.h:1522
static int swapIntAcqRel(AtomicTypes::Int *atomicInt, int swapValue)
Definition bsls_atomicoperations.h:1570
Definition bsls_spinlock.h:381
void lockWithBackoff()
Definition bsls_spinlock.h:596
int tryLock(int numRetries=0)
Definition bsls_spinlock.h:628
void lockWithoutBackoff()
Definition bsls_spinlock.h:613
static const SpinLock s_unlocked
Definition bsls_spinlock.h:456
void lock()
Definition bsls_spinlock.h:591
void unlock()
Definition bsls_spinlock.h:645
AtomicOperations::AtomicTypes::Int d_state
Definition bsls_spinlock.h:433