Skip to content

Commit 82509cb

Browse files
committed
Merge pull request #1731 from shawnce/SR-946
SR-946 - Unify mutexes in the Swift runtime
2 parents 56e3875 + aeceb2c commit 82509cb

File tree

12 files changed

+814
-45
lines changed

12 files changed

+814
-45
lines changed

include/swift/Runtime/Mutex.h

+306
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
//===--- Mutex.h - Lockables ------------------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2016 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Mutex, Condition, and Scoped lock abstactions for use in Swift runtime.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef SWIFT_RUNTIME_MUTEX_H
18+
#define SWIFT_RUNTIME_MUTEX_H
19+
20+
#if (defined(__APPLE__) || defined(__linux__) || defined(__CYGWIN__))
21+
#define SWIFT_RUNTIME_MUTEX_HAVE_PHTREADS
22+
#else
23+
#error "Must implement the following if your platform doesn't support phtreads."
24+
#endif
25+
26+
#ifdef SWIFT_RUNTIME_MUTEX_HAVE_PHTREADS
27+
#include <pthread.h>
28+
#endif
29+
30+
namespace swift {
31+
32+
/// A Condition that works with Mutex to allow – as an example – multi-threaded
33+
/// producers and consumers to signal each other in a safe way.
34+
class Condition {
35+
friend class MutexImpl;
36+
37+
public:
38+
explicit Condition();
39+
~Condition();
40+
41+
Condition(const Condition &) = delete;
42+
Condition &operator=(const Condition &) = delete;
43+
Condition(Condition &&) = delete;
44+
Condition &operator=(Condition &&) = delete;
45+
46+
public:
47+
/// Notifies one waiter (if any exists) that the condition has been met.
48+
///
49+
/// Note: To avoid missed notification it is best hold the related mutex
50+
// lock when calling notifyOne.
51+
void notifyOne();
52+
53+
/// Notifies all waiters (if any exists) that the condition has been met.
54+
///
55+
/// Note: To avoid missed notification it is best hold the related mutex
56+
// lock when calling notifyAll.
57+
void notifyAll();
58+
59+
private:
60+
#ifdef SWIFT_RUNTIME_MUTEX_HAVE_PHTREADS
61+
pthread_cond_t PThreadCond;
62+
#endif
63+
};
64+
65+
/// Internal (private) implementation of mutex functionality.
66+
/// Use Mutex instead (see below).
67+
class MutexImpl {
68+
friend class Mutex;
69+
70+
public:
71+
MutexImpl() = delete;
72+
~MutexImpl();
73+
74+
MutexImpl(const MutexImpl &) = delete;
75+
MutexImpl &operator=(const MutexImpl &) = delete;
76+
MutexImpl(MutexImpl &&) = delete;
77+
MutexImpl &operator=(MutexImpl &&) = delete;
78+
79+
public:
80+
void lock();
81+
void unlock();
82+
bool try_lock();
83+
void wait(Condition &condition);
84+
85+
private:
86+
explicit MutexImpl(bool checked);
87+
88+
#ifdef SWIFT_RUNTIME_MUTEX_HAVE_PHTREADS
89+
pthread_mutex_t PThreadMutex;
90+
#endif
91+
};
92+
93+
/// Internal (private) implementation of scoped locking functionality.
94+
/// Use ScopedLock instead (see below).
95+
class ScopedLockImpl {
96+
friend class Mutex;
97+
friend class ScopedLock;
98+
99+
public:
100+
ScopedLockImpl() = delete;
101+
~ScopedLockImpl() { Impl.unlock(); }
102+
103+
ScopedLockImpl(const ScopedLockImpl &) = delete;
104+
ScopedLockImpl &operator=(const ScopedLockImpl &) = delete;
105+
ScopedLockImpl(ScopedLockImpl &&) = delete;
106+
ScopedLockImpl &operator=(ScopedLockImpl &&) = delete;
107+
108+
private:
109+
ScopedLockImpl(MutexImpl &impl) : Impl(impl) { Impl.lock(); }
110+
MutexImpl &Impl;
111+
};
112+
113+
/// Internal (private) implementation of scoped unlocking functionality.
114+
/// Use ScopedUnlock instead (see below).
115+
class ScopedUnlockImpl {
116+
friend class Mutex;
117+
friend class ScopedUnlock;
118+
119+
public:
120+
ScopedUnlockImpl() = delete;
121+
~ScopedUnlockImpl() { Impl.lock(); }
122+
123+
ScopedUnlockImpl(const ScopedUnlockImpl &) = delete;
124+
ScopedUnlockImpl &operator=(const ScopedUnlockImpl &) = delete;
125+
ScopedUnlockImpl(ScopedUnlockImpl &&) = delete;
126+
ScopedUnlockImpl &operator=(ScopedUnlockImpl &&) = delete;
127+
128+
private:
129+
ScopedUnlockImpl(MutexImpl &impl) : Impl(impl) { Impl.unlock(); }
130+
MutexImpl &Impl;
131+
};
132+
133+
/// A Mutex object that supports `BasicLockable` and `Lockable` C++ concepts.
134+
/// See http://en.cppreference.com/w/cpp/concept/BasicLockable
135+
/// See http://en.cppreference.com/w/cpp/concept/Lockable
136+
///
137+
/// This is NOT a recursive mutex.
138+
class Mutex {
139+
friend class ScopedLock;
140+
friend class ScopedUnlock;
141+
142+
Mutex(const Mutex &) = delete;
143+
Mutex &operator=(const Mutex &) = delete;
144+
Mutex(Mutex &&) = delete;
145+
Mutex &operator=(Mutex &&) = delete;
146+
147+
public:
148+
/// Constructs a non-recursive mutex.
149+
///
150+
/// If `checked` is true the mutex will attempt to check for misuse and
151+
/// fatalError when detected. If `checked` is false (the default) the
152+
/// mutex will make little to no effort to check for misuse (e.g. efficient).
153+
explicit Mutex(bool checked = false) : Impl(checked) {}
154+
155+
public:
156+
/// The method lock() has the following properties:
157+
/// - Behaves as an atomic operation.
158+
/// - Blocks the calling thread until exclusive ownership of the mutex
159+
/// can be obtained.
160+
/// - Prior m.unlock() operations on the same mutex synchronize-with
161+
/// this lock operation.
162+
/// - The behavior is undefined if the calling thread already owns
163+
/// the mutex (likely a deadlock).
164+
/// - Does not throw exceptions but will halt on error (fatalError).
165+
void lock() { Impl.lock(); }
166+
167+
/// The method unlock() has the following properties:
168+
/// - Behaves as an atomic operation.
169+
/// - Releases the calling thread's ownership of the mutex and
170+
/// synchronizes-with the subsequent successful lock operations on
171+
/// the same object.
172+
/// - The behavior is undefined if the calling thread does not own
173+
/// the mutex.
174+
/// - Does not throw exceptions but will halt on error (fatalError).
175+
void unlock() { Impl.unlock(); }
176+
177+
/// The method lock() has the following properties:
178+
/// - Behaves as an atomic operation.
179+
/// - Attempts to obtain exclusive ownership of the mutex for the calling
180+
/// thread without blocking. If ownership is not obtained, returns
181+
/// immediately. The function is allowed to spuriously fail and return
182+
/// even if the mutex is not currently owned by another thread.
183+
/// - If try_lock() succeeds, prior unlock() operations on the same object
184+
/// synchronize-with this operation. lock() does not synchronize with a
185+
/// failed try_lock()
186+
/// - The behavior is undefined if the calling thread already owns
187+
/// the mutex (likely a deadlock)?
188+
/// - Does not throw exceptions but will halt on error (fatalError).
189+
bool try_lock() { return Impl.try_lock(); }
190+
191+
public:
192+
/// Releases lock, waits on supplied condition, and relocks before returning.
193+
///
194+
/// Precondition: Mutex locked by this thread, undefined otherwise.
195+
void wait(Condition &condition) { Impl.wait(condition); }
196+
197+
public:
198+
/// Acquires lock before calling the supplied critical section and release
199+
/// lock on return from critical section.
200+
///
201+
/// For example the following mutates value while holding the mutex lock.
202+
///
203+
/// mutex.lock([&value] { value++; });
204+
///
205+
/// Precondition: Mutex unlocked by this thread, undefined otherwise.
206+
template <typename CriticalSection>
207+
void lock(CriticalSection criticalSection) {
208+
ScopedLockImpl guard(Impl);
209+
criticalSection();
210+
}
211+
212+
/// Acquires lock before calling the supplied critical section. If critical
213+
/// section returns `true` then it will wait on the supplied condition and
214+
/// call critical section again when wait returns (again holding the lock).
215+
/// If critical section returns `false` it will no longer wait, it will
216+
/// release the lock and return (e.g. lockOrWait returns).
217+
///
218+
/// For example the following will loop waiting on condition until
219+
/// `value > 0`. It will then "consume" that value and stop looping.
220+
/// ...all while being correctly protected by mutex.
221+
///
222+
/// mutex.lockOrWait(condition, [&value] {
223+
/// if (value > 0) {
224+
/// value--;
225+
/// return false;
226+
/// }
227+
/// return true;
228+
/// });
229+
///
230+
/// Precondition: Mutex unlocked by this thread, undefined otherwise.
231+
template <typename CriticalSection>
232+
void lockOrWait(Condition &condition, CriticalSection criticalSection) {
233+
ScopedLockImpl guard(Impl);
234+
while (criticalSection()) {
235+
Impl.wait(condition);
236+
}
237+
}
238+
239+
/// Acquires lock before calling the supplied critical section and on return
240+
/// from critical section it notifies one waiter of supplied condition and
241+
/// then releases the lock.
242+
///
243+
/// For example the following mutates value while holding the mutex lock and
244+
/// then notifies one condition waiter about this change.
245+
///
246+
/// mutex.lockAndNotifyOne([&value] { value++; });
247+
///
248+
/// Precondition: Mutex unlocked by this thread, undefined otherwise.
249+
template <typename CriticalSection>
250+
void lockAndNotifyOne(Condition &condition, CriticalSection criticalSection) {
251+
ScopedLockImpl guard(Impl);
252+
criticalSection();
253+
condition.notifyOne();
254+
}
255+
256+
/// Acquires lock before calling the supplied critical section and on return
257+
/// from critical section it notifies all waiters of supplied condition and
258+
/// then releases the lock.
259+
///
260+
/// For example the following mutates value while holding the mutex lock and
261+
/// then notifies all condition waiters about this change.
262+
///
263+
/// mutex.lockAndNotifyAll([&value] { value++; });
264+
///
265+
/// Precondition: Mutex unlocked by this thread, undefined otherwise.
266+
template <typename CriticalSection>
267+
void lockAndNotifyAll(Condition &condition, CriticalSection criticalSection) {
268+
ScopedLockImpl guard(Impl);
269+
criticalSection();
270+
condition.notifyAll();
271+
}
272+
273+
private:
274+
MutexImpl Impl;
275+
};
276+
277+
/// A stack based object that locks the supplied mutex on construction
278+
/// and unlock it on destruction.
279+
///
280+
/// Precondition: Mutex unlocked by this thread, undefined otherwise.
281+
class ScopedLock : ScopedLockImpl {
282+
public:
283+
explicit ScopedLock(Mutex &mutex) : ScopedLockImpl(mutex.Impl) {}
284+
285+
ScopedLock(const ScopedLock &) = delete;
286+
ScopedLock &operator=(const ScopedLock &) = delete;
287+
ScopedLock(ScopedLock &&) = delete;
288+
ScopedLock &operator=(ScopedLock &&) = delete;
289+
};
290+
291+
/// A stack based object that unlocks the supplied mutex on construction
292+
/// and relocks it on destruction.
293+
///
294+
/// Precondition: Mutex locked by this thread, undefined otherwise.
295+
class ScopedUnlock : ScopedUnlockImpl {
296+
public:
297+
explicit ScopedUnlock(Mutex &mutex) : ScopedUnlockImpl(mutex.Impl) {}
298+
299+
ScopedUnlock(const ScopedUnlock &) = delete;
300+
ScopedUnlock &operator=(const ScopedUnlock &) = delete;
301+
ScopedUnlock(ScopedUnlock &&) = delete;
302+
ScopedUnlock &operator=(ScopedUnlock &&) = delete;
303+
};
304+
}
305+
306+
#endif

