Skip to content

Commit 8c9ffb1

Browse files
committed
Prevent the Swift scratch AST context to be reset while it is being used.
This patch introduces a Reader-Writer-Lock around the scratch context. Users of a scratch context acquire a read lock while they are depending on a scratch context. Only if no readers a around is Target allowed to reset a Swift scratch context and delete the old one. The Swift scratch context may need to be replaced when it gets corrupted, for example due to incompatible ClangImporter options. This locking mechanism guarantees that this won't happen while a client is using the context. In Swift there are three use-cases for ASTContexts with different requirements and guarantees. - Module ASTContexts are used for the static type system. They are created once for each lldb::Module and live forever. - Scratch AST Contexts are used for expressions (thus far everything is like in the Clang language support) - Scratch AST Contexts are also used to express the results of any dynamic type resolution done by RemoteAST or Archetype binding. Because expressions and dynamic type resolution may trigger the import of another module, the scratch context may become unusable. When a scratch context is in a fatal error state, GetScratchSwiftASTContext() will create a fresh global context, or even separate scratch contexts for each lldb::Module. But it will only do this if no client holds on to a read lock on \b m_scratch_typesystem_lock. rdar://problem/42399818 apple-llvm-split-commit: 8d2c620ea07618284b8da074ba49a22e2a7becda apple-llvm-split-dir: lldb/
1 parent b506a0d commit 8c9ffb1

16 files changed

+344
-143
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
//===-- SwiftASTContextReader.h ---------------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef liblldb_SwiftASTContextReader_h_
14+
#define liblldb_SwiftASTContextReader_h_
15+
16+
#include "llvm/Support/RWMutex.h"
17+
18+
namespace lldb_private {
19+
20+
class SwiftASTContext;
21+
class ExecutionContext;
22+
class ExecutionContextRef;
23+
24+
/// This is like llvm::sys::SmartRWMutex<false>, 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. This implementation still allows
28+
/// for a race condition in \c try_lock() between the checking of
29+
/// m_readers and the lock acquisition.
30+
class SharedMutex {
31+
llvm::sys::RWMutexImpl m_impl;
32+
unsigned m_readers = 0;
33+
public:
34+
SharedMutex() = default;
35+
SharedMutex(const SharedMutex &) = delete;
36+
SharedMutex &operator=(const SharedMutex &) = delete;
37+
bool lock_shared() { ++m_readers; return m_impl.reader_acquire(); }
38+
bool unlock_shared() { --m_readers; return m_impl.reader_release(); }
39+
bool try_lock() { return m_readers ? false : m_impl.writer_acquire(); }
40+
bool unlock() { return m_impl.writer_release(); }
41+
};
42+
43+
/// RAII acquisition of a reader lock.
44+
struct ScopedSharedMutexReader {
45+
SharedMutex *m_mutex;
46+
ScopedSharedMutexReader(const ScopedSharedMutexReader&) = default;
47+
explicit ScopedSharedMutexReader(SharedMutex *m) : m_mutex(m) {
48+
if (m_mutex)
49+
m_mutex->lock_shared();
50+
}
51+
52+
~ScopedSharedMutexReader() {
53+
if (m_mutex)
54+
m_mutex->unlock_shared();
55+
}
56+
};
57+
58+
/// A scratch Swift AST context pointer and its reader lock.
59+
/// The Swift scratch context may need to be replaced when it gets corrupted,
60+
/// for example due to incompatible ClangImporter options. This locking
61+
/// mechanism guarantees that this won't happen while a client is using the
62+
/// context.
63+
///
64+
/// In Swift there are three use-cases for ASTContexts with
65+
/// different requirements and guarantees.
66+
///
67+
/// - Module ASTContexts are used for the static type system. They
68+
/// are created once for each lldb::Module and live forever.
69+
///
70+
/// - Scratch AST Contexts are used for expressions
71+
/// (thus far everything is like in the Clang language support)
72+
///
73+
/// - Scratch AST Contexts are also used to express the results of
74+
/// any dynamic type resolution done by RemoteAST or Archetype
75+
/// binding.
76+
///
77+
/// Because expressions and dynamic type resolution may trigger the
78+
/// import of another module, the scratch context may become
79+
/// unusable. When a scratch context is in a fatal error state,
80+
/// GetScratchSwiftASTContext() will create a fresh global context,
81+
/// or even separate scratch contexts for each lldb::Module. But it
82+
/// will only do this if no client holds on to a read lock on \b
83+
/// m_scratch_typesystem_lock.
84+
struct SwiftASTContextReader : ScopedSharedMutexReader {
85+
SwiftASTContext *m_ptr = nullptr;
86+
SwiftASTContextReader() : ScopedSharedMutexReader(nullptr) {}
87+
SwiftASTContextReader(SharedMutex &mutex, SwiftASTContext *ctx)
88+
: ScopedSharedMutexReader(&mutex), m_ptr(ctx) {}
89+
SwiftASTContextReader(const SwiftASTContextReader &copy)
90+
: ScopedSharedMutexReader(copy.m_mutex), m_ptr(copy.m_ptr) {}
91+
SwiftASTContext *get() { return m_ptr; }
92+
operator bool() const { return m_ptr; }
93+
SwiftASTContext *operator->() { return m_ptr; }
94+
SwiftASTContext &operator*() { return *m_ptr; }
95+
};
96+
97+
/// An RAII object that just acquires the reader lock.
98+
struct SwiftASTContextLock : ScopedSharedMutexReader {
99+
SwiftASTContextLock(const ExecutionContextRef *exe_ctx_ref);
100+
SwiftASTContextLock(const ExecutionContext *exe_ctx);
101+
};
102+
103+
} // namespace lldb_private
104+
#endif

