Skip to content

Commit 51d937c

Browse files
authored
Merge pull request swiftlang#61276 from mikeash/fix-concurrent-type-by-name-again
[Runtime] Fix concurrent lookups of types by name (again).
2 parents 1d66c33 + e15783e commit 51d937c

File tree

2 files changed

+1060
-44
lines changed

2 files changed

+1060
-44
lines changed

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 53 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -283,18 +283,6 @@ struct MaybeIncompleteSuperclassIterator {
283283
}
284284
};
285285

286-
/// Return a range that will iterate over the given metadata and all its
287-
/// superclasses in order. If the metadata is not a class, iteration will
288-
/// provide that metadata and then stop.
289-
iterator_range<MaybeIncompleteSuperclassIterator>
290-
iterateMaybeIncompleteSuperclasses(const Metadata *metadata,
291-
bool instantiateSuperclassMetadata) {
292-
return iterator_range<MaybeIncompleteSuperclassIterator>(
293-
MaybeIncompleteSuperclassIterator(metadata,
294-
instantiateSuperclassMetadata),
295-
MaybeIncompleteSuperclassIterator(nullptr, false));
296-
}
297-
298286
/// Take the type reference inside a protocol conformance record and fetch the
299287
/// canonical metadata pointer for the type it refers to.
300288
/// Returns nil for universal or generic type references.
@@ -635,8 +623,9 @@ searchInConformanceCache(const Metadata *type,
635623
auto origType = type;
636624
auto snapshot = C.Cache.snapshot();
637625

638-
for (auto type : iterateMaybeIncompleteSuperclasses(
639-
type, instantiateSuperclassMetadata)) {
626+
MaybeIncompleteSuperclassIterator superclassIterator{
627+
type, instantiateSuperclassMetadata};
628+
for (; auto type = superclassIterator.metadata; ++superclassIterator) {
640629
if (auto *Value = snapshot.find(ConformanceCacheKey(type, protocol))) {
641630
return {type == origType, Value->getWitnessTable()};
642631
}
@@ -723,16 +712,20 @@ namespace {
723712

724713
/// Retrieve the type that matches the conformance candidate, which may
725714
/// be a superclass of the given type. Returns null if this type does not
726-
/// match this conformance.
727-
const Metadata *getMatchingType(const Metadata *conformingType,
728-
bool instantiateSuperclassMetadata) const {
729-
for (auto conformingType : iterateMaybeIncompleteSuperclasses(
730-
conformingType, instantiateSuperclassMetadata)) {
715+
/// match this conformance, along with the final metadata state of the
716+
/// superclass iterator.
717+
std::pair<const Metadata *, llvm::Optional<MetadataState>>
718+
getMatchingType(const Metadata *conformingType,
719+
bool instantiateSuperclassMetadata) const {
720+
MaybeIncompleteSuperclassIterator superclassIterator{
721+
conformingType, instantiateSuperclassMetadata};
722+
for (; auto conformingType = superclassIterator.metadata;
723+
++superclassIterator) {
731724
if (matches(conformingType))
732-
return conformingType;
725+
return {conformingType, llvm::None};
733726
}
734727

735-
return nullptr;
728+
return {nullptr, superclassIterator.state};
736729
}
737730
};
738731
}
@@ -755,7 +748,8 @@ static void validateDyldResults(
755748
continue;
756749

757750
ConformanceCandidate candidate(descriptor);
758-
if (candidate.getMatchingType(type, instantiateSuperclassMetadata))
751+
if (std::get<const Metadata *>(
752+
candidate.getMatchingType(type, instantiateSuperclassMetadata)))
759753
conformances.push_back(&descriptor);
760754
}
761755
}
@@ -915,8 +909,9 @@ findConformanceWithDyld(ConformanceState &C, const Metadata *type,
915909
dyldResult.value);
916910

917911
assert(conformanceDescriptor->getProtocol() == protocol);
918-
assert(ConformanceCandidate{*conformanceDescriptor}.getMatchingType(
919-
type, instantiateSuperclassMetadata));
912+
assert(std::get<const Metadata *>(
913+
ConformanceCandidate{*conformanceDescriptor}.getMatchingType(
914+
type, instantiateSuperclassMetadata)));
920915

921916
if (conformanceDescriptor->getGenericWitnessTable()) {
922917
DYLD_CONFORMANCES_LOG(
@@ -980,11 +975,29 @@ swift_conformsToProtocolMaybeInstantiateSuperclasses(
980975
const ProtocolConformanceDescriptor *dyldCachedConformanceDescriptor =
981976
nullptr;
982977

978+
// Track whether we have uninstantiated superclasses. Each time we iterate
979+
// over our superclasses, we check the final state to see if there are more
980+
// superclasses we haven't instantiated by calling noteFinalMetadataState.
981+
// If we ever see Abstract, that means there are more superclasses we can't
982+
// check yet, and we might get a false negative. We have to do this after each
983+
// iteration (really, just the first iteration, but it's hard to keep track of
984+
// which iteration is the first time), because another thread might
985+
// instantiate the superclass while we're in the middle of searching. If we
986+
// only look at the state after the last iteration, we might have hit a false
987+
// negative before that no longer shows up.
988+
bool hasUninstantiatedSuperclass = false;
989+
auto noteFinalMetadataState = [&](llvm::Optional<MetadataState> state) {
990+
hasUninstantiatedSuperclass =
991+
hasUninstantiatedSuperclass || state == MetadataState::Abstract;
992+
};
993+
983994
// Search the shared cache tables for a conformance for this type, and for
984995
// superclasses (if it's a class).
985996
if (C.dyldOptimizationsActive()) {
986-
for (auto dyldSearchType : iterateMaybeIncompleteSuperclasses(
987-
type, instantiateSuperclassMetadata)) {
997+
MaybeIncompleteSuperclassIterator superclassIterator{
998+
type, instantiateSuperclassMetadata};
999+
for (; auto dyldSearchType = superclassIterator.metadata;
1000+
++superclassIterator) {
9881001
bool definitiveFailure;
9891002
std::tie(dyldCachedWitnessTable, dyldCachedConformanceDescriptor,
9901003
definitiveFailure) =
@@ -997,6 +1010,7 @@ swift_conformsToProtocolMaybeInstantiateSuperclasses(
9971010
if (dyldCachedWitnessTable || dyldCachedConformanceDescriptor)
9981011
break;
9991012
}
1013+
noteFinalMetadataState(superclassIterator.state);
10001014

10011015
validateDyldResults(C, type, protocol, dyldCachedWitnessTable,
10021016
dyldCachedConformanceDescriptor,
@@ -1025,8 +1039,8 @@ swift_conformsToProtocolMaybeInstantiateSuperclasses(
10251039

10261040
if (dyldCachedConformanceDescriptor) {
10271041
ConformanceCandidate candidate(*dyldCachedConformanceDescriptor);
1028-
auto *matchingType =
1029-
candidate.getMatchingType(type, instantiateSuperclassMetadata);
1042+
auto *matchingType = std::get<const Metadata *>(
1043+
candidate.getMatchingType(type, instantiateSuperclassMetadata));
10301044
assert(matchingType);
10311045
auto witness = dyldCachedConformanceDescriptor->getWitnessTable(matchingType);
10321046
C.cacheResult(type, protocol, witness, /*always cache*/ 0);
@@ -1048,8 +1062,12 @@ swift_conformsToProtocolMaybeInstantiateSuperclasses(
10481062
// The matching type is exact, so they can't go stale, and we should
10491063
// always cache them.
10501064
ConformanceCandidate candidate(descriptor);
1051-
if (auto *matchingType =
1052-
candidate.getMatchingType(type, instantiateSuperclassMetadata)) {
1065+
const Metadata *matchingType;
1066+
llvm::Optional<MetadataState> finalState;
1067+
std::tie(matchingType, finalState) =
1068+
candidate.getMatchingType(type, instantiateSuperclassMetadata);
1069+
noteFinalMetadataState(finalState);
1070+
if (matchingType) {
10531071
auto witness = descriptor.getWitnessTable(matchingType);
10541072
C.cacheResult(matchingType, protocol, witness, /*always cache*/ 0);
10551073
foundWitnesses.insert({matchingType, witness});
@@ -1078,9 +1096,6 @@ swift_conformsToProtocolMaybeInstantiateSuperclasses(
10781096
const WitnessTable *foundWitness = nullptr;
10791097
const Metadata *foundType = nullptr;
10801098

1081-
// Use MaybeIncompleteSuperclassIterator directly so we can examine its final
1082-
// state. Complete indicates that we finished normally, Abstract indicates
1083-
// that there's an uninstantiated superclass we didn't iterate over.
10841099
MaybeIncompleteSuperclassIterator superclassIterator{
10851100
type, instantiateSuperclassMetadata};
10861101
for (; auto searchType = superclassIterator.metadata; ++superclassIterator) {
@@ -1102,15 +1117,13 @@ swift_conformsToProtocolMaybeInstantiateSuperclasses(
11021117
}
11031118
}
11041119
}
1105-
1106-
// Do not cache negative results if there were uninstantiated superclasses we
1107-
// didn't search. They might have a conformance that will be found later.
1108-
bool hasUninstantiatedSuperclass =
1109-
superclassIterator.state == MetadataState::Abstract;
1120+
noteFinalMetadataState(superclassIterator.state);
11101121

11111122
// If it's for a superclass or if we didn't find anything, then add an
11121123
// authoritative entry for this type.
11131124
if (foundType != type)
1125+
// Do not cache negative results if there were uninstantiated superclasses
1126+
// we didn't search. They might have a conformance that will be found later.
11141127
if (foundWitness || !hasUninstantiatedSuperclass)
11151128
C.cacheResult(type, protocol, foundWitness, snapshot.count());
11161129

@@ -1378,8 +1391,8 @@ const Metadata *swift::findConformingSuperclass(
13781391
// Figure out which type we're looking for.
13791392
ConformanceCandidate candidate(*conformance);
13801393

1381-
const Metadata *conformingType =
1382-
candidate.getMatchingType(type, true /*instantiateSuperclassMetadata*/);
1394+
const Metadata *conformingType = std::get<const Metadata *>(
1395+
candidate.getMatchingType(type, true /*instantiateSuperclassMetadata*/));
13831396
assert(conformingType);
13841397
return conformingType;
13851398
}

0 commit comments

Comments
 (0)