Skip to content

Commit 78d6c42

Browse files
committed
Replace hand-rolled SharedMutex with C++17 std::shared_mutex (NFCI)
We are getting reports where LLDB is hanging in try_lock(). This patch makes the code easier to reason about by using standard primitives, and avoids the pitfall of the copy constructor that the old code was still allowing. While this may not fix the hangs, it should certainly make it easier to diagnose. rdar://112025620
1 parent af1feb8 commit 78d6c42

File tree

4 files changed

+42
-83
lines changed

4 files changed

+42
-83
lines changed

lldb/include/lldb/Core/SwiftScratchContextReader.h

Lines changed: 13 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -13,64 +13,14 @@
1313
#ifndef liblldb_SwiftASTContextReader_h_
1414
#define liblldb_SwiftASTContextReader_h_
1515

16-
#include "llvm/Support/RWMutex.h"
16+
#include <shared_mutex>
1717

1818
namespace lldb_private {
1919

2020
class TypeSystemSwiftTypeRefForExpressions;
2121
class ExecutionContext;
2222
class ExecutionContextRef;
2323

24-
/// This is like llvm::sys::SmartRWMutex<true>, but with a try_lock method.
25-
///
26-
/// FIXME: Replace this with a C++14 shared_timed_mutex or a C++17
27-
/// share_mutex as soon as possible.
28-
class SharedMutex {
29-
llvm::sys::SmartRWMutex<true> m_impl;
30-
unsigned m_readers = 0;
31-
std::mutex m_reader_mutex;
32-
public:
33-
SharedMutex() = default;
34-
SharedMutex(const SharedMutex &) = delete;
35-
SharedMutex &operator=(const SharedMutex &) = delete;
36-
bool lock_shared() {
37-
std::lock_guard<std::mutex> lock(m_reader_mutex);
38-
++m_readers;
39-
return m_impl.lock_shared();
40-
}
41-
bool unlock_shared() {
42-
std::lock_guard<std::mutex> lock(m_reader_mutex);
43-
assert(m_readers > 0);
44-
--m_readers;
45-
return m_impl.unlock_shared();
46-
}
47-
bool try_lock() {
48-
std::lock_guard<std::mutex> lock(m_reader_mutex);
49-
return m_readers ? false : m_impl.lock();
50-
}
51-
bool unlock() { return m_impl.unlock(); }
52-
};
53-
54-
/// RAII acquisition of a reader lock.
55-
struct ScopedSharedMutexReader {
56-
SharedMutex *m_mutex;
57-
explicit ScopedSharedMutexReader(SharedMutex *m) : m_mutex(m) {
58-
if (m_mutex)
59-
m_mutex->lock_shared();
60-
}
61-
62-
ScopedSharedMutexReader(const ScopedSharedMutexReader &copy)
63-
: m_mutex(copy.m_mutex) {
64-
if (m_mutex)
65-
m_mutex->lock_shared();
66-
}
67-
68-
~ScopedSharedMutexReader() {
69-
if (m_mutex)
70-
m_mutex->unlock_shared();
71-
}
72-
};
73-
7424
/// A scratch Swift context pointer and its reader lock.
7525
/// The Swift scratch context may need to be replaced when it gets corrupted,
7626
/// for example due to incompatible ClangImporter options. This locking
@@ -97,27 +47,25 @@ struct ScopedSharedMutexReader {
9747
/// or even separate scratch contexts for each lldb::Module. But it
9848
/// will only do this if no client holds on to a read lock on \b
9949
/// m_scratch_typesystem_lock.
100-
class SwiftScratchContextReader : ScopedSharedMutexReader {
101-
TypeSystemSwiftTypeRefForExpressions *m_ptr;
50+
class SwiftScratchContextReader {
51+
std::shared_lock<std::shared_mutex> m_lock;
52+
TypeSystemSwiftTypeRefForExpressions *m_ts;
10253

10354
public:
104-
SwiftScratchContextReader(SharedMutex &mutex,
105-
TypeSystemSwiftTypeRefForExpressions &ctx)
106-
: ScopedSharedMutexReader(&mutex), m_ptr(&ctx) {
107-
assert(m_ptr && "invalid context");
108-
}
109-
110-
TypeSystemSwiftTypeRefForExpressions *get() {
111-
assert(m_ptr && "invalid context");
112-
return m_ptr;
113-
}
114-
55+
SwiftScratchContextReader(std::shared_mutex &mutex,
56+
TypeSystemSwiftTypeRefForExpressions &ts);
57+
SwiftScratchContextReader(const SwiftScratchContextReader &) = delete;
58+
SwiftScratchContextReader(SwiftScratchContextReader &&other) = default;
59+
SwiftScratchContextReader &
60+
operator=(SwiftScratchContextReader &&other) = default;
61+
TypeSystemSwiftTypeRefForExpressions *get() { return m_ts; }
11562
TypeSystemSwiftTypeRefForExpressions *operator->() { return get(); }
11663
TypeSystemSwiftTypeRefForExpressions &operator*() { return *get(); }
11764
};
11865

11966
/// An RAII object that just acquires the reader lock.
120-
struct SwiftScratchContextLock : ScopedSharedMutexReader {
67+
struct SwiftScratchContextLock {
68+
std::shared_lock<std::shared_mutex> lock;
12169
SwiftScratchContextLock(const ExecutionContextRef *exe_ctx_ref);
12270
SwiftScratchContextLock(const ExecutionContext *exe_ctx);
12371
};

lldb/include/lldb/Target/Target.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,7 +1257,7 @@ class Target : public std::enable_shared_from_this<Target>,
12571257

12581258
#ifdef LLDB_ENABLE_SWIFT
12591259
/// Get the lock guarding the scratch typesystem from being re-initialized.
1260-
SharedMutex &GetSwiftScratchContextLock() {
1260+
std::shared_mutex &GetSwiftScratchContextLock() {
12611261
return m_scratch_typesystem_lock;
12621262
}
12631263

@@ -1694,7 +1694,7 @@ class Target : public std::enable_shared_from_this<Target>,
16941694
m_scratch_typesystem_for_module;
16951695

16961696
/// Guards the scratch typesystem from being re-initialized.
1697-
SharedMutex m_scratch_typesystem_lock;
1697+
std::shared_mutex m_scratch_typesystem_lock;
16981698

16991699
static void ImageSearchPathsChanged(const PathMappingList &path_list,
17001700
void *baton);

lldb/source/Core/ValueObjectDynamicValue.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -450,8 +450,8 @@ bool ValueObjectDynamicValue::DynamicValueTypeInfoNeedsUpdate() {
450450

451451
#ifdef LLDB_ENABLE_SWIFT
452452
auto cached_ctx = m_value.GetCompilerType().GetTypeSystem();
453-
llvm::Optional<SwiftScratchContextReader> scratch_ctx =
454-
GetSwiftScratchContext();
453+
llvm::Optional<SwiftScratchContextReader> scratch_ctx(
454+
GetSwiftScratchContext());
455455

456456
if (!scratch_ctx || !cached_ctx)
457457
return true;

lldb/source/Target/Target.cpp

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2426,12 +2426,12 @@ Target::GetScratchTypeSystemForLanguage(lldb::LanguageType language,
24262426
if (swift_ast_ctx && (swift_ast_ctx->CheckProcessChanged() ||
24272427
swift_ast_ctx->HasFatalErrors())) {
24282428
// If it is safe to replace the scratch context, do so. If
2429-
// try_lock() fails, then higher stack frame (or another
2429+
// try_lock() fails, then a higher stack frame (or another
24302430
// thread) is holding a read lock to the scratch context and
24312431
// replacing it could cause a use-after-free later on.
2432-
if (GetSwiftScratchContextLock().try_lock()) {
2433-
auto unlock = llvm::make_scope_exit(
2434-
[this] { GetSwiftScratchContextLock().unlock(); });
2432+
auto &lock = GetSwiftScratchContextLock();
2433+
if (lock.try_lock()) {
2434+
auto unlock = llvm::make_scope_exit([&lock] { lock.unlock(); });
24352435
if (m_use_scratch_typesystem_per_module)
24362436
DisplayFallbackSwiftContextErrors(swift_ast_ctx);
24372437
else if (StreamSP errs = GetDebugger().GetAsyncErrorStream()) {
@@ -2758,14 +2758,13 @@ llvm::Optional<SwiftScratchContextReader> Target::GetSwiftScratchContext(
27582758
return nullptr;
27592759
}
27602760

2761-
if (!GetSwiftScratchContextLock().try_lock()) {
2761+
auto &lock = GetSwiftScratchContextLock();
2762+
if (!lock.try_lock()) {
27622763
if (log)
27632764
log->Printf("couldn't acquire scratch context lock\n");
27642765
return nullptr;
27652766
}
2766-
2767-
auto unlock = llvm::make_scope_exit(
2768-
[this] { GetSwiftScratchContextLock().unlock(); });
2767+
auto unlock = llvm::make_scope_exit([&lock] { lock.unlock(); });
27692768

27702769
// With the lock held, get the current scratch type system. This ensures the
27712770
// current instance is used even in the unlikely event it was changed during
@@ -2824,7 +2823,7 @@ llvm::Optional<SwiftScratchContextReader> Target::GetSwiftScratchContext(
28242823
*swift_scratch_ctx);
28252824
}
28262825

2827-
static SharedMutex *
2826+
static std::shared_mutex *
28282827
GetSwiftScratchContextMutex(const ExecutionContext *exe_ctx) {
28292828
if (!exe_ctx)
28302829
return nullptr;
@@ -2834,11 +2833,19 @@ GetSwiftScratchContextMutex(const ExecutionContext *exe_ctx) {
28342833
return nullptr;
28352834
}
28362835

2836+
SwiftScratchContextReader::SwiftScratchContextReader(
2837+
std::shared_mutex &mutex, TypeSystemSwiftTypeRefForExpressions &ts)
2838+
: m_lock(mutex), m_ts(&ts) {}
2839+
28372840
SwiftScratchContextLock::SwiftScratchContextLock(
2838-
const ExecutionContext *exe_ctx)
2839-
: ScopedSharedMutexReader(GetSwiftScratchContextMutex(exe_ctx)) {}
2841+
const ExecutionContext *exe_ctx) {
2842+
if (auto *mutex = GetSwiftScratchContextMutex(exe_ctx)) {
2843+
std::shared_lock<std::shared_mutex> tmp(*mutex);
2844+
lock.swap(tmp);
2845+
}
2846+
}
28402847

2841-
static SharedMutex *
2848+
static std::shared_mutex *
28422849
GetSwiftScratchContextMutex(const ExecutionContextRef *exe_ctx_ref) {
28432850
if (!exe_ctx_ref)
28442851
return nullptr;
@@ -2847,8 +2854,12 @@ GetSwiftScratchContextMutex(const ExecutionContextRef *exe_ctx_ref) {
28472854
}
28482855

28492856
SwiftScratchContextLock::SwiftScratchContextLock(
2850-
const ExecutionContextRef *exe_ctx_ref)
2851-
: ScopedSharedMutexReader(GetSwiftScratchContextMutex(exe_ctx_ref)) {}
2857+
const ExecutionContextRef *exe_ctx_ref) {
2858+
if (auto *mutex = GetSwiftScratchContextMutex(exe_ctx_ref)) {
2859+
std::shared_lock<std::shared_mutex> tmp(*mutex);
2860+
lock.swap(tmp);
2861+
}
2862+
}
28522863

28532864
void Target::DisplayFallbackSwiftContextErrors(
28542865
SwiftASTContextForExpressions *swift_ast_ctx) {

0 commit comments

Comments
 (0)