Skip to content

Commit 14f2dca

Browse files
committed
Implement fast user-space mutexes on Windows using Wait/WakeOnAddress
1 parent 7ff6042 commit 14f2dca

7 files changed

+407
-16
lines changed

configure.cmd

+1-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ IF NOT DEFINED NTF_CONFIGURE_WITH_SPIN_LOCKS (
154154
)
155155

156156
IF NOT DEFINED NTF_CONFIGURE_WITH_USERSPACE_MUTEXES (
157-
set NTF_CONFIGURE_WITH_USERSPACE_MUTEXES=0
157+
set NTF_CONFIGURE_WITH_USERSPACE_MUTEXES=1
158158
)
159159

160160
IF NOT DEFINED NTF_CONFIGURE_WITH_SYSTEM_MUTEXES (

groups/ntc/ntccfg/ntccfg_inline.h

+20
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,24 @@ BSLS_IDENT("$Id: $")
6565
#error Not implemented
6666
#endif
6767

68+
#if defined(BSLS_PLATFORM_CMP_GNU) || defined(BSLS_PLATFORM_CMP_CLANG)
69+
70+
/// Hint that the following function should never be inlined at its call site.
71+
/// @ingroup module_ntccfg
72+
#define NTCCFG_INLINE_NEVER __attribute__((noinline))
73+
74+
#elif defined(BSLS_PLATFORM_CMP_MSVC)
75+
76+
/// Hint that the following function should never be inlined at its call site.
77+
/// @ingroup module_ntccfg
78+
#define NTCCFG_INLINE_NEVER __declspec(noinline)
79+
80+
#else
81+
82+
/// Hint that the following function should never be inlined at its call site.
83+
/// @ingroup module_ntccfg
84+
#define NTCCFG_INLINE_NEVER
85+
86+
#endif
87+
6888
#endif

groups/ntc/ntccfg/ntccfg_mutex.cpp

+85-7
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@
1818
#include <bsls_ident.h>
1919
BSLS_IDENT_RCSID(ntccfg_mutex_cpp, "$Id$ $CSID$")
2020

21-
#include <bslma_allocator.h>
22-
#include <bslma_default.h>
23-
#include <bsls_assert.h>
21+
#if NTCCFG_FUTEX_ENABLED
2422

2523
#if defined(BSLS_PLATFORM_OS_LINUX)
2624
#include <errno.h>
@@ -29,29 +27,61 @@ BSLS_IDENT_RCSID(ntccfg_mutex_cpp, "$Id$ $CSID$")
2927
#include <unistd.h>
3028
#endif
3129

30+
#if defined(BSLS_PLATFORM_OS_WINDOWS)
31+
#ifdef NTDDI_VERSION
32+
#undef NTDDI_VERSION
33+
#endif
34+
#ifdef WINVER
35+
#undef WINVER
36+
#endif
37+
#ifdef _WIN32_WINNT
38+
#undef _WIN32_WINNT
39+
#endif
40+
#define NTDDI_VERSION 0x06000100
41+
#define WINVER 0x0600
42+
#define _WIN32_WINNT 0x0600
43+
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
44+
#define _WINSOCK_DEPRECATED_NO_WARNINGS
45+
#endif
46+
#ifndef WIN32_LEAN_AND_MEAN
47+
#define WIN32_LEAN_AND_MEAN
48+
#endif
49+
// clang-format off
50+
#include <windows.h>
51+
// clang-format on
52+
#pragma comment(lib, "synchronization")
53+
#endif
54+
55+
#endif // NTCCFG_FUTEX_ENABLED
56+
3257
namespace BloombergLP {
3358
namespace ntccfg {
3459

3560
#if NTCCFG_FUTEX_ENABLED
3661

62+
#if defined(BSLS_PLATFORM_OS_LINUX)
63+
3764
// Some versions of GCC issue a spurious warning that the 'current' parameter
3865
// is set but not used when 'Futex::compareAndSwap' is called.
3966
#if defined(BSLS_PLATFORM_CMP_GNU)
4067
#pragma GCC diagnostic push
4168
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
4269
#endif
4370

44-
__attribute__((noinline)) void Futex::wait()
71+
NTCCFG_INLINE_NEVER
72+
void Futex::wait()
4573
{
4674
syscall(SYS_futex, (int*)(&d_value), FUTEX_WAIT, 2, 0, 0, 0);
4775
}
4876

49-
__attribute__((noinline)) void Futex::wake()
77+
NTCCFG_INLINE_NEVER
78+
void Futex::wake()
5079
{
5180
syscall(SYS_futex, (int*)(&d_value), FUTEX_WAKE, 1, 0, 0, 0);
5281
}
5382

54-
__attribute__((noinline)) void Futex::lockContention(int c)
83+
NTCCFG_INLINE_NEVER
84+
void Futex::lockContention(int c)
5585
{
5686
do {
5787
if (c == 2 || compareAndSwap(&d_value, 1, 2) != 0) {
@@ -60,7 +90,8 @@ __attribute__((noinline)) void Futex::lockContention(int c)
6090
} while ((c = compareAndSwap(&d_value, 0, 2)) != 0);
6191
}
6292

63-
__attribute__((noinline)) void Futex::unlockContention()
93+
NTCCFG_INLINE_NEVER
94+
void Futex::unlockContention()
6495
{
6596
__atomic_store_n(&d_value, 0, __ATOMIC_SEQ_CST);
6697
this->wake();
@@ -70,6 +101,53 @@ __attribute__((noinline)) void Futex::unlockContention()
70101
#pragma GCC diagnostic pop
71102
#endif
72103

104+
#elif defined(BSLS_PLATFORM_OS_WINDOWS)
105+
106+
NTCCFG_INLINE_NEVER
107+
void Futex::wait()
108+
{
109+
Value compare = 2;
110+
WaitOnAddress(&d_value, &compare, sizeof compare, INFINITE);
111+
}
112+
113+
NTCCFG_INLINE_NEVER
114+
void Futex::wake()
115+
{
116+
WakeByAddressSingle((Value*)(&d_value));
117+
}
118+
119+
NTCCFG_INLINE_NEVER
120+
void Futex::lockContention(Value c)
121+
{
122+
while (true) {
123+
if (c == 2) {
124+
this->wait();
125+
}
126+
else {
127+
const Value previous = compareAndSwap(&d_value, 1, 2);
128+
if (previous != 0) {
129+
this->wait();
130+
}
131+
}
132+
133+
c = compareAndSwap(&d_value, 0, 2);
134+
if (c == 0) {
135+
break;
136+
}
137+
}
138+
}
139+
140+
NTCCFG_INLINE_NEVER
141+
void Futex::unlockContention()
142+
{
143+
_InterlockedExchange(&d_value, 0);
144+
this->wake();
145+
}
146+
147+
#else
148+
#error Not implemented
149+
#endif
150+
73151
#endif // NTCCFG_FUTEX_ENABLED
74152

75153
} // close package namespace

groups/ntc/ntccfg/ntccfg_mutex.h

+107
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,23 @@ BSLS_IDENT("$Id: $")
3333
#if defined(BSLS_PLATFORM_OS_LINUX) && \
3434
(defined(BSLS_PLATFORM_CMP_GNU) || defined(BSLS_PLATFORM_CMP_CLANG))
3535
#define NTCCFG_FUTEX_ENABLED 1
36+
#elif defined(BSLS_PLATFORM_OS_WINDOWS) && defined(BSLS_PLATFORM_CMP_MSVC)
37+
#define NTCCFG_FUTEX_ENABLED 1
3638
#else
3739
#define NTCCFG_FUTEX_ENABLED 0
3840
#endif
3941

42+
#if NTCCFG_WUTEX_ENABLED
43+
#include <intrin.h>
44+
#endif
45+
4046
namespace BloombergLP {
4147
namespace ntccfg {
4248

4349
#if NTCCFG_FUTEX_ENABLED
4450

51+
#if defined(BSLS_PLATFORM_OS_LINUX)
52+
4553
// Some versions of GCC issue a spurious warning that the 'current' parameter
4654
// is set but not used when 'Futex::compareAndSwap' is called.
4755
#if defined(BSLS_PLATFORM_CMP_GNU)
@@ -148,6 +156,105 @@ void Futex::unlock()
148156
#pragma GCC diagnostic pop
149157
#endif
150158

159+
#elif defined(BSLS_PLATFORM_OS_WINDOWS)
160+
161+
/// @internal @brief
162+
/// Provide a synchronization primitive for mutually-exclusive access
163+
/// implemented by the Win32 WaitOnAddress function.
164+
///
165+
/// @par Thread Safety
166+
/// This class is thread safe.
167+
///
168+
/// @ingroup module_ntccfg
169+
__declspec(align(4)) class Futex
170+
{
171+
/// Define a type alias for the type of the synchronized value.
172+
typedef long Value;
173+
174+
/// The synchronized value.
175+
volatile Value d_value;
176+
177+
private:
178+
Futex(const Futex&) BSLS_KEYWORD_DELETED;
179+
Futex& operator=(const Futex&) BSLS_KEYWORD_DELETED;
180+
181+
private:
182+
/// Wait until the lock may be acquired.
183+
void wait();
184+
185+
/// Wake the next thread waiting to acquire the lock.
186+
void wake();
187+
188+
/// Continue locking the mutex after discovering the mutex was probably
189+
/// previously unlocked.
190+
void lockContention(Value c);
191+
192+
/// Continue unlocking the mutex after discovering the mutex probably
193+
/// has other threads trying to lock the mutex.
194+
void unlockContention();
195+
196+
/// Compare the specified '*current' value to the specified 'expected'
197+
/// value, and if equal, set '*current' to 'desired'. Return the
198+
/// previous value of 'current'.
199+
static Value compareAndSwap(volatile Value* current,
200+
Value expected,
201+
Value desired);
202+
203+
public:
204+
/// Create a new mutex.
205+
Futex();
206+
207+
/// Destroy this object.
208+
~Futex();
209+
210+
/// Lock the mutex.
211+
void lock();
212+
213+
/// Unlock the mutex.
214+
void unlock();
215+
};
216+
217+
NTCCFG_INLINE
218+
Futex::Value Futex::compareAndSwap(volatile Value* current,
219+
Value expected,
220+
Value desired)
221+
{
222+
return _InterlockedCompareExchange(current, desired, expected);
223+
}
224+
225+
NTCCFG_INLINE
226+
Futex::Futex()
227+
{
228+
_InterlockedExchange(&d_value, 0);
229+
}
230+
231+
NTCCFG_INLINE
232+
Futex::~Futex()
233+
{
234+
}
235+
236+
NTCCFG_INLINE
237+
void Futex::lock()
238+
{
239+
const Value previous = compareAndSwap(&d_value, 0, 1);
240+
if (previous != 0) {
241+
this->lockContention(previous);
242+
}
243+
}
244+
245+
NTCCFG_INLINE
246+
void Futex::unlock()
247+
{
248+
const Value previous = _InterlockedDecrement(&d_value) + 1;
249+
if (previous != 1) {
250+
this->unlockContention();
251+
}
252+
}
253+
254+
#else
255+
#error Not implemented
256+
#endif
257+
151258
#endif // NTCCFG_FUTEX_ENABLED
152259

153260
/// @internal @brief

0 commit comments

Comments
 (0)