Skip to content

Commit b7bd752

Browse files
committed
Simplify and optimize the structural type metadata caches to use ConcurrentMap directly.
Previously, these were all using MetadataCache. MetadataCache is a more heavyweight structure which acquires a lock before building the metadata. This is appropriate if building the metadata is very expensive or might have semantic side-effects which cannot be rolled back. It's also useful when there's a risk of re-entrance, since it can diagnose such things instead of simply dead-locking or infinitely recursing. However, it's necessary for structural cases like tuple and function types, and instead we can just use ConcurrentMap, which does a compare-and-swap to publish the constructed metadata and potentially destroys it if another thread successfully won the race. This is an optimization which we could not previously attempt. As part of this, fix tuple metadata uniquing to consider the label string correctly. This exposes a bug where the runtime demangling of tuple metadata nodes doesn't preserve labels; fix this as well.
1 parent f2080cb commit b7bd752

File tree

3 files changed

+545
-407
lines changed

3 files changed

+545
-407
lines changed

include/swift/Runtime/Concurrent.h

+37-8
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#include <stdio.h>
2020
#endif
2121

22+
namespace swift {
23+
2224
/// This is a node in a concurrent linked list.
2325
template <class ElemTy> struct ConcurrentListNode {
2426
ConcurrentListNode(ElemTy Elem) : Payload(Elem), Next(nullptr) {}
@@ -123,6 +125,29 @@ template <class ElemTy> struct ConcurrentList {
123125
std::atomic<ConcurrentListNode<ElemTy> *> First;
124126
};
125127

128+
template <class T, bool Delete> class AtomicMaybeOwningPointer;
129+
130+
template <class T>
131+
class AtomicMaybeOwningPointer<T, false> {
132+
public:
133+
std::atomic<T*> Value;
134+
constexpr AtomicMaybeOwningPointer(T *value) : Value(value) {}
135+
};
136+
137+
template <class T>
138+
class AtomicMaybeOwningPointer<T, true> {
139+
public:
140+
std::atomic<T*> Value;
141+
constexpr AtomicMaybeOwningPointer(T *value) : Value(value) {}
142+
143+
~AtomicMaybeOwningPointer() {
144+
// This can use relaxed memory order because the client has to ensure
145+
// that all accesses are safely completed and their effects fully
146+
// visible before destruction occurs anyway.
147+
::delete Value.load(std::memory_order_relaxed);
148+
}
149+
};
150+
126151
/// A concurrent map that is implemented using a binary tree. It supports
127152
/// concurrent insertions but does not support removals or rebalancing of
128153
/// the tree.
@@ -140,7 +165,8 @@ template <class ElemTy> struct ConcurrentList {
140165
/// /// where KeyTy is the type of the first argument to getOrInsert and
141166
/// /// ArgTys is the type of the remaining arguments.
142167
/// static size_t getExtraAllocationSize(KeyTy key, ArgTys...)
143-
template <class EntryTy> class ConcurrentMap {
168+
template <class EntryTy, bool ProvideDestructor = true>
169+
class ConcurrentMap {
144170
struct Node {
145171
std::atomic<Node*> Left;
146172
std::atomic<Node*> Right;
@@ -182,7 +208,7 @@ template <class EntryTy> class ConcurrentMap {
182208
};
183209

184210
/// The root of the tree.
185-
std::atomic<Node*> Root;
211+
AtomicMaybeOwningPointer<Node, ProvideDestructor> Root;
186212

187213
/// This member stores the address of the last node that was found by the
188214
/// search procedure. We cache the last search to accelerate code that
@@ -195,13 +221,14 @@ template <class EntryTy> class ConcurrentMap {
195221
ConcurrentMap(const ConcurrentMap &) = delete;
196222
ConcurrentMap &operator=(const ConcurrentMap &) = delete;
197223

198-
~ConcurrentMap() {
199-
::delete Root.load(std::memory_order_relaxed);
200-
}
224+
// ConcurrentMap<T, false> must have a trivial destructor.
225+
~ConcurrentMap() = default;
226+
227+
public:
201228

202229
#ifndef NDEBUG
203230
void dump() const {
204-
auto R = Root.load(std::memory_order_acquire);
231+
auto R = Root.Value.load(std::memory_order_acquire);
205232
printf("digraph g {\n"
206233
"graph [ rankdir = \"TB\"];\n"
207234
"node [ fontsize = \"16\" ];\n"
@@ -225,7 +252,7 @@ template <class EntryTy> class ConcurrentMap {
225252
}
226253

227254
// Search the tree, starting from the root.
228-
Node *node = Root.load(std::memory_order_acquire);
255+
Node *node = Root.Value.load(std::memory_order_acquire);
229256
while (node) {
230257
int comparisonResult = node->Payload.compareWithKey(key);
231258
if (comparisonResult == 0) {
@@ -258,7 +285,7 @@ template <class EntryTy> class ConcurrentMap {
258285
Node *newNode = nullptr;
259286

260287
// Start from the root.
261-
auto edge = &Root;
288+
auto edge = &Root.Value;
262289

263290
while (true) {
264291
// Load the edge.
@@ -313,4 +340,6 @@ template <class EntryTy> class ConcurrentMap {
313340
}
314341
};
315342

343+
} // end namespace swift
344+
316345
#endif // SWIFT_RUNTIME_CONCURRENTUTILS_H

stdlib/public/runtime/Demangle.cpp

+27-2
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,35 @@ Demangle::NodePointer swift::_swift_buildDemanglingForMetadata(const Metadata *t
209209
}
210210
case MetadataKind::Tuple: {
211211
auto tuple = static_cast<const TupleTypeMetadata *>(type);
212+
const char *labels = tuple->Labels;
212213
auto tupleNode = NodeFactory::create(Node::Kind::NonVariadicTuple);
213214
for (unsigned i = 0, e = tuple->NumElements; i < e; ++i) {
214-
auto elt = _swift_buildDemanglingForMetadata(tuple->getElement(i).Type);
215-
tupleNode->addChild(elt);
215+
auto elt = NodeFactory::create(Node::Kind::TupleElement);
216+
217+
// Add a label child if applicable:
218+
if (labels) {
219+
// Look for the next space in the labels string.
220+
if (const char *space = strchr(labels, ' ')) {
221+
// If there is one, and the label isn't empty, add a label child.
222+
if (labels != space) {
223+
auto eltName =
224+
NodeFactory::create(Node::Kind::TupleElementName,
225+
std::string(labels, space));
226+
elt->addChild(std::move(eltName));
227+
}
228+
229+
// Skip past the space.
230+
labels = space + 1;
231+
}
232+
}
233+
234+
// Add the element type child.
235+
auto eltType =
236+
_swift_buildDemanglingForMetadata(tuple->getElement(i).Type);
237+
elt->addChild(std::move(eltType));
238+
239+
// Add the completed element to the tuple.
240+
tupleNode->addChild(std::move(elt));
216241
}
217242
return tupleNode;
218243
}

0 commit comments

Comments
 (0)