Ocean
Loading...
Searching...
No Matches
Mutex.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8#ifndef META_OCEAN_TRACKING_SLAM_MUTEX_H
9#define META_OCEAN_TRACKING_SLAM_MUTEX_H
10
12
14#include "ocean/base/Lock.h"
17
18#include <mutex>
19#include <shared_mutex>
20
21namespace Ocean
22{
23
24namespace Tracking
25{
26
27namespace SLAM
28{
29
30/**
31 * Definition of a mutex supporting read and write locks.
32 * The mutex is not recursive.
33 *
34 * This mutex allows multiple concurrent read locks (shared access) but only one write lock (exclusive access).
35 * Use ReadLock for read-only operations and WriteLock when modifying shared data.
36 *
37 * Example usage:
38 * @code
39 * class SharedResource
40 * {
41 * public:
42 *
43 * // Read operation: multiple threads can read simultaneously
44 * int getValue() const
45 * {
46 * const ReadLock readLock(mutex_);
47 *
48 * return value_;
49 * }
50 *
51 * // Write operation: exclusive access, blocks all readers and writers
52 * void setValue(int newValue)
53 * {
54 * const WriteLock writeLock(mutex_);
55 *
56 * value_ = newValue;
57 * }
58 *
59 * // Named locks for timing diagnostics (when LockManager::isEnabled_ is true)
60 * void updateWithTiming(int newValue)
61 * {
62 * const WriteLock writeLock(mutex_, "SharedResource::updateWithTiming");
63 *
64 * value_ = newValue;
65 * }
66 *
67 * protected:
68 *
69 * /// The value of the shared resource.
70 * int value_ = 0;
71 *
72 * /// The mutex of the shared resource.
73 * mutable Mutex mutex_;
74 * };
75 * @endcode
76 * @ingroup trackingslam
77 */
78using Mutex = std::shared_mutex;
79
80/**
81 * This class implements a singleton manager for collecting and reporting lock timing measurements.
82 * The isEnabled_ constant allows to activate or deactivate measurements in general.<br>
83 * The manager can operate in two modes based on the threshold values:<br>
84 * - If threshold > 0: Logs to console when acquisition delay or hold duration exceeds the threshold.<br>
85 * - If threshold == 0: Collects all measurements for aggregate reporting via report().
86 * In production code, measurements should be disabled as they add minor performance overhead.
87 * @ingroup trackingslam
88 */
89class LockManager : public Singleton<LockManager>
90{
91 friend class Singleton<LockManager>;
92
93 public:
94
95 /// True, to enable lock timing measurements; False, to disable.
96 static constexpr bool isEnabled_ = false;
97
98 /// The threshold in milliseconds for acquisition delay logging, with range [0, infinity), 0 to collect measurements instead of logging.
99 static constexpr double acquisitionDelayThresholdMs_ = 2.0;
100
101 /// The threshold in milliseconds for hold duration logging, with range [0, infinity), 0 to collect measurements instead of logging.
102 static constexpr double holdDurationThresholdMs_ = 2.0;
103
104 protected:
105
106 /**
107 * Definition of a key combining a lock type (read/write) and a name.
108 */
109 using Key = std::pair<bool, std::string>;
110
111 /**
112 * Definition of a vector holding duration measurements in seconds.
113 */
114 using Measurements = std::vector<double>;
115
116 /**
117 * Definition of a map mapping keys to measurements.
118 */
119 using Map = std::unordered_map<Key, Measurements, PairHash>;
120
121 public:
122
123 /**
124 * Generates a report of all collected lock timing measurements.
125 * The report includes P99, P99.5, P99.9, and P100 (worst) statistics for each lock.
126 * @param skipZero True, to skip locks with measurements below 0.01ms; False, to include all locks
127 * @return The formatted report string, "Report is disabled" if isEnabled_ is false
128 */
129 std::string report(const bool skipZero = true);
130
131 /**
132 * Adds an acquisition delay measurement for a specific lock.
133 * @param name The name of the lock
134 * @param isWriteLock True, if this is a write lock; False, if this is a read lock
135 * @param duration The acquisition delay in seconds, with range [0, infinity)
136 */
137 void addAcquisitionDelayMeasurement(const std::string& name, const bool isWriteLock, const double duration);
138
139 /**
140 * Adds a hold duration measurement for a specific lock.
141 * @param name The name of the lock
142 * @param isWriteLock True, if this is a write lock; False, if this is a read lock
143 * @param duration The hold duration in seconds, with range [0, infinity)
144 */
145 void addHoldDurationMeasurement(const std::string& name, const bool isWriteLock, const double duration);
146
147 protected:
148
149 /**
150 * Creates a new lock manager.
151 */
153
154 protected:
155
156 /// The map storing acquisition delay measurements for each lock.
158
159 /// The map storing hold duration measurements for each lock.
161
162 /// The lock protecting the maps.
163 mutable Lock lock_;
164};
165
166/**
167 * This class implements a helper for timing lock acquisition and hold durations.
168 * The class is used as a base class for ReadLock and WriteLock to optionally measure lock performance.
169 * @tparam tEnable True, to enable timing measurements; False, to disable (no-op implementation)
170 * @ingroup trackingslam
171 */
172template <bool tEnable>
174{
175 protected:
176
177 /**
178 * Creates a new lock timer without a name.
179 */
180 LockTimer() = default;
181
182 /**
183 * Creates a new lock timer with a name.
184 * @param name The name of the lock for identification in reports, must be valid
185 */
186 LockTimer(const char* name);
187
188 /**
189 * Reports the acquisition delay to the LockManager.
190 * This function is called after the lock has been acquired.
191 * @param isWriteLock True, if this is a write lock; False, if this is a read lock
192 */
193 void reportAcquisitionDelay(const bool isWriteLock);
194
195 /**
196 * Reports the hold duration to the LockManager.
197 * This function is called before the lock is released.
198 * @param isWriteLock True, if this is a write lock; False, if this is a read lock
199 */
200 void reportHoldDuration(const bool isWriteLock);
201
202 protected:
203
204 /// The name of the lock, nullptr if not specified.
205 const char* name_ = nullptr;
206
207 /// The timer for measuring acquisition delay and hold duration.
209};
210
211/**
212 * Specialization of LockTimer for disabled timing.
213 * This specialization provides no-op implementations for all methods to eliminate overhead when timing is disabled.
214 * @ingroup trackingslam
215 */
216template <>
217class LockTimer<false>
218{
219 protected:
220
221 /**
222 * Creates a new lock timer without a name.
223 */
224 LockTimer() = default;
225
226 /**
227 * Creates a new lock timer with a name.
228 * @param name The name of the lock, unused in this specialization
229 */
230 LockTimer(const char* /*name*/)
231 {
232 // nothing to do here
233 }
234
235 /**
236 * No-op implementation of acquisition delay reporting.
237 * @param isWriteLock Unused in this specialization
238 */
239 void reportAcquisitionDelay(const bool /*isWriteLock*/)
240 {
241 // nothing to do here
242 }
243
244 /**
245 * No-op implementation of hold duration reporting.
246 * @param isWriteLock Unused in this specialization
247 */
248 void reportHoldDuration(const bool /*isWriteLock*/)
249 {
250 // nothing to do here
251 }
252};
253
254/**
255 * This class implements a scoped read lock for a shared mutex.
256 * The lock is acquired upon construction and released upon destruction.
257 * Multiple read locks can be held simultaneously, but a read lock cannot be acquired while a write lock is held.
258 * @ingroup trackingslam
259 */
260class ReadLock : public LockTimer<LockManager::isEnabled_>
261{
262 public:
263
264 /**
265 * Creates a new read lock and acquires the mutex.
266 * @param mutex The mutex to lock
267 */
268 explicit ReadLock(Mutex& mutex);
269
270 /**
271 * Creates a new read lock with a name and acquires the mutex.
272 * @param mutex The mutex to lock
273 * @param name The name of the lock for identification in timing reports, must be valid
274 */
275 ReadLock(Mutex& mutex, const char* name);
276
277 /**
278 * Move constructor.
279 * @param readLock The read lock to move
280 */
281 ReadLock(ReadLock&&) = default;
282
283 /**
284 * Move assignment operator.
285 * @param readLock The read lock to move
286 * @return Reference to this object
287 */
289
290 /**
291 * Destructs the read lock and releases the mutex.
292 */
293 ~ReadLock();
294
295 /**
296 * Explicitly releases the lock before the scoped object is destroyed.
297 */
298 void unlock();
299
300#ifdef OCEAN_DEBUG
301 /**
302 * Returns whether a mutex is currently locked (debug only).
303 * This function attempts to acquire an exclusive lock to determine if the mutex is held.
304 * @param mutex The mutex to check
305 * @return True, if the mutex is locked; False, otherwise
306 */
307 static bool debugIsLocked(Mutex& mutex);
308#endif // OCEAN_DEBUG
309
310 protected:
311
312 /// The underlying shared lock.
313 std::shared_lock<Mutex> lock_;
314};
315
316/**
317 * This class implements a scoped write lock for a shared mutex.
318 * The lock is acquired upon construction and released upon destruction.
319 * A write lock provides exclusive access; no other read or write locks can be held simultaneously.
320 * @ingroup trackingslam
321 */
322class WriteLock : public LockTimer<LockManager::isEnabled_>
323{
324 public:
325
326 /**
327 * Creates a new write lock and acquires the mutex.
328 * @param mutex The mutex to lock
329 */
330 explicit WriteLock(Mutex& mutex);
331
332 /**
333 * Creates a new write lock with a name and acquires the mutex.
334 * @param mutex The mutex to lock
335 * @param name The name of the lock for identification in timing reports, must be valid
336 */
337 explicit WriteLock(Mutex& mutex, const char* name);
338
339 /**
340 * Move constructor.
341 * @param writeLock The write lock to move
342 */
343 WriteLock(WriteLock&&) = default;
344
345 /**
346 * Move assignment operator.
347 * @param writeLock The write lock to move
348 * @return Reference to this object
349 */
351
352 /**
353 * Destructs the write lock and releases the mutex.
354 */
355 ~WriteLock();
356
357 /**
358 * Explicitly releases the lock before the scoped object is destroyed.
359 */
360 void unlock();
361
362#ifdef OCEAN_DEBUG
363 /**
364 * Returns whether a mutex is currently locked (debug only).
365 * This function attempts to acquire an exclusive lock to determine if the mutex is held.
366 * @param mutex The mutex to check
367 * @return True, if the mutex is locked; False, otherwise
368 */
369 static bool debugIsLocked(Mutex& mutex);
370#endif // OCEAN_DEBUG
371
372 protected:
373
374 /// The underlying unique lock.
375 std::unique_lock<Mutex> lock_;
376};
377
378template <bool tEnable>
380 name_(name)
381{
382 ocean_assert(name_ != nullptr);
383}
384
385template <bool tEnable>
387{
388 const double seconds = timer_.seconds();
389
390 const char* name = name_ != nullptr ? name_ : "Unknown";
391
393 {
394 const double milliseconds = seconds * 1000.0;
395
397 {
398 if (isWriteLock)
399 {
400 Log::warning() << "Write lock acquisition time: " << name << " took " << milliseconds << "ms";
401 }
402 else
403 {
404 Log::warning() << "Read lock acquisition time: " << name << " took " << milliseconds << "ms";
405 }
406 }
407 }
408 else
409 {
410 LockManager::get().addAcquisitionDelayMeasurement(name, isWriteLock, seconds);
411 }
412
413 timer_.start(); // re-start timer for hold duration measurement
414}
415
416template <bool tEnable>
417void LockTimer<tEnable>::reportHoldDuration(const bool isWriteLock)
418{
419 const double seconds = timer_.seconds();
420
421 const char* name = name_ != nullptr ? name_ : "Unknown";
422
423 if constexpr (LockManager::holdDurationThresholdMs_ > 0.0)
424 {
425 const double milliseconds = seconds * 1000.0;
426
427 if (milliseconds >= LockManager::holdDurationThresholdMs_)
428 {
429 if (isWriteLock)
430 {
431 Log::warning() << "Write lock hold duration time: " << name << " took " << milliseconds << "ms";
432 }
433 else
434 {
435 Log::warning() << "Read lock hold duration time: " << name << " took " << milliseconds << "ms";
436 }
437 }
438 }
439 else
440 {
441 LockManager::get().addHoldDurationMeasurement(name, isWriteLock, seconds);
442 }
443}
444
445inline ReadLock::ReadLock(Mutex& mutex) :
446 LockTimer(),
447 lock_(mutex)
448{
450}
451
452inline ReadLock::ReadLock(Mutex& mutex, const char* name) :
453 LockTimer(name),
454 lock_(mutex)
455{
457}
458
460{
461 if constexpr (LockManager::isEnabled_)
462 {
463 if (lock_.owns_lock())
464 {
465 reportHoldDuration(false);
466 }
467 }
468}
469
470inline void ReadLock::unlock()
471{
472 ocean_assert(lock_.owns_lock());
473
474 if constexpr (LockManager::isEnabled_)
475 {
476 reportHoldDuration(false);
477 }
478
479 lock_.unlock();
480}
481
482#ifdef OCEAN_DEBUG
484{
485 if (!mutex.try_lock()) // not try_lock_shared() as this returns would always return true for a read lock
486 {
487 // we cannot acquire the lock, so it must be locked
488 return true;
489 }
490
491 mutex.unlock();
492 return false;
493}
494#endif // OCEAN_DEBUG
495
497 LockTimer(),
498 lock_(mutex)
499{
501}
502
503inline WriteLock::WriteLock(Mutex& mutex, const char* name) :
504 LockTimer(name),
505 lock_(mutex)
506{
508}
509
511{
512 if constexpr (LockManager::isEnabled_)
513 {
514 if (lock_.owns_lock())
515 {
516 reportHoldDuration(true);
517 }
518 }
519}
520
521inline void WriteLock::unlock()
522{
523 ocean_assert(lock_.owns_lock());
524
525 if constexpr (LockManager::isEnabled_)
526 {
527 if (lock_.owns_lock())
528 {
529 reportHoldDuration(true);
530 }
531 }
532
533 lock_.unlock();
534}
535
536#ifdef OCEAN_DEBUG
538{
539 if (!mutex.try_lock())
540 {
541 // we cannot acquire the lock, so it must be locked
542 return true;
543 }
544
545 mutex.unlock();
546 return false;
547}
548#endif // OCEAN_DEBUG
549
550}
551
552}
553
554}
555
556#endif // META_OCEAN_TRACKING_SLAM_MUTEX_H
This class implements a high performance timer.
Definition HighPerformanceTimer.h:31
This class implements a recursive lock object.
Definition Lock.h:31
static MessageObject warning()
Returns the message for warning messages.
Definition Messenger.h:1090
This template class is the base class for all singleton objects.
Definition Singleton.h:71
static LockManager & get()
Returns a reference to the unique object.
Definition Singleton.h:115
This class implements a singleton manager for collecting and reporting lock timing measurements.
Definition Mutex.h:90
Map acquisitionDelayMap_
The map storing acquisition delay measurements for each lock.
Definition Mutex.h:157
static constexpr double holdDurationThresholdMs_
The threshold in milliseconds for hold duration logging, with range [0, infinity),...
Definition Mutex.h:102
std::vector< double > Measurements
Definition of a vector holding duration measurements in seconds.
Definition Mutex.h:114
void addHoldDurationMeasurement(const std::string &name, const bool isWriteLock, const double duration)
Adds a hold duration measurement for a specific lock.
void addAcquisitionDelayMeasurement(const std::string &name, const bool isWriteLock, const double duration)
Adds an acquisition delay measurement for a specific lock.
static constexpr bool isEnabled_
True, to enable lock timing measurements; False, to disable.
Definition Mutex.h:96
Map holdDurationMap_
The map storing hold duration measurements for each lock.
Definition Mutex.h:160
std::pair< bool, std::string > Key
Definition of a key combining a lock type (read/write) and a name.
Definition Mutex.h:109
std::string report(const bool skipZero=true)
Generates a report of all collected lock timing measurements.
std::unordered_map< Key, Measurements, PairHash > Map
Definition of a map mapping keys to measurements.
Definition Mutex.h:119
LockManager()
Creates a new lock manager.
static constexpr double acquisitionDelayThresholdMs_
The threshold in milliseconds for acquisition delay logging, with range [0, infinity),...
Definition Mutex.h:99
Lock lock_
The lock protecting the maps.
Definition Mutex.h:163
LockTimer()=default
Creates a new lock timer without a name.
void reportAcquisitionDelay(const bool)
No-op implementation of acquisition delay reporting.
Definition Mutex.h:239
LockTimer(const char *)
Creates a new lock timer with a name.
Definition Mutex.h:230
void reportHoldDuration(const bool)
No-op implementation of hold duration reporting.
Definition Mutex.h:248
This class implements a helper for timing lock acquisition and hold durations.
Definition Mutex.h:174
void reportHoldDuration(const bool isWriteLock)
Reports the hold duration to the LockManager.
Definition Mutex.h:417
HighPerformanceTimer timer_
The timer for measuring acquisition delay and hold duration.
Definition Mutex.h:208
void reportAcquisitionDelay(const bool isWriteLock)
Reports the acquisition delay to the LockManager.
Definition Mutex.h:386
LockTimer(const char *name)
Creates a new lock timer with a name.
Definition Mutex.h:379
const char * name_
The name of the lock, nullptr if not specified.
Definition Mutex.h:205
LockTimer()=default
Creates a new lock timer without a name.
This class implements a scoped read lock for a shared mutex.
Definition Mutex.h:261
static bool debugIsLocked(Mutex &mutex)
Returns whether a mutex is currently locked (debug only).
Definition Mutex.h:483
void unlock()
Explicitly releases the lock before the scoped object is destroyed.
Definition Mutex.h:470
std::shared_lock< Mutex > lock_
The underlying shared lock.
Definition Mutex.h:313
ReadLock(Mutex &mutex)
Creates a new read lock and acquires the mutex.
Definition Mutex.h:445
ReadLock(ReadLock &&)=default
Move constructor.
ReadLock & operator=(ReadLock &&)=default
Move assignment operator.
~ReadLock()
Destructs the read lock and releases the mutex.
Definition Mutex.h:459
This class implements a scoped write lock for a shared mutex.
Definition Mutex.h:323
void unlock()
Explicitly releases the lock before the scoped object is destroyed.
Definition Mutex.h:521
WriteLock(WriteLock &&)=default
Move constructor.
std::unique_lock< Mutex > lock_
The underlying unique lock.
Definition Mutex.h:375
WriteLock(Mutex &mutex)
Creates a new write lock and acquires the mutex.
Definition Mutex.h:496
WriteLock & operator=(WriteLock &&)=default
Move assignment operator.
~WriteLock()
Destructs the write lock and releases the mutex.
Definition Mutex.h:510
static bool debugIsLocked(Mutex &mutex)
Returns whether a mutex is currently locked (debug only).
Definition Mutex.h:537
std::shared_mutex Mutex
Definition of a mutex supporting read and write locks.
Definition Mutex.h:78
The namespace covering the entire Ocean framework.
Definition Accessor.h:15