BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bslmt_qlock.h
Go to the documentation of this file.
1/// @file bslmt_qlock.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bslmt_qlock.h -*-C++-*-
8#ifndef INCLUDED_BSLMT_QLOCK
9#define INCLUDED_BSLMT_QLOCK
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bslmt_qlock bslmt_qlock
15/// @brief Provide small, statically-initializable mutex lock.
16/// @addtogroup bsl
17/// @{
18/// @addtogroup bslmt
19/// @{
20/// @addtogroup bslmt_qlock
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bslmt_qlock-purpose"> Purpose</a>
25/// * <a href="#bslmt_qlock-classes"> Classes </a>
26/// * <a href="#bslmt_qlock-description"> Description </a>
27/// * <a href="#bslmt_qlock-the-bslmt-qlockguard-class"> The bslmt::QLockGuard Class </a>
28/// * <a href="#bslmt_qlock-usage"> Usage </a>
29/// * <a href="#bslmt_qlock-example-1-using-bslmt-qlock-to-implement-a-thread-safe-singleton"> Example 1: Using bslmt::QLock to Implement a Thread-Safe Singleton </a>
30///
31/// # Purpose {#bslmt_qlock-purpose}
32/// Provide small, statically-initializable mutex lock.
33///
34/// # Classes {#bslmt_qlock-classes}
35///
36/// - bslmt::QLock: Small, statically-initializable intra-process mutex
37/// - bslmt::QLockGuard: Automatic locking-unlocking of bslmt::QLock
38///
39/// @see bslmt_mutex, bslmt_atomictypes, bslmt_lockguard, bslmt_once
40///
41/// # Description {#bslmt_qlock-description}
42/// This component defines a portable and efficient lock for
43/// ensuring that only one thread at a time enters a specific "critical region"
44/// -- a section of code that accesses a shared resource -- `bslmt::Qlock` and
45/// its associated `bslmt::QLockGuard`. The functionality of the `bslmt::QLock`
46/// class overlaps those of the `bslmt::Mutex` and `bsls::SpinLock` classes, but
47/// with different usage and performance characteristics, as shown in the
48/// following grid:
49/// @code
50/// | QLock | Mutex | SpinLock
51/// -----------------------------------+-------+-------+---------
52/// Memory footprint | small | large | small
53/// Cost of construction/destruction | cheap | costly| cheap
54/// Statically initializable | yes | no | yes
55/// Speed at low contention | fast | fast | fast
56/// Speed at high contention | slow | fast | very slow
57/// Suitable for long critical regions | yes | yes | no
58/// Fair | yes | no | no
59/// @endcode
60/// The performance trade-offs for a QLock are quite different than those for a
61/// conventional mutex. QLocks are best suited for low-contention applications
62/// where large numbers of locks may be needed. For example, a node-based data
63/// structure that needs a lock for each node can benefit from the small size
64/// and low initialization cost of a QLock compared to that of a conventional
65/// mutex. A `bslmt::Mutex` object cannot be initialized statically because
66/// some platforms (e.g., Windows XP) do not have a native
67/// statically-initializable mutex type. A `bslmt::QLock` object, in contrast
68/// is statically initializable on all platforms.
69///
70/// The performance characteristics of a QLock are very similar to those of a
71/// SpinLock. However, a QLock is much more suitable than a SpinLock for
72/// situations where the critical region is more than a few instructions long.
73/// Also, although QLocks are best for low-contention situations, they do not
74/// degrade nearly as badly as SpinLocks if there is a lot of contention for the
75/// lock. They also use significantly fewer CPU cycles in high-contention
76/// situations than do SpinLocks.
77///
78/// A unique characteristic of QLocks is that they are fair. If there is
79/// contention for a lock, each thread is given the lock in the order in which
80/// it requested it. Consequently, every thread competing for the lock will get
81/// a chance before any other thread can have a second turn; no thread is ever
82/// "starved" out of the critical region. This fairness comes at a cost,
83/// however, in that the scheduler is given less leeway to schedule threads in
84/// the most efficient manner.
85///
86/// ## The bslmt::QLockGuard Class {#bslmt_qlock-the-bslmt-qlockguard-class}
87///
88///
89/// A `bslmt::QLock` is different from other locking classes such as
90/// `bslmt::Mutex` and `bsls::SpinLock` in that it cannot be manipulated except
91/// through the auxiliary `bslmt::QLockGuard` class. The reason for this
92/// limited interface is that a QLock requires a small amount of additional
93/// storage for each thread that is holding or waiting for the lock. The
94/// `bslmt::QLockGuard` provides this extra storage efficiently on the stack.
95///
96/// In typical usage, a `bslmt::QLockGuard` is created as a local (stack)
97/// variable, acquires the lock in its constructor and releases the lock in its
98/// destructor. If the lock is in use at construction time, then the current
99/// thread blocks until the lock becomes available. Although the QLock itself
100/// is intended to be shared among multiple threads, the guard object must never
101/// be used by more than one thread at a time. When multiple threads want to
102/// acquire the same QLock, each must use its own `bslmt::QLockGuard` object.
103///
104/// `bslmt::QLockGuard` also provides the following manipulators typical of
105/// locking classes:
106/// @code
107/// void lock(); // Acquire the lock, waiting if necessary
108/// int tryLock(); // Acquire the lock if possible. Fail if lock is in use.
109/// void unlock(); // Free the lock.
110/// @endcode
111/// As with other types of mutexes, only one thread may hold the lock at a time.
112/// Other threads attempting to call `lock` will block until the lock becomes
113/// available. However, it is important to remember that the manipulators
114/// listed above are only pass-through operations on the shared `bslmt::QLock`
115/// object. In other words, upon return from calling `lock` on a
116/// `bslmt::QLockGuard` object, a thread has actually acquired the lock to the
117/// underlying `bslmt::QLock`.
118///
119/// Although it is only a proxy for the actual QLock, `lock`/`unlock`/`tryLock`
120/// interface of `bslmt::QLockGuard` allows it to be treated as though it were
121/// itself a lock. In particular, it is possible to instantiate the
122/// `bslmt::LockGuard` and `bslmt::LockGuardUnlock` class templates using
123/// `bslmt::QLockGuard`. This layering of guard classes is useful for creating
124/// regions where the QLock is locked or unlocked. For example, if a thread
125/// acquires a QLock and then needs to temporarily relinquish it, it could use a
126/// `bslmt::LockGuardUnlock` as follows:
127/// @code
128/// void Node::update()
129/// {
130/// bslmt::QLockGuard qguard(&d_qlock); // 'd_qlock' is a 'bslmt::QLock'.
131/// readLunarState();
132/// if (d_moonIsFull) {
133/// // Free lock while we sleep
134/// bslmt::LockGuardUnlock<bslmt::QLockGuard> unlock(&qguard)
135/// sleep(TWENTY_FOUR_HOURS);
136/// }
137/// // Lock has been re-acquired
138/// ...
139/// }
140/// @endcode
141/// The behavior is undefined if `unlock` is invoked from a thread that did not
142/// successfully acquire the lock, or if `lock` is called twice in a thread
143/// without an intervening call to `unlock` (i.e., `bslmt::QLockGuard` is
144/// non-recursive).
145///
146/// ## Usage {#bslmt_qlock-usage}
147///
148///
149/// This section illustrates intended use of this component.
150///
151/// ### Example 1: Using bslmt::QLock to Implement a Thread-Safe Singleton {#bslmt_qlock-example-1-using-bslmt-qlock-to-implement-a-thread-safe-singleton}
152///
153///
154/// For this example, assume that we have the need to use the string "Hello"
155/// repeatedly in the form of an `bsl::string` object. Rather than construct
156/// the string each time we use it, it would be nice to have only one copy so
157/// that we can amortize the memory allocation and construction cost over all
158/// the uses of the string. It is thus logical to have a single, static
159/// variable (a singleton) of type `bsl::string` initialized with the value,
160/// "Hello". Unfortunately, as this is a multithreaded application, there is
161/// the danger that more than one thread will attempt to initialize the
162/// singleton simultaneously, causing a memory leak at best and memory
163/// corruption at worse. To solve this problem, we use a `bslmt::QLock` to
164/// synchronize access to the singleton.
165///
166/// We begin by wrapping the singleton in a function:
167/// @code
168/// const bsl::string& helloString()
169/// {
170/// @endcode
171/// This function defines two static variables, a pointer to the singleton, and
172/// a QLock to control access to the singleton. Note that both of these
173/// variables are statically initialized, so there is no need for a run-time
174/// constructor and hence no danger of a race condition among threads. The need
175/// for static initialization is the main reason we choose to use `bslmt::QLock`
176/// over `bslmt::Mutex`:
177/// @code
178/// static const bsl::string *singletonPtr = 0;
179/// static bslmt::QLock qlock = BSLMT_QLOCK_INITIALIZER;
180/// @endcode
181/// Before checking the status of the singleton pointer, we must make sure that
182/// we are not accessing the pointer at the same time that some other thread is
183/// modifying the pointer. We do this by acquiring the lock by constructing a
184/// `bslmt::QLockGuard` object:
185/// @code
186/// bslmt::QLockGuard qlockGuard(&qlock);
187/// @endcode
188/// Now we are inside the critical region. If the pointer has not already been
189/// set, we can initialize the singleton knowing that no other thread is
190/// manipulating or accessing these variables at the same time. Note that this
191/// critical region involves constructing a variable of type `bsl::string`.
192/// This operation, while not ultra-expensive, is too lengthy for comfortably
193/// holding a spinlock. Again, the characteristics of `bslmt::QLock` are
194/// superior to the alternatives for this application. (It is worth noting that
195/// the QLock concept was created specifically to permit this kind of one-time
196/// processing. See also @ref bslmt_once .)
197/// @code
198/// if (! singletonPtr) {
199/// static bsl::string singleton("Hello");
200/// singletonPtr = &singleton;
201/// }
202/// @endcode
203/// Finally, we return a reference to the singleton. The destructor for
204/// `bslmt::QLockGuard` will automatically unlock the QLock and allow another
205/// thread into the critical region.
206/// @code
207/// return *singletonPtr;
208/// }
209/// @endcode
210/// The following test program shows how our singleton function can be called.
211/// Note that `hello1` and `hello2` have the same address, demonstrating that
212/// there was only one string created.
213/// @code
214/// int usageExample1()
215/// {
216/// const bsl::string EXPECTED("Hello");
217///
218/// const bsl::string& hello1 = helloString();
219/// assert(hello1 == EXPECTED);
220///
221/// const bsl::string& hello2 = helloString();
222/// assert(hello2 == EXPECTED);
223/// assert(&hello2 == &hello1);
224///
225/// return 0;
226/// }
227/// @endcode
228/// @}
229/** @} */
230/** @} */
231
232/** @addtogroup bsl
233 * @{
234 */
235/** @addtogroup bslmt
236 * @{
237 */
238/** @addtogroup bslmt_qlock
239 * @{
240 */
241
242#include <bslscm_version.h>
243
244#include <bsls_atomic.h>
246#include <bsls_assert.h>
247
248
249namespace bslmt {
250
251class Semaphore;
252
253/// Use this macro as the value for initializing an object of type `QLock`
254/// For example:
255/// @code
256/// QLock mylock = BSLMT_QLOCK_INITIALIZER;
257/// @endcode
258#define BSLMT_QLOCK_INITIALIZER { {0} }
259
260 // ============
261 // struct QLock
262 // ============
263
264/// An efficient statically-initializable synchronization primitive that
265/// enables serialized access to shared resources. Objects of this class
266/// can only be manipulated through the use of a `QLockGuard`. The
267/// following idiom is used to initialize objects of type `QLock`:
268/// @code
269/// QLock mylock = BSLMT_QLOCK_INITIALIZER;
270/// @endcode
271struct QLock {
272
273 private:
274 // NOT IMPLEMENTED
275 QLock& operator=(const QLock&);
276
277 // We would like to prohibit copy construction, but then this class would
278 // not be a POD and we would lose the ability to initialize objects of this
279 // class statically.
280
281 // QLock(const QLock&);
282
283 public:
284 /// Pointer to the last guard in the queue of guards waiting for this
285 /// lock, or 0 if the lock is unlocked.
286 ///
287 /// Note that the first guard in the queue owns the lock so that
288 /// `d_guardQueueTail` points to the owner of the lock when the lock is
289 /// locked and there are no additional guards waiting.
290 ///
291 /// It would have been preferable for this member to be private, but
292 /// then this class would not be statically initializable. Also, it
293 /// would have been preferable to make this member an instance of
294 /// `bsls::AtomicPointer<>`, but again, we would lose the ability to
295 /// initialize statically.
296 bsls::AtomicOperations::AtomicTypes::Pointer d_guardQueueTail;
297
298 public:
299 // MANIPULATORS
300
301 /// Set this lock into the initial unlocked state.
302 void initialize();
303
304 // ACCESSORS
305
306 /// Return true if this lock is locked and false otherwise.
307 bool isLocked() const;
308};
309
310 // =====================
311 // class QLock_EventFlag
312 // =====================
313
314/// [**PRIVATE**] This class provides a thread-safe mechanism for one thread
315/// to inform another thread that some event has occurred. A flag provides
316/// two primary manipulators, `set`, which indicates the event has occurred,
317/// and `waitUntilSet`, which waits until that event has occurred (or
318/// returns immediately if it has already occurred). A flag is intended to
319/// be used by only two threads: a thread setting the flag, and a thread
320/// waiting for the flag to be set, and the behavior is undefined if `set`
321/// is called while the flag is already set, or if `waitUntilSet` is called
322/// while another thread is waiting for the flag.
323///
324/// This class is an implementation detail of the @ref bslmt_qlock , and must
325/// not be used by client code.
326///
327/// See @ref bslmt_qlock
329
330 private:
331 // PRIVATE TYPES
333
334 // DATA
335 AtomicSemaphorePtr d_status; // status of this flag
336
337 private:
338 // NOT IMPLEMENTED
340 QLock_EventFlag& operator=(const QLock_EventFlag&);
341
342 public:
343
344 // CREATORS
345
346 /// Create an unset flag.
348
349 /// Destroy this flag. The behavior is undefined if a thread is
350 /// currently waiting for the flag to be set.
352
353 // MANIPULATORS
354
355 /// Reset this flag to the unset state. The behavior is undefined if a
356 /// thread is waiting for this flag to be set.
357 void reset();
358
359 /// Set this flag, and if a thread is waiting for it, signal the waiting
360 /// thread. The behavior is undefined if this flag is already set.
361 void set();
362
363 /// Wait until this flag has been set (returning immediately if this
364 /// flag is already set), and, if this flag is not already set, spin for
365 /// the specified `spinCount` iterations before waiting on a semaphore.
366 /// The behavior is undefined unless there are no other threads waiting
367 /// for this flag to be set.
368 void waitUntilSet(int spinRetryCount);
369};
370
371 // ================
372 // class QLockGuard
373 // ================
374
375/// This class provides the means to acquire and release the lock on a
376/// `QLock` object. Typically, the lock is acquired at construction and
377/// released automatically on destruction. This class also provides
378/// explicit `lock`, `tryLock`, and `unlock` primitives.
379///
380/// See @ref bslmt_qlock
382
383 private:
384
385 QLock *d_qlock_p; // points to tail of the queue.
386
387 QLockGuard *d_next; // next object in queue.
388
389 QLock_EventFlag d_readyFlag; // flag indicating when the lock is released
390 // by its predecessor in the queue
391
392 QLock_EventFlag d_nextFlag; // flag indicating 'd_next' is set by its
393 // successor.
394
395 bool d_locked; // 'true' if this guard holds the lock
396
397 private:
398 // NOT IMPLEMENTED
399 QLockGuard(const QLockGuard&);
400 QLockGuard& operator=(const QLockGuard&);
401
402 // PRIVATE MANIPULATORS
403
404 /// Free the lock but do not clear the member variables of this class.
405 void unlockRaw();
406
407 public:
408 // CREATORS
409
410 /// Create a guard in the unlocked state, not associated with any
411 /// `QLock` objects.
412 QLockGuard();
413
414 /// Create a guard associated with the specified `qlock`. Acquire the
415 /// lock unless the (optionally) specified `doLock` is false. If the
416 /// `lock` is not free, block until it can be acquired.
417 explicit QLockGuard(QLock *qlock, bool doLock = true);
418
419 /// Destroy this object. If this object holds a lock, automatically
420 /// free it.
421 ~QLockGuard();
422
423 // MANIPULATORS
424
425 /// Associate this guard with the specified `qlock`. The behavior is
426 /// undefined if this object is already in a locked state.
427 void setQLock(QLock *qlock);
428
429 /// Acquire a lock on the associated `QLock` object. If the `lock` is
430 /// not free, block until it can be acquired. The behavior is undefined
431 /// if the calling thread already owns the lock on the QLock.
432 void lock();
433
434 /// Associate this guard with the specified `qlock` and acquire the
435 /// lock. If the `lock` is not free, block until it can be acquired.
436 /// The behavior is undefined if the calling thread already owns the
437 /// lock on `qlock` or if this object is in the locked state.
438 void lock(QLock *qlock);
439
440 /// Attempt to acquire a lock on the associated `QLock` object. Return
441 /// 0 on success, a positive value of the associated QLock object is
442 /// already locked, or a negative value if an error occurs.
443 int tryLock();
444
445 /// Release the lock on the associated `QLock`. The behavior is
446 /// undefined unless this guard previously acquired the lock and has not
447 /// already released it.
448 void unlock();
449};
450
451// ============================================================================
452// INLINE DEFINITIONS
453// ============================================================================
454
455 // -----------
456 // class QLock
457 // -----------
458
459// MANIPULATORS
460inline
465
466// ACCESSORS
467inline
468bool QLock::isLocked() const
469{
471}
472
473 // ---------------------
474 // class QLock_EventFlag
475 // ---------------------
476
477// CREATORS
478inline
480: d_status(0)
481{
482}
483
484inline
488
489// MANIPULATORS
490inline
492{
493 d_status = 0;
494}
495
496 // ----------------
497 // class QLockGuard
498 // ----------------
499
500// CREATORS
501inline
503: d_qlock_p (0)
504, d_next (0)
505, d_readyFlag ()
506, d_nextFlag ()
507, d_locked (false)
508{
509}
510
511inline
512QLockGuard::QLockGuard(QLock *qlock, bool doLock)
513: d_qlock_p (qlock)
514, d_next (0)
515, d_readyFlag ()
516, d_nextFlag ()
517, d_locked (false)
518{
519 if (doLock) {
520 lock();
521 }
522}
523
524inline
526{
527 if (d_locked) {
528 unlockRaw();
529 }
530}
531
532// MANIPULATORS
533inline
535{
536 BSLS_ASSERT_SAFE(!d_locked);
537
538 d_qlock_p = qlock;
539}
540
541inline
543{
544 BSLS_ASSERT_SAFE(!d_locked);
545 BSLS_ASSERT_SAFE(qlock);
546
547 d_qlock_p = qlock;
548 lock();
549}
550
551inline
553{
554 if (d_locked) {
555 // Release the lock, and reset the state variables so it can be
556 // relocked.
557
558 unlockRaw();
559
560 d_locked = false;
561 d_next = 0;
562 d_readyFlag.reset();
563 d_nextFlag.reset();
564 }
565}
566
567} // close package namespace
568
569
570#endif
571
572// ----------------------------------------------------------------------------
573// Copyright 2015 Bloomberg Finance L.P.
574//
575// Licensed under the Apache License, Version 2.0 (the "License");
576// you may not use this file except in compliance with the License.
577// You may obtain a copy of the License at
578//
579// http://www.apache.org/licenses/LICENSE-2.0
580//
581// Unless required by applicable law or agreed to in writing, software
582// distributed under the License is distributed on an "AS IS" BASIS,
583// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
584// See the License for the specific language governing permissions and
585// limitations under the License.
586// ----------------------------- END-OF-FILE ----------------------------------
587
588/** @} */
589/** @} */
590/** @} */
Definition bslmt_qlock.h:381
QLockGuard()
Definition bslmt_qlock.h:502
void setQLock(QLock *qlock)
Definition bslmt_qlock.h:534
~QLockGuard()
Definition bslmt_qlock.h:525
void unlock()
Definition bslmt_qlock.h:552
Definition bslmt_qlock.h:328
void reset()
Definition bslmt_qlock.h:491
void waitUntilSet(int spinRetryCount)
QLock_EventFlag()
Create an unset flag.
Definition bslmt_qlock.h:479
~QLock_EventFlag()
Definition bslmt_qlock.h:485
Definition bsls_atomic.h:1349
#define BSLS_ASSERT_SAFE(X)
Definition bsls_assert.h:1762
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
Definition bslmt_barrier.h:344
Definition bslmt_qlock.h:271
bool isLocked() const
Return true if this lock is locked and false otherwise.
Definition bslmt_qlock.h:468
bsls::AtomicOperations::AtomicTypes::Pointer d_guardQueueTail
Definition bslmt_qlock.h:296
void initialize()
Set this lock into the initial unlocked state.
Definition bslmt_qlock.h:461
static void * getPtr(AtomicTypes::Pointer const *atomicPtr)
Definition bsls_atomicoperations.h:2306
static void setPtrRelaxed(AtomicTypes::Pointer *atomicPtr, void *value)
Definition bsls_atomicoperations.h:2338