HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
spinMutex.h
Go to the documentation of this file.
1 //
2 // Copyright 2023 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #ifndef PXR_BASE_TF_SPIN_MUTEX_H
25 #define PXR_BASE_TF_SPIN_MUTEX_H
26 
27 #include "pxr/pxr.h"
28 #include "pxr/base/tf/api.h"
29 
30 #include "pxr/base/arch/hints.h"
32 
33 #include <atomic>
34 
36 
37 /// \class TfSpinMutex
38 ///
39 /// This class implements a simple spin lock that emphasizes throughput when
40 /// there is little to no contention. Like all spin locks, any contention
41 /// performs poorly; consider a different algorithm design or synchronization
42 /// strategy in that case.
43 ///
44 /// This class provides a nested TfSpinMutex::ScopedLock that makes it easy to
45 /// acquire locks and have those locks automatically release when the ScopedLock
46 /// is destroyed.
47 ///
48 /// TfSpinMutex is observed to compile to the same instruction sequence as
49 /// tbb::spin_mutex on x86-64 for uncontended lock/unlock. The main difference
50 /// between TfSpinMutex and tbb:spin_mutex is that, for contended lock
51 /// operations, TfSpinMutex calls an out-of-line function to handle spinning &
52 /// backoff, while the tbb::spin_mutex inlines that code. This translates to 4
53 /// instructions inlined to take a TfSpinMutex lock, compared to 28 instructions
54 /// inlined for tbb:spin_mutex at the time of this writing. Correspondingly
55 /// tbb::spin_mutex offers ~2% better throughput under high contention. But
56 /// again, avoid spin locks if you have contention.
57 ///
59 {
60 public:
61 
62  /// Construct a mutex, initially unlocked.
63  TfSpinMutex() : _lockState(false) {}
64 
65  /// Scoped lock utility class. API modeled roughly after
66  /// tbb::spin_rw_mutex::scoped_lock.
67  struct ScopedLock {
68 
69  /// Construct a scoped lock for mutex \p m and acquire a lock.
70  explicit ScopedLock(TfSpinMutex &m)
71  : _mutex(&m)
72  , _acquired(false) {
73  Acquire();
74  }
75 
76  /// Construct a scoped lock not associated with a \p mutex.
77  ScopedLock() : _mutex(nullptr), _acquired(false) {}
78 
79  /// If this scoped lock is acquired, Release() it.
81  Release();
82  }
83 
84  /// If the current scoped lock is acquired, Release() it, then associate
85  /// this lock with \p m and acquire a lock.
86  void Acquire(TfSpinMutex &m) {
87  Release();
88  _mutex = &m;
89  Acquire();
90  }
91 
92  /// Release the currently required lock on the associated mutex. If
93  /// this lock is not currently acquired, silently do nothing.
94  void Release() {
95  if (_acquired) {
96  _Release();
97  }
98  }
99 
100  /// Acquire a lock on this lock's associated mutex. This lock must not
101  /// already be acquired when calling \p Acquire().
102  void Acquire() {
103  TF_DEV_AXIOM(!_acquired);
104  _mutex->Acquire();
105  _acquired = true;
106  }
107 
108  private:
109 
110  void _Release() {
111  TF_DEV_AXIOM(_acquired);
112  _mutex->Release();
113  _acquired = false;
114  }
115 
116  TfSpinMutex *_mutex;
117  bool _acquired;
118  };
119 
120  /// Acquire a lock on this mutex if it is not currently held by another
121  /// thread. Return true if the lock was acquired, or false if it was not
122  /// because another thread held the lock. This thread must not already hold
123  /// a lock on this mutex.
124  inline bool TryAcquire() {
125  return _lockState.exchange(true, std::memory_order_acquire) == false;
126  }
127 
128  /// Acquire a lock on this mutex. If another thread holds a lock on this
129  /// mutex, wait until it is released and this thread successfully acquires
130  /// it. This thread must not already hold a lock on this mutex.
131  void Acquire() {
132  if (ARCH_LIKELY(TryAcquire())) {
133  return;
134  }
135  _AcquireContended();
136  }
137 
138  /// Release this thread's lock on this mutex.
139  inline void Release() {
140  _lockState.store(false, std::memory_order_release);
141  }
142 
143 private:
144 
145  TF_API void _AcquireContended();
146 
147  std::atomic<bool> _lockState;
148 };
149 
151 
152 #endif // PXR_BASE_TF_SPIN_MUTEX_H
#define ARCH_LIKELY(x)
Definition: hints.h:46
void Release()
Release this thread's lock on this mutex.
Definition: spinMutex.h:139
#define TF_API
Definition: api.h:40
ScopedLock()
Construct a scoped lock not associated with a mutex.
Definition: spinMutex.h:77
~ScopedLock()
If this scoped lock is acquired, Release() it.
Definition: spinMutex.h:80
#define TF_DEV_AXIOM(cond)
void Acquire(TfSpinMutex &m)
Definition: spinMutex.h:86
bool TryAcquire()
Definition: spinMutex.h:124
void Acquire()
Definition: spinMutex.h:131
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1432
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:91
TfSpinMutex()
Construct a mutex, initially unlocked.
Definition: spinMutex.h:63
ScopedLock(TfSpinMutex &m)
Construct a scoped lock for mutex m and acquire a lock.
Definition: spinMutex.h:70