BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bslmt_meteredmutex.h
Go to the documentation of this file.
1/// @file bslmt_meteredmutex.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bslmt_meteredmutex.h -*-C++-*-
8#ifndef INCLUDED_BSLMT_METEREDMUTEX
9#define INCLUDED_BSLMT_METEREDMUTEX
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bslmt_meteredmutex bslmt_meteredmutex
15/// @brief Provide a mutex capable of keeping track of wait and hold time.
16/// @addtogroup bsl
17/// @{
18/// @addtogroup bslmt
19/// @{
20/// @addtogroup bslmt_meteredmutex
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bslmt_meteredmutex-purpose"> Purpose</a>
25/// * <a href="#bslmt_meteredmutex-classes"> Classes </a>
26/// * <a href="#bslmt_meteredmutex-description"> Description </a>
27/// * <a href="#bslmt_meteredmutex-precise-definitions-of-wait-and-hold-time"> Precise Definitions of Wait and Hold Time </a>
28/// * <a href="#bslmt_meteredmutex-performance"> Performance </a>
29/// * <a href="#bslmt_meteredmutex-inaccuracy-of-waittime-and-holdtime"> Inaccuracy of waitTime and holdTime </a>
30/// * <a href="#bslmt_meteredmutex-usage"> Usage </a>
31/// * <a href="#bslmt_meteredmutex-example-1-basic-usage"> Example 1: Basic Usage </a>
32///
33/// # Purpose {#bslmt_meteredmutex-purpose}
34/// Provide a mutex capable of keeping track of wait and hold time.
35///
36/// # Classes {#bslmt_meteredmutex-classes}
37///
38/// - bslmt::MeteredMutex: mutex capable of keeping track of wait and hold time
39///
40/// @see
41///
42/// # Description {#bslmt_meteredmutex-description}
43/// This component provides a class, `bslmt::MeteredMutex`, that
44/// functions as a mutex and has additional capability to keep track of wait
45/// time and hold time. This class can be used, for example, in evaluating the
46/// performance of an application, based on its lock contention behavior.
47///
48/// ## Precise Definitions of Wait and Hold Time {#bslmt_meteredmutex-precise-definitions-of-wait-and-hold-time}
49///
50///
51/// Wait time is defined as the sum of the time intervals between each call to
52/// `lock` (or `tryLock`) on the underlying mutex and the return of that call.
53/// Note that if one or more threads are waiting for the lock at the point when
54/// `waitTime` is called, those waiting time intervals are *not* included in the
55/// returned wait time. Hold time is defined as the sum of the time intervals
56/// between return from each call to `lock` (or a successful call to `tryLock`)
57/// on the underlying mutex and the subsequent call to `unlock`. Note that if a
58/// thread is holding the lock at the point when `holdTime` is called, then that
59/// holding time is *not* included in the returned hold time.
60///
61/// ## Performance {#bslmt_meteredmutex-performance}
62///
63///
64/// It should be noted that the overhead in keeping track of wait and hold time
65/// is very small. We do not use additional mutexes to manipulate these times,
66/// instead, we use atomic data types (which have very small overhead compared
67/// to a mutex) to update these times atomically.
68///
69/// ## Inaccuracy of waitTime and holdTime {#bslmt_meteredmutex-inaccuracy-of-waittime-and-holdtime}
70///
71///
72/// Times reported by `waitTime` and `holdTime` are (close) approximate times
73/// and *not* 100% accurate. This inaccuracy can sometime cause surprising
74/// behavior. For example, one can incorrectly assume `lock()` and
75/// `while (tryLock() != 0);` to be effectively the same (both disallowing the
76/// thread to advance until the lock is acquired) but the wait time reported in
77/// the first case can be much more accurate than that of the second because the
78/// `lock` is called only once (and thus computation error is introduced only
79/// once) in the first case.
80///
81/// ## Usage {#bslmt_meteredmutex-usage}
82///
83///
84/// This section illustrates intended use of this component.
85///
86/// ### Example 1: Basic Usage {#bslmt_meteredmutex-example-1-basic-usage}
87///
88///
89/// In the following example, we have `NUM_THREADS` threads (that are
90/// sequentially numbered from `0` to `NUM_THREADS-1`) and two counters
91/// `evenCount` and `oddCount`. `evenCount` is incremented by the even numbered
92/// threads and `oddCount` is incremented by the odd ones. We considers two
93/// strategies to increment these counters. In the first strategy (strategy1),
94/// we use two mutexes (one for each counter) and in the second strategy
95/// (strategy2), we use a single mutex for both counters.
96/// @code
97/// int oddCount = 0;
98/// int evenCount = 0;
99///
100/// typedef bslmt::MeteredMutex Obj;
101/// Obj oddMutex;
102/// Obj evenMutex;
103/// Obj globalMutex;
104///
105/// enum { k_USAGE_NUM_THREADS = 4, k_USAGE_SLEEP_TIME = 100000 };
106/// bslmt::Barrier usageBarrier(k_USAGE_NUM_THREADS);
107///
108/// /// Create the specified `numThreads`, each executing the specified
109/// /// `function`. Number each thread (sequentially from 0 to
110/// /// `numThreads - 1`) by passing i to i'th thread. Finally join all the
111/// /// threads.
112/// void executeInParallel(int numThreads,
113/// bslmt::ThreadUtil::ThreadFunction function)
114/// {
115/// bslmt::ThreadUtil::Handle *threads =
116/// new bslmt::ThreadUtil::Handle[numThreads];
117/// assert(threads);
118///
119/// for (int i = 0; i < numThreads; ++i) {
120/// bslmt::ThreadUtil::create(&threads[i], function, (void*)i);
121/// }
122/// for (int i = 0; i < numThreads; ++i) {
123/// bslmt::ThreadUtil::join(threads[i]);
124/// }
125///
126/// delete [] threads;
127/// }
128///
129/// extern "C" {
130/// void *strategy1(void *arg)
131/// {
132/// usageBarrier.wait();
133/// int remainder = (int)(bsls::Types::IntPtr)arg % 2;
134/// if (remainder == 1) {
135/// oddMutex.lock();
136/// ++oddCount;
137/// bslmt::ThreadUtil::microSleep(k_USAGE_SLEEP_TIME);
138/// oddMutex.unlock();
139/// }
140/// else {
141/// evenMutex.lock();
142/// ++evenCount;
143/// bslmt::ThreadUtil::microSleep(k_USAGE_SLEEP_TIME);
144/// evenMutex.unlock();
145/// }
146/// return NULL;
147/// }
148/// } // extern "C"
149///
150/// extern "C" {
151/// void *strategy2(void *arg)
152/// {
153/// usageBarrier.wait();
154/// int remainder = (int)(bsls::Types::IntPtr)arg % 2;
155/// if (remainder == 1) {
156/// globalMutex.lock();
157/// ++oddCount;
158/// bslmt::ThreadUtil::microSleep(k_USAGE_SLEEP_TIME);
159/// globalMutex.unlock();
160/// }
161/// else {
162/// globalMutex.lock();
163/// ++evenCount;
164/// bslmt::ThreadUtil::microSleep(k_USAGE_SLEEP_TIME);
165/// globalMutex.unlock();
166/// }
167/// return NULL;
168/// }
169/// } // extern "C"
170/// @endcode
171/// Then in the application `main`:
172/// @code
173/// executeInParallel(k_USAGE_NUM_THREADS, strategy1);
174/// bsls::Types::Int64 waitTimeForStrategy1 =
175/// oddMutex.waitTime() + evenMutex.waitTime();
176///
177/// executeInParallel(k_USAGE_NUM_THREADS, strategy2);
178/// bsls::Types::Int64 waitTimeForStrategy2 = globalMutex.waitTime();
179///
180/// assert(waitTimeForStrategy2 > waitTimeForStrategy1);
181/// if (veryVerbose) {
182/// P(waitTimeForStrategy1);
183/// P(waitTimeForStrategy2);
184/// }
185/// @endcode
186/// We measured the wait times for each strategy. Intuitively, the wait time
187/// for the second strategy should be greater than that of the first. The
188/// output was consistent with our expectation.
189/// @code
190/// waitTimeForStrategy1 = 400787000
191/// waitTimeForStrategy2 = 880765000
192/// @endcode
193/// @}
194/** @} */
195/** @} */
196
197/** @addtogroup bsl
198 * @{
199 */
200/** @addtogroup bslmt
201 * @{
202 */
203/** @addtogroup bslmt_meteredmutex
204 * @{
205 */
206
207#include <bslscm_version.h>
208
209#include <bslmt_mutex.h>
210
211#include <bsls_atomic.h>
212#include <bsls_timeutil.h>
213#include <bsls_types.h>
214
215
216namespace bslmt {
217
218 // ==================
219 // class MeteredMutex
220 // ==================
221
222/// This class implements a mutex, that has the additional capability to
223/// keep track of hold time and wait time. The hold time is defined as the
224/// cumulative duration for which the mutex was in the locked state. The
225/// wait time is defined as the duration for which threads waited for the
226/// mutex.
227///
228/// See @ref bslmt_meteredmutex
230
231 // DATA
232 Mutex d_mutex; // underlying mutex
233 bsls::AtomicInt64 d_waitTime; // wait time
234 bsls::AtomicInt64 d_holdTime; // hold time
235 bsls::Types::Int64 d_startHoldTime; // starting point of hold time
236 bsls::AtomicInt64 d_lastResetTime; // last reset time
237
238 // NOT IMPLEMENTED
240 MeteredMutex& operator=(const MeteredMutex&);
241
242 public:
243 // CREATORS
244
245 /// Create a metered mutex in the unlocked state.
246 MeteredMutex();
247
248 /// Destroy this metered mutex.
250
251 // MANIPULATORS
252
253 /// Acquire the lock on this metered mutex. If this mutex is currently
254 /// locked, suspend the execution of the current thread until the lock
255 /// can be acquired. Update the wait and hold time appropriately. The
256 /// behavior is undefined if the calling thread already owns the lock.
257 void lock();
258
259 /// Reset the wait and hold time to zero and record the current time.
260 /// All subsequent calls (that are made before a subsequent call to
261 /// `resetMetrics`) to `waitTime` (or `holdTime`) will return the wait
262 /// (or hold) time, accumulated since this call. Also, all subsequent
263 /// calls (that are made before a subsequent call to `resetMetrics`) to
264 /// `lastResetTime` will return the time of this call.
266
267 /// Attempt to acquire the lock on this metered mutex. Return 0 on
268 /// success, and a non-zero value if this mutex is already locked, or if
269 /// an error occurs. Update the wait and hold time appropriately. The
270 /// behavior is undefined if the calling thread already owns the lock.
271 int tryLock();
272
273 /// Release the lock on this mutex that was previously acquired through
274 /// a successful call to `lock` or `tryLock`. Update the hold time
275 /// appropriately. The behavior is undefined unless the calling thread
276 /// currently owns the lock.
277 void unlock();
278
279 // ACCESSORS
280
281 /// Return the hold time (in nanoseconds) accumulated since the most
282 /// recent call to `resetMetrics` (or `MeteredMutex` if `resetMetrics`
283 /// was never called).
285
286 /// Return the time in nanoseconds (referenced to an arbitrary but fixed
287 /// origin) of the most recent invocation to `resetMetrics` (or creation
288 /// time if `resetMetrics` was never invoked). User can calculate the
289 /// difference (in nanoseconds) between the current time and the last
290 /// reset time by expression
291 /// `bsls::TimeUtil::getTimer() - clientMutex.lastResetTime()`.
293
294 /// Return the wait time (in nanoseconds), accumulated since the most
295 /// recent call to `resetMetrics` (or `MeteredMutex` if `resetMetrics`
296 /// was never called).
298};
299
300// ============================================================================
301// INLINE DEFINITIONS
302// ============================================================================
303
304 // ------------
305 // MeteredMutex
306 // ------------
307// CREATORS
308inline
310: d_lastResetTime(bsls::TimeUtil::getTimer())
311{
312}
313
314inline
318
319// MANIPULATORS
320inline
322{
324 d_mutex.lock();
325 d_startHoldTime = bsls::TimeUtil::getTimer();
326 d_waitTime += (d_startHoldTime - t1);
327}
328
329inline
331{
333 int returnStatus = d_mutex.tryLock();
335 d_waitTime += t2 - t1;
336 if (returnStatus == 0) {
337 d_startHoldTime = t2;
338 }
339 return returnStatus;
340}
341
342inline
344{
345 d_holdTime += (bsls::TimeUtil::getTimer() - d_startHoldTime);
346 d_mutex.unlock();
347}
348
349// ACCESSORS
350inline
352{
353 return d_holdTime;
354}
355
356inline
358{
359 return d_lastResetTime;
360}
361
362inline
364{
365 return d_waitTime;
366}
367
368} // close package namespace
369
370
371#endif
372
373// ----------------------------------------------------------------------------
374// Copyright 2015 Bloomberg Finance L.P.
375//
376// Licensed under the Apache License, Version 2.0 (the "License");
377// you may not use this file except in compliance with the License.
378// You may obtain a copy of the License at
379//
380// http://www.apache.org/licenses/LICENSE-2.0
381//
382// Unless required by applicable law or agreed to in writing, software
383// distributed under the License is distributed on an "AS IS" BASIS,
384// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
385// See the License for the specific language governing permissions and
386// limitations under the License.
387// ----------------------------- END-OF-FILE ----------------------------------
388
389/** @} */
390/** @} */
391/** @} */
Definition bslmt_meteredmutex.h:229
~MeteredMutex()
Destroy this metered mutex.
Definition bslmt_meteredmutex.h:315
bsls::Types::Int64 waitTime() const
Definition bslmt_meteredmutex.h:363
bsls::Types::Int64 lastResetTime() const
Definition bslmt_meteredmutex.h:357
void lock()
Definition bslmt_meteredmutex.h:321
bsls::Types::Int64 holdTime() const
Definition bslmt_meteredmutex.h:351
MeteredMutex()
Create a metered mutex in the unlocked state.
Definition bslmt_meteredmutex.h:309
int tryLock()
Definition bslmt_meteredmutex.h:330
void unlock()
Definition bslmt_meteredmutex.h:343
Definition bslmt_mutex.h:315
void lock()
Definition bslmt_mutex.h:392
int tryLock()
Definition bslmt_mutex.h:404
void unlock()
Definition bslmt_mutex.h:410
Definition bsls_atomic.h:892
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
Definition bslmt_barrier.h:344
Definition bdlt_iso8601util.h:691
static Types::Int64 getTimer()
long long Int64
Definition bsls_types.h:132