lldb/include/lldb/Core/ValueObject.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#ifndef liblldb_ValueObject_h_
1111
#define liblldb_ValueObject_h_
1212

13+
#include "lldb/Core/SwiftASTContextReader.h"
1314
#include "lldb/Core/Value.h"
1415
#include "lldb/Symbol/CompilerType.h"
1516
#include "lldb/Symbol/Type.h" // for TypeImpl
@@ -615,7 +616,7 @@ class ValueObject : public UserID {
615616
virtual bool HasSyntheticValue();
616617

617618
SwiftASTContext *GetSwiftASTContext();
618-
SwiftASTContext *GetScratchSwiftASTContext();
619+
SwiftASTContextReader GetScratchSwiftASTContext();
619620

620621
virtual bool IsSynthetic() { return false; }
621622

lldb/include/lldb/Core/ValueObjectDynamicValue.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ class ValueObjectDynamicValue : public ValueObject {
123123

124124
bool HasDynamicValueTypeInfo() override { return true; }
125125

126+
/// Determine whether the scratch AST context has been replaced
127+
/// since this dynamic type has been created.
128+
bool DynamicValueTypeInfoNeedsUpdate();
129+
126130
CompilerType GetCompilerTypeImpl() override;
127131

128132
Address m_address; ///< The variable that this value object is based upon

lldb/include/lldb/Target/Target.h

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ typedef enum LoadCWDlldbinitFile {
7171
eLoadCWDlldbinitWarn
7272
} LoadCWDlldbinitFile;
7373

74+
7475
//----------------------------------------------------------------------
7576
// TargetProperties
7677
//----------------------------------------------------------------------
@@ -1130,29 +1131,18 @@ class Target : public std::enable_shared_from_this<Target>,
11301131

11311132
lldb::ClangASTImporterSP GetClangASTImporter();
11321133

1133-
/// Convenience wrapper that extracts the scope's module first.
1134-
SwiftASTContext *GetScratchSwiftASTContext(Status &error,
1135-
ExecutionContextScope &exe_scope,
1136-
bool create_on_demand = true);
1137-
1138-
#ifdef __clang_analyzer__
1139-
// See GetScratchTypeSystemForLanguage()
1140-
SwiftASTContext *GetScratchSwiftASTContext(
1141-
Status &error, Module *lldb_module, bool create_on_demand = true,
1142-
const char *extra_options = nullptr) __attribute__((always_inline)) {
1143-
SwiftASTContext *ret = GetScratchSwiftASTContextImpl(
1144-
error, lldb_module, create_on_demand, extra_options);
1145-
1146-
return ret ? ret : nullptr;
1134+
/// Get the lock guarding the scratch typesystem from being re-initialized.
1135+
SharedMutex &GetSwiftScratchContextLock() {
1136+
return m_scratch_typesystem_lock;
11471137
}
11481138

1149-
SwiftASTContext *GetScratchSwiftASTContextImpl(Status &error,
1150-
Module *lldb_module,
1151-
bool create_on_demand = true);
1152-
#else
1153-
SwiftASTContext *GetScratchSwiftASTContext(Status &error, Module *lldb_module,
1154-
bool create_on_demand = true);
1155-
#endif
1139+
/// Convenience wrapper that extracts the scope's module first.
1140+
SwiftASTContextReader
1141+
GetScratchSwiftASTContext(Status &error, ExecutionContextScope &exe_scope,
1142+
bool create_on_demand = true);
1143+
SwiftASTContextReader
1144+
GetScratchSwiftASTContext(Status &error, Module *lldb_module,
1145+
bool create_on_demand = true);
11561146

11571147
private:
11581148
void DisplayFallbackSwiftContextErrors(SwiftASTContext *swift_ast_ctx);
@@ -1417,6 +1407,9 @@ class Target : public std::enable_shared_from_this<Target>,
14171407
llvm::DenseMap<ModuleLanguage, lldb::TypeSystemSP>
14181408
m_scratch_typesystem_for_module;
14191409

1410+
/// Guards the scratch typesystem from being re-initialized.
1411+
SharedMutex m_scratch_typesystem_lock;
1412+
14201413
static void ImageSearchPathsChanged(const PathMappingList &path_list,
14211414
void *baton);
14221415

lldb/source/Core/ValueObject.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,15 @@ bool ValueObject::UpdateValueIfNeeded(bool update_format) {
155155

156156
bool did_change_formats = false;
157157

158+
// Swift: Check whether the dynamic type system became stale.
159+
if (m_dynamic_value) {
160+
auto *dyn_val = static_cast<ValueObjectDynamicValue *>(m_dynamic_value);
161+
if (dyn_val->DynamicValueTypeInfoNeedsUpdate()) {
162+
dyn_val->SetNeedsUpdate();
163+
SetNeedsUpdate();
164+
}
165+
}
166+
158167
if (update_format)
159168
did_change_formats = UpdateFormatsIfNeeded();
160169

@@ -1742,13 +1751,13 @@ SwiftASTContext *ValueObject::GetSwiftASTContext() {
17421751
auto ts = module_sp->GetTypeSystemForLanguage(lldb::eLanguageTypeSwift);
17431752
return llvm::dyn_cast_or_null<SwiftASTContext>(ts);
17441753
}
1745-
return GetScratchSwiftASTContext();
1754+
return GetScratchSwiftASTContext().get();
17461755
}
17471756

1748-
SwiftASTContext *ValueObject::GetScratchSwiftASTContext() {
1757+
SwiftASTContextReader ValueObject::GetScratchSwiftASTContext() {
17491758
lldb::TargetSP target_sp(GetTargetSP());
17501759
if (!target_sp)
1751-
return nullptr;
1760+
return {};
17521761

17531762
Status error;
17541763
ExecutionContext ctx = GetExecutionContextRef().Lock(false);
@@ -2831,9 +2840,16 @@ void ValueObject::LogValueObject(Log *log,
28312840
}
28322841
}
28332842

2843+
static const ExecutionContextRef *GetSwiftExeCtx(ValueObject &valobj) {
2844+
return (valobj.GetPreferredDisplayLanguage() == eLanguageTypeSwift)
2845+
? &valobj.GetExecutionContextRef()
2846+
: nullptr;
2847+
}
2848+
28342849
void ValueObject::Dump(Stream &s) { Dump(s, DumpValueObjectOptions(*this)); }
28352850

28362851
void ValueObject::Dump(Stream &s, const DumpValueObjectOptions &options) {
2852+
auto swift_scratch_ctx_lock = SwiftASTContextLock(GetSwiftExeCtx(*this));
28372853
ValueObjectPrinter printer(this, &s, options);
28382854
printer.PrintValueObject();
28392855
}

lldb/source/Core/ValueObjectDynamicValue.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ bool ValueObjectDynamicValue::UpdateValue() {
145145
m_data.SetByteOrder(target->GetArchitecture().GetByteOrder());
146146
m_data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize());
147147
}
148+
149+
auto swift_scratch_ctx_lock = SwiftASTContextLock(&exe_ctx);
148150

149151
// First make sure our Type and/or Address haven't changed:
150152
Process *process = exe_ctx.GetProcessPtr();
@@ -409,3 +411,15 @@ void ValueObjectDynamicValue::SetLanguageFlags(uint64_t flags) {
409411
else
410412
this->ValueObject::SetLanguageFlags(flags);
411413
}
414+
415+
bool ValueObjectDynamicValue::DynamicValueTypeInfoNeedsUpdate() {
416+
if (GetPreferredDisplayLanguage() != eLanguageTypeSwift)
417+
return false;
418+
419+
if (!m_dynamic_type_info.HasType())
420+
return false;
421+
422+
auto *cached_ctx =
423+
static_cast<SwiftASTContext *>(m_value.GetCompilerType().GetTypeSystem());
424+
return cached_ctx == GetScratchSwiftASTContext().get();
425+
}

lldb/source/Expression/Materializer.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -922,10 +922,12 @@ class EntityResultVariable : public Materializer::Entity {
922922

923923
Status type_system_error;
924924
TypeSystem *type_system;
925-
925+
926926
if (lang == lldb::eLanguageTypeSwift)
927+
// We already acquired the lock in the UserExpression.
927928
type_system =
928-
target_sp->GetScratchSwiftASTContext(type_system_error, *frame_sp);
929+
target_sp->GetScratchSwiftASTContext(type_system_error, *frame_sp)
930+
.get();
929931
else
930932
type_system = target_sp->GetScratchTypeSystemForLanguage(
931933
&type_system_error, m_type.GetMinimumLanguage());

lldb/source/Expression/UserExpression.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,11 @@ lldb::ExpressionResults UserExpression::Evaluate(
261261
execution_results = lldb::eExpressionParseError;
262262
if (fixed_expression && !fixed_expression->empty() &&
263263
options.GetAutoApplyFixIts()) {
264+
// Swift: temporarily release scratch context lock held by
265+
// SwiftUserExpression, so the context can be restored if necessary.
266+
if (expr == *fixed_expression)
267+
user_expression_sp = nullptr;
268+
264269
lldb::UserExpressionSP fixed_expression_sp(
265270
target->GetUserExpressionForLanguage(exe_ctx,
266271
fixed_expression->c_str(),

0 commit comments

Comments
 (0)