Skip to content

Commit 4f27dc5

Browse files
authored
Merge pull request #37656 from mikeash/fix-concurrent-typeByName
[Runtime] Fix concurrent _typeByName with generic arguments, protocol conformances, and superclasses.
2 parents a410243 + a80fe85 commit 4f27dc5

File tree

2 files changed

+1169
-74
lines changed

2 files changed

+1169
-74
lines changed

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 110 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,90 @@ const ClassMetadata *TypeReference::getObjCClass(TypeReferenceKind kind) const {
147147
}
148148
#endif
149149

150+
static MetadataState
151+
tryGetCompleteMetadataNonblocking(const Metadata *metadata) {
152+
return swift_checkMetadataState(
153+
MetadataRequest(MetadataState::Complete, /*isNonBlocking*/ true),
154+
metadata)
155+
.State;
156+
}
157+
158+
/// Get the superclass of metadata, which may be incomplete. When the metadata
159+
/// is not sufficiently complete, then we fall back to demangling the superclass
160+
/// in the nominal type descriptor, which is slow but works. Return NULL if the
161+
/// metadata is not a class.
162+
///
163+
/// If the metadata's current state is known, it may be passed in as
164+
/// knownMetadataState. This saves the cost of retrieving that info separately.
165+
static MetadataResponse getSuperclassForMaybeIncompleteMetadata(
166+
const Metadata *metadata,
167+
llvm::Optional<MetadataState> knownMetadataState) {
168+
const ClassMetadata *classMetadata = dyn_cast<ClassMetadata>(metadata);
169+
if (!classMetadata)
170+
return {_swift_class_getSuperclass(metadata), MetadataState::Complete};
171+
172+
MetadataState metadataState;
173+
if (knownMetadataState)
174+
metadataState = *knownMetadataState;
175+
else
176+
metadataState = tryGetCompleteMetadataNonblocking(classMetadata);
177+
178+
if (metadataState == MetadataState::Complete) {
179+
// The subclass metadata is complete. Fetch and return the superclass.
180+
auto *superMetadata = getMetadataForClass(classMetadata->Superclass);
181+
return {superMetadata, MetadataState::Complete};
182+
}
183+
if (metadataState == MetadataState::NonTransitiveComplete) {
184+
// The subclass metadata is complete, but, unlike above, not transitively.
185+
// Its Superclass field is valid, so just read that field to get to the
186+
// superclass to proceed to the next step.
187+
auto *superMetadata = getMetadataForClass(classMetadata->Superclass);
188+
auto superState = tryGetCompleteMetadataNonblocking(superMetadata);
189+
return {superMetadata, superState};
190+
} else {
191+
// The subclass metadata is either LayoutComplete or Abstract, so the
192+
// Superclass field is not valid. To get to the superclass, make the
193+
// expensive call to getSuperclassMetadata which demangles the superclass
194+
// name from the nominal type descriptor to get the metadata for the
195+
// superclass.
196+
MetadataRequest request(MetadataState::Complete,
197+
/*non-blocking*/ true);
198+
return getSuperclassMetadata(request, classMetadata);
199+
}
200+
}
201+
202+
class MaybeIncompleteSuperclassIterator {
203+
const Metadata *metadata;
204+
llvm::Optional<MetadataState> state;
205+
206+
public:
207+
MaybeIncompleteSuperclassIterator(const Metadata *metadata)
208+
: metadata(metadata), state(llvm::None) {}
209+
210+
MaybeIncompleteSuperclassIterator &operator++() {
211+
auto response = getSuperclassForMaybeIncompleteMetadata(metadata, state);
212+
metadata = response.Value;
213+
state = response.State;
214+
return *this;
215+
}
216+
217+
const Metadata *operator*() const { return metadata; }
218+
219+
bool operator!=(const MaybeIncompleteSuperclassIterator rhs) const {
220+
return metadata != rhs.metadata;
221+
}
222+
};
223+
224+
/// Return a range that will iterate over the given metadata and all its
225+
/// superclasses in order. If the metadata is not a class, iteration will
226+
/// provide that metadata and then stop.
227+
iterator_range<MaybeIncompleteSuperclassIterator>
228+
iterateMaybeIncompleteSuperclasses(const Metadata *metadata) {
229+
return iterator_range<MaybeIncompleteSuperclassIterator>(
230+
MaybeIncompleteSuperclassIterator(metadata),
231+
MaybeIncompleteSuperclassIterator(nullptr));
232+
}
233+
150234
/// Take the type reference inside a protocol conformance record and fetch the
151235
/// canonical metadata pointer for the type it refers to.
152236
/// Returns nil for universal or generic type references.
@@ -470,13 +554,10 @@ searchInConformanceCache(const Metadata *type,
470554
auto origType = type;
471555
auto snapshot = C.Cache.snapshot();
472556

473-
while (type) {
557+
for (auto type : iterateMaybeIncompleteSuperclasses(type)) {
474558
if (auto *Value = snapshot.find(ConformanceCacheKey(type, protocol))) {
475559
return {type == origType, Value->getWitnessTable()};
476560
}
477-
478-
// If there is a superclass, look there.
479-
type = _swift_class_getSuperclass(type);
480561
}
481562

482563
// We did not find a cache entry.
@@ -562,13 +643,10 @@ namespace {
562643
/// be a superclass of the given type. Returns null if this type does not
563644
/// match this conformance.
564645
const Metadata *getMatchingType(const Metadata *conformingType) const {
565-
while (conformingType) {
566-
// Check for a match.
646+
for (auto conformingType :
647+
iterateMaybeIncompleteSuperclasses(conformingType)) {
567648
if (matches(conformingType))
568649
return conformingType;
569-
570-
// Look for a superclass.
571-
conformingType = _swift_class_getSuperclass(conformingType);
572650
}
573651

574652
return nullptr;
@@ -739,8 +817,7 @@ swift_conformsToProtocolImpl(const Metadata *const type,
739817
// Search the shared cache tables for a conformance for this type, and for
740818
// superclasses (if it's a class).
741819
if (C.sharedCacheOptimizationsActive()) {
742-
const Metadata *dyldSearchType = type;
743-
do {
820+
for (auto dyldSearchType : iterateMaybeIncompleteSuperclasses(type)) {
744821
bool definitiveFailure;
745822
std::tie(dyldCachedWitnessTable, dyldCachedConformanceDescriptor,
746823
definitiveFailure) =
@@ -749,9 +826,9 @@ swift_conformsToProtocolImpl(const Metadata *const type,
749826
if (definitiveFailure)
750827
return nullptr;
751828

752-
dyldSearchType = _swift_class_getSuperclass(dyldSearchType);
753-
} while (dyldSearchType && !dyldCachedWitnessTable &&
754-
!dyldCachedConformanceDescriptor);
829+
if (dyldCachedWitnessTable || dyldCachedConformanceDescriptor)
830+
break;
831+
}
755832

756833
validateSharedCacheResults(C, type, protocol, dyldCachedWitnessTable,
757834
dyldCachedConformanceDescriptor);
@@ -827,18 +904,18 @@ swift_conformsToProtocolImpl(const Metadata *const type,
827904

828905
// Find the most specific conformance that was scanned.
829906
const WitnessTable *foundWitness = nullptr;
830-
const Metadata *searchType = type;
831-
while (!foundWitness && searchType) {
907+
const Metadata *foundType = nullptr;
908+
for (auto searchType : iterateMaybeIncompleteSuperclasses(type)) {
832909
foundWitness = foundWitnesses.lookup(searchType);
833-
834-
// If there's no entry here, move up to the superclass (if any).
835-
if (!foundWitness)
836-
searchType = _swift_class_getSuperclass(searchType);
910+
if (foundWitness) {
911+
foundType = searchType;
912+
break;
913+
}
837914
}
838915

839916
// If it's for a superclass or if we didn't find anything, then add an
840917
// authoritative entry for this type.
841-
if (searchType != type)
918+
if (foundType != type)
842919
C.cacheResult(type, protocol, foundWitness, snapshot.count());
843920

844921
// A negative result can be overridden by a result from dyld.
@@ -864,67 +941,26 @@ swift::_searchConformancesByMangledTypeName(Demangle::NodePointer node) {
864941
return nullptr;
865942
}
866943

867-
static MetadataState
868-
tryGetCompleteMetadataNonblocking(const Metadata *metadata) {
869-
return swift_checkMetadataState(
870-
MetadataRequest(MetadataState::Complete, /*isNonBlocking*/ true),
871-
metadata)
872-
.State;
873-
}
874-
875944
template <typename HandleObjc>
876945
bool isSwiftClassMetadataSubclass(const ClassMetadata *subclass,
877946
const ClassMetadata *superclass,
878947
HandleObjc handleObjc) {
879948
assert(subclass);
880949
assert(superclass);
881950

882-
MetadataState subclassState = tryGetCompleteMetadataNonblocking(subclass);
883-
884-
do {
885-
if (subclassState == MetadataState::Complete) {
886-
// The subclass metadata is complete. That means not just that its
887-
// Superclass field is valid, but that the Superclass field of the
888-
// referenced class metadata is valid, and the Superclass field of the
889-
// class metadata referenced there, and so on transitively.
890-
//
891-
// Scan the superclass chains in the ClassMetadata looking for a match.
892-
while ((subclass = subclass->Superclass)) {
893-
if (subclass == superclass)
894-
return true;
895-
}
896-
return false;
897-
}
898-
if (subclassState == MetadataState::NonTransitiveComplete) {
899-
// The subclass metadata is complete, but, unlike above, not transitively.
900-
// Its Superclass field is valid, so just read that field to get to the
901-
// superclass to proceed to the next step.
902-
subclass = subclass->Superclass;
903-
if (subclass->isPureObjC()) {
904-
return handleObjc(subclass, superclass);
905-
}
906-
subclassState = tryGetCompleteMetadataNonblocking(subclass);
907-
} else {
908-
// The subclass metadata is either LayoutComplete or Abstract, so the
909-
// Superclass field is not valid. To get to the superclass, make the
910-
// expensive call to getSuperclassMetadata which demangles the superclass
911-
// name from the nominal type descriptor to get the metadata for the
912-
// superclass.
913-
MetadataRequest request(MetadataState::Complete,
914-
/*non-blocking*/ true);
915-
auto response = getSuperclassMetadata(request, subclass);
916-
auto newMetadata = response.Value;
917-
if (auto newSubclass = dyn_cast<ClassMetadata>(newMetadata)) {
918-
subclass = newSubclass;
919-
subclassState = response.State;
920-
} else {
921-
return handleObjc(newMetadata, superclass);
922-
}
923-
}
924-
if (subclass == superclass)
951+
llvm::Optional<MetadataState> subclassState = llvm::None;
952+
while (true) {
953+
auto response =
954+
getSuperclassForMaybeIncompleteMetadata(subclass, subclassState);
955+
if (response.Value == superclass)
925956
return true;
926-
} while (subclass);
927-
return false;
957+
if (!response.Value)
958+
return false;
959+
960+
subclass = dyn_cast<ClassMetadata>(response.Value);
961+
if (!subclass || subclass->isPureObjC())
962+
return handleObjc(response.Value, superclass);
963+
}
928964
}
929965

930966
// Whether the provided `subclass` is metadata for a subclass* of the superclass

0 commit comments

Comments
 (0)