stdlib/public/runtime/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ set(swift_runtime_sources
4545
KnownMetadata.cpp
4646
Metadata.cpp
4747
MetadataLookup.cpp
48+
Mutex.cpp
4849
Once.cpp
4950
ProtocolConformance.cpp
5051
Reflection.cpp

stdlib/public/runtime/Casting.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,12 @@
3232
#include "stddef.h"
3333

3434
#include <cstring>
35-
#include <mutex>
3635
#include <type_traits>
3736

37+
// FIXME: SR-946 - we ideally want to switch off of using pthread_rwlock
38+
// directly and instead expand Mutex.h to support rwlocks.
39+
#include <mutex>
40+
3841
// FIXME: Clang defines max_align_t in stddef.h since 3.6.
3942
// Remove this hack when we don't care about older Clangs on all platforms.
4043
#ifdef __APPLE__

stdlib/public/runtime/Errors.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ struct crashreporter_annotations_t gCRAnnotations
165165
static void
166166
reportOnCrash(uint32_t flags, const char *message)
167167
{
168+
// FIXME: SR-946 - we can't yet switch the following to use swift::Mutex
169+
// since swift::Mutex uses fatalError and we could end
170+
// up back here again ...and again ...and again
168171
static pthread_mutex_t crashlogLock = PTHREAD_MUTEX_INITIALIZER;
169172
pthread_mutex_lock(&crashlogLock);
170173

stdlib/public/runtime/Metadata.cpp

+5-8
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@
2121
#include "swift/Basic/Lazy.h"
2222
#include "swift/Runtime/HeapObject.h"
2323
#include "swift/Runtime/Metadata.h"
24+
#include "swift/Runtime/Mutex.h"
2425
#include "swift/Strings.h"
2526
#include "MetadataCache.h"
2627
#include <algorithm>
2728
#include <condition_variable>
2829
#include <new>
2930
#include <cctype>
3031
#include <sys/mman.h>
31-
#include <pthread.h>
3232
#include <unistd.h>
3333
#include "llvm/ADT/DenseMap.h"
3434
#include "llvm/ADT/Hashing.h"
@@ -2380,12 +2380,8 @@ struct llvm::DenseMapInfo<GlobalString> {
23802380
// StringMap because we don't need to actually copy the string.
23812381
namespace {
23822382
struct ForeignTypeState {
2383-
pthread_mutex_t Lock;
2383+
Mutex Lock;
23842384
llvm::DenseMap<GlobalString, const ForeignTypeMetadata *> Types;
2385-
2386-
ForeignTypeState() {
2387-
pthread_mutex_init(&Lock, nullptr);
2388-
}
23892385
};
23902386
}
23912387

@@ -2400,7 +2396,8 @@ swift::swift_getForeignTypeMetadata(ForeignTypeMetadata *nonUnique) {
24002396

24012397
// Okay, insert a new row.
24022398
auto &Foreign = ForeignTypes.get();
2403-
pthread_mutex_lock(&Foreign.Lock);
2399+
ScopedLock guard(Foreign.Lock);
2400+
24042401
auto insertResult = Foreign.Types.insert({GlobalString(nonUnique->getName()),
24052402
nonUnique});
24062403
auto uniqueMetadata = insertResult.first->second;
@@ -2418,7 +2415,7 @@ swift::swift_getForeignTypeMetadata(ForeignTypeMetadata *nonUnique) {
24182415
// it will be possible for code to fast-path through this function
24192416
// too soon.
24202417
nonUnique->setCachedUniqueMetadata(uniqueMetadata);
2421-
pthread_mutex_unlock(&Foreign.Lock);
2418+
24222419
return uniqueMetadata;
24232420
}
24242421

0 commit comments

Comments
 (0)