Skip to content

Commit 73002af

Browse files
committed
[Runtime] Change ConcurrentReadableArray's API to provide iterable snapshots rather than using a callback-based read call.
rdar://problem/40230581
1 parent a66931e commit 73002af

File tree

2 files changed

+132
-127
lines changed

2 files changed

+132
-127
lines changed

include/swift/Runtime/Concurrent.h

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,37 @@ template <class ElemTy> struct ConcurrentReadableArray {
451451
Mutex WriterLock;
452452
std::vector<Storage *> FreeList;
453453

454+
void incrementReaders() {
455+
ReaderCount.fetch_add(1, std::memory_order_acquire);
456+
}
457+
458+
void decrementReaders() {
459+
ReaderCount.fetch_sub(1, std::memory_order_release);
460+
}
461+
454462
public:
463+
struct Snapshot {
464+
ConcurrentReadableArray *Array;
465+
const ElemTy *Start;
466+
size_t Count;
467+
468+
Snapshot(ConcurrentReadableArray *array, const ElemTy *start, size_t count)
469+
: Array(array), Start(start), Count(count) {}
470+
471+
Snapshot(const Snapshot &other)
472+
: Array(other.Array), Start(other.Start), Count(other.Count) {
473+
Array->incrementReaders();
474+
}
475+
476+
~Snapshot() {
477+
Array->decrementReaders();
478+
}
479+
480+
const ElemTy *begin() { return Start; }
481+
const ElemTy *end() { return Start + Count; }
482+
size_t count() { return Count; }
483+
};
484+
455485
// This type cannot be safely copied, moved, or deleted.
456486
ConcurrentReadableArray(const ConcurrentReadableArray &) = delete;
457487
ConcurrentReadableArray(ConcurrentReadableArray &&) = delete;
@@ -486,28 +516,16 @@ template <class ElemTy> struct ConcurrentReadableArray {
486516
storage->deallocate();
487517
}
488518

489-
/// Read the contents of the array. The parameter `f` is called with
490-
/// two parameters: a pointer to the elements in the array, and the
491-
/// count. This represents a snapshot of the contents at the time
492-
/// `read` was called. The pointer becomes invalid after `f` returns.
493-
template <class F> auto read(F f) -> decltype(f(nullptr, 0)) {
494-
ReaderCount.fetch_add(1, std::memory_order_acquire);
519+
Snapshot snapshot() {
520+
incrementReaders();
495521
auto *storage = Elements.load(SWIFT_MEMORY_ORDER_CONSUME);
522+
if (storage == nullptr) {
523+
return Snapshot(this, nullptr, 0);
524+
}
525+
496526
auto count = storage->Count.load(std::memory_order_acquire);
497527
const auto *ptr = storage->data();
498-
499-
decltype(f(nullptr, 0)) result = f(ptr, count);
500-
501-
ReaderCount.fetch_sub(1, std::memory_order_release);
502-
503-
return result;
504-
}
505-
506-
/// Get the current count. It's just a snapshot and may be obsolete immediately.
507-
size_t count() {
508-
return read([](const ElemTy *ptr, size_t count) -> size_t {
509-
return count;
510-
});
528+
return Snapshot(this, ptr, count);
511529
}
512530
};
513531

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 95 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -326,14 +326,11 @@ void ConformanceState::verify() const {
326326
// Iterate over all of the sections and verify all of the protocol
327327
// descriptors.
328328
auto &Self = const_cast<ConformanceState &>(*this);
329-
Self.SectionsToScan.read([](const ConformanceSection *ptr, size_t count) -> char {
330-
for (size_t i = 0; i < count; i++) {
331-
for (const auto &Record : ptr[i]) {
332-
Record.get()->verify();
333-
}
329+
for (const auto &Section : Self.SectionsToScan.snapshot()) {
330+
for (const auto &Record : Section) {
331+
Record.get()->verify();
334332
}
335-
return 0;
336-
});
333+
}
337334
}
338335
#endif
339336

@@ -445,7 +442,7 @@ searchInConformanceCache(const Metadata *type,
445442
}
446443

447444
// Check if the negative cache entry is up-to-date.
448-
if (Value->getFailureGeneration() == C.SectionsToScan.count()) {
445+
if (Value->getFailureGeneration() == C.SectionsToScan.snapshot().count()) {
449446
// Negative cache entry is up-to-date. Return failure along with
450447
// the original query type's own cache entry, if we found one.
451448
// (That entry may be out of date but the caller still has use for it.)
@@ -546,100 +543,94 @@ swift_conformsToProtocolImpl(const Metadata * const type,
546543
auto failureEntry = FoundConformance.failureEntry;
547544

548545
// Prepare to scan conformance records.
549-
size_t scannedCount;
550-
auto returnNull = C.SectionsToScan
551-
.read([&](const ConformanceSection *ptr, size_t count) -> bool {
552-
scannedCount = count;
553-
// Scan only sections that were not scanned yet.
554-
// If we found an out-of-date negative cache entry,
555-
// we need not to re-scan the sections that it covers.
556-
auto startIndex = failureEntry ? failureEntry->getFailureGeneration() : 0;
557-
auto endIndex = count;
546+
auto snapshot = C.SectionsToScan.snapshot();
558547

559-
// If there are no unscanned sections outstanding
560-
// then we can cache failure and give up now.
561-
if (startIndex == endIndex) {
562-
C.cacheFailure(type, protocol, count);
563-
return true;
548+
// Scan only sections that were not scanned yet.
549+
// If we found an out-of-date negative cache entry,
550+
// we need not to re-scan the sections that it covers.
551+
auto startIndex = failureEntry ? failureEntry->getFailureGeneration() : 0;
552+
auto endIndex = snapshot.count();
553+
554+
// If there are no unscanned sections outstanding
555+
// then we can cache failure and give up now.
556+
if (startIndex == endIndex) {
557+
C.cacheFailure(type, protocol, snapshot.count());
558+
return nullptr;
559+
}
560+
561+
/// Local function to retrieve the witness table and record the result.
562+
auto recordWitnessTable = [&](const ProtocolConformanceDescriptor &descriptor,
563+
const Metadata *type) {
564+
switch (descriptor.getConformanceKind()) {
565+
case ConformanceFlags::ConformanceKind::WitnessTable:
566+
// If the record provides a nondependent witness table for all
567+
// instances of a generic type, cache it for the generic pattern.
568+
C.cacheSuccess(type, protocol, descriptor.getStaticWitnessTable());
569+
return;
570+
571+
case ConformanceFlags::ConformanceKind::WitnessTableAccessor:
572+
// If the record provides a dependent witness table accessor,
573+
// cache the result for the instantiated type metadata.
574+
C.cacheSuccess(type, protocol, descriptor.getWitnessTable(type));
575+
return;
576+
577+
case ConformanceFlags::ConformanceKind::ConditionalWitnessTableAccessor: {
578+
auto witnessTable = descriptor.getWitnessTable(type);
579+
if (witnessTable)
580+
C.cacheSuccess(type, protocol, witnessTable);
581+
else
582+
C.cacheFailure(type, protocol, snapshot.count());
583+
return;
584+
}
564585
}
565586

566-
/// Local function to retrieve the witness table and record the result.
567-
auto recordWitnessTable = [&](const ProtocolConformanceDescriptor &descriptor,
568-
const Metadata *type) {
569-
switch (descriptor.getConformanceKind()) {
570-
case ConformanceFlags::ConformanceKind::WitnessTable:
571-
// If the record provides a nondependent witness table for all
572-
// instances of a generic type, cache it for the generic pattern.
573-
C.cacheSuccess(type, protocol, descriptor.getStaticWitnessTable());
574-
return;
575-
576-
case ConformanceFlags::ConformanceKind::WitnessTableAccessor:
577-
// If the record provides a dependent witness table accessor,
578-
// cache the result for the instantiated type metadata.
579-
C.cacheSuccess(type, protocol, descriptor.getWitnessTable(type));
580-
return;
581-
582-
case ConformanceFlags::ConformanceKind::ConditionalWitnessTableAccessor: {
583-
auto witnessTable = descriptor.getWitnessTable(type);
584-
if (witnessTable)
585-
C.cacheSuccess(type, protocol, witnessTable);
586-
else
587-
C.cacheFailure(type, protocol, count);
588-
return;
589-
}
590-
}
587+
// Always fail, because we cannot interpret a future conformance
588+
// kind.
589+
C.cacheFailure(type, protocol, snapshot.count());
590+
};
591591

592-
// Always fail, because we cannot interpret a future conformance
593-
// kind.
594-
C.cacheFailure(type, protocol, count);
595-
};
596-
597-
// Really scan conformance records.
598-
for (size_t i = startIndex; i < endIndex; i++) {
599-
auto &section = ptr[i];
600-
// Eagerly pull records for nondependent witnesses into our cache.
601-
for (const auto &record : section) {
602-
auto &descriptor = *record.get();
603-
604-
// If the record applies to a specific type, cache it.
605-
if (auto metadata = descriptor.getCanonicalTypeMetadata()) {
606-
auto P = descriptor.getProtocol();
607-
608-
// Look for an exact match.
609-
if (protocol != P)
610-
continue;
611-
612-
if (!isRelatedType(type, metadata, /*candidateIsMetadata=*/true))
613-
continue;
614-
615-
// Record the witness table.
616-
recordWitnessTable(descriptor, metadata);
617-
618-
// TODO: "Nondependent witness table" probably deserves its own flag.
619-
// An accessor function might still be necessary even if the witness table
620-
// can be shared.
621-
} else if (descriptor.getTypeKind()
622-
== TypeMetadataRecordKind::DirectNominalTypeDescriptor ||
623-
descriptor.getTypeKind()
624-
== TypeMetadataRecordKind::IndirectNominalTypeDescriptor) {
625-
auto R = descriptor.getTypeContextDescriptor();
626-
auto P = descriptor.getProtocol();
627-
628-
// Look for an exact match.
629-
if (protocol != P)
630-
continue;
631-
632-
if (!isRelatedType(type, R, /*candidateIsMetadata=*/false))
633-
continue;
634-
635-
recordWitnessTable(descriptor, type);
636-
}
592+
// Really scan conformance records.
593+
for (size_t i = startIndex; i < endIndex; i++) {
594+
auto &section = snapshot.Start[i];
595+
// Eagerly pull records for nondependent witnesses into our cache.
596+
for (const auto &record : section) {
597+
auto &descriptor = *record.get();
598+
599+
// If the record applies to a specific type, cache it.
600+
if (auto metadata = descriptor.getCanonicalTypeMetadata()) {
601+
auto P = descriptor.getProtocol();
602+
603+
// Look for an exact match.
604+
if (protocol != P)
605+
continue;
606+
607+
if (!isRelatedType(type, metadata, /*candidateIsMetadata=*/true))
608+
continue;
609+
610+
// Record the witness table.
611+
recordWitnessTable(descriptor, metadata);
612+
613+
// TODO: "Nondependent witness table" probably deserves its own flag.
614+
// An accessor function might still be necessary even if the witness table
615+
// can be shared.
616+
} else if (descriptor.getTypeKind()
617+
== TypeMetadataRecordKind::DirectNominalTypeDescriptor ||
618+
descriptor.getTypeKind()
619+
== TypeMetadataRecordKind::IndirectNominalTypeDescriptor) {
620+
auto R = descriptor.getTypeContextDescriptor();
621+
auto P = descriptor.getProtocol();
622+
623+
// Look for an exact match.
624+
if (protocol != P)
625+
continue;
626+
627+
if (!isRelatedType(type, R, /*candidateIsMetadata=*/false))
628+
continue;
629+
630+
recordWitnessTable(descriptor, type);
637631
}
638632
}
639-
return false;
640-
});
641-
642-
if (returnNull) return nullptr;
633+
}
643634

644635
// Conformance scan is complete.
645636
// Search the cache once more, and this time update the cache if necessary.
@@ -648,7 +639,7 @@ swift_conformsToProtocolImpl(const Metadata * const type,
648639
if (FoundConformance.isAuthoritative) {
649640
return FoundConformance.witnessTable;
650641
} else {
651-
C.cacheFailure(type, protocol, scannedCount);
642+
C.cacheFailure(type, protocol, snapshot.count());
652643
return nullptr;
653644
}
654645
}
@@ -657,19 +648,15 @@ const TypeContextDescriptor *
657648
swift::_searchConformancesByMangledTypeName(Demangle::NodePointer node) {
658649
auto &C = Conformances.get();
659650

660-
return C.SectionsToScan
661-
.read([&](const ConformanceSection *ptr, size_t count) -> const TypeContextDescriptor * {
662-
for (size_t i = 0; i < count; i++) {
663-
auto &section = ptr[i];
664-
for (const auto &record : section) {
665-
if (auto ntd = record->getTypeContextDescriptor()) {
666-
if (_contextDescriptorMatchesMangling(ntd, node))
667-
return ntd;
668-
}
651+
for (auto &section : C.SectionsToScan.snapshot()) {
652+
for (const auto &record : section) {
653+
if (auto ntd = record->getTypeContextDescriptor()) {
654+
if (_contextDescriptorMatchesMangling(ntd, node))
655+
return ntd;
669656
}
670657
}
671-
return nullptr;
672-
});
658+
}
659+
return nullptr;
673660
}
674661

675662
/// Resolve a reference to a generic parameter to type metadata.

0 commit comments

Comments
 (0)