@@ -536,7 +536,7 @@ namespace {
536536 };
537537
538538 struct ConformanceCacheEntry {
539- private :
539+ public :
540540 // / Storage used when we have global actor isolation on the conformance.
541541 struct ExtendedStorage {
542542 // / The protocol to which the type conforms.
@@ -549,28 +549,48 @@ namespace {
549549 // / When the conformance is global-actor-isolated, this is the conformance
550550 // / of globalActorIsolationType to GlobalActor.
551551 const WitnessTable *globalActorIsolationWitnessTable = nullptr ;
552+
553+ // / The next pointer in the list of extended storage allocations.
554+ ExtendedStorage *next = nullptr ;
552555 };
553556
554557 const Metadata *Type;
555- llvm::PointerUnion<const ProtocolDescriptor *, const ExtendedStorage *>
558+ llvm::PointerUnion<const ProtocolDescriptor *, ExtendedStorage *>
556559 ProtoOrStorage;
557560
558561 // / The witness table.
559562 const WitnessTable *Witness;
560563
561564 public:
562565 ConformanceCacheEntry (ConformanceCacheKey key,
563- ConformanceLookupResult result)
566+ ConformanceLookupResult result,
567+ std::atomic<ExtendedStorage *> &storageHead)
564568 : Type(key.Type), Witness(result.witnessTable)
565569 {
566- if (result.globalActorIsolationType ) {
567- ProtoOrStorage = new ExtendedStorage{
568- key.Proto , result.globalActorIsolationType ,
569- result.globalActorIsolationWitnessTable
570- };
571- } else {
570+ if (!result.globalActorIsolationType ) {
572571 ProtoOrStorage = key.Proto ;
572+ return ;
573573 }
574+
575+ // Allocate extended storage.
576+ void *memory = malloc (sizeof (ExtendedStorage));
577+ auto storage = new (memory) ExtendedStorage{
578+ key.Proto , result.globalActorIsolationType ,
579+ result.globalActorIsolationWitnessTable
580+ };
581+
582+ ProtoOrStorage = storage;
583+
584+ // Add the storage pointer to the list of extended storage allocations
585+ // so that we can free them later.
586+ auto head = storageHead.load (std::memory_order_relaxed);
587+ while (true ) {
588+ storage->next = head;
589+ if (storageHead.compare_exchange_weak (
590+ head, storage, std::memory_order_release,
591+ std::memory_order_relaxed))
592+ break ;
593+ };
574594 }
575595
576596 bool matchesKey (const ConformanceCacheKey &key) const {
@@ -586,7 +606,7 @@ namespace {
586606 if (auto proto = ProtoOrStorage.dyn_cast <const ProtocolDescriptor *>())
587607 return proto;
588608
589- if (auto storage = ProtoOrStorage.dyn_cast <const ExtendedStorage *>())
609+ if (auto storage = ProtoOrStorage.dyn_cast <ExtendedStorage *>())
590610 return storage->Proto ;
591611
592612 return nullptr ;
@@ -606,7 +626,7 @@ namespace {
606626 if (ProtoOrStorage.is <const ProtocolDescriptor *>())
607627 return ConformanceLookupResult { Witness, nullptr , nullptr };
608628
609- if (auto storage = ProtoOrStorage.dyn_cast <const ExtendedStorage *>()) {
629+ if (auto storage = ProtoOrStorage.dyn_cast <ExtendedStorage *>()) {
610630 return ConformanceLookupResult (
611631 Witness, storage->globalActorIsolationType ,
612632 storage->globalActorIsolationWitnessTable );
@@ -621,6 +641,11 @@ namespace {
621641struct ConformanceState {
622642 ConcurrentReadableHashMap<ConformanceCacheEntry> Cache;
623643 ConcurrentReadableArray<ConformanceSection> SectionsToScan;
644+
645+ // / The head of an intrusive linked list that keeps track of all of the
646+ // / conformance cache entries that require extended storage.
647+ std::atomic<ConformanceCacheEntry::ExtendedStorage *> ExtendedStorageHead{nullptr };
648+
624649 bool scanSectionsBackwards;
625650
626651#if USE_DYLD_SHARED_CACHE_CONFORMANCE_TABLES
@@ -709,7 +734,8 @@ struct ConformanceState {
709734 return false ; // abandon the new entry
710735
711736 ::new (entry) ConformanceCacheEntry (
712- ConformanceCacheKey (type, proto), result);
737+ ConformanceCacheKey (type, proto), result,
738+ ExtendedStorageHead);
713739 return true ; // keep the new entry
714740 });
715741 }
@@ -743,7 +769,20 @@ static void _registerProtocolConformances(ConformanceState &C,
743769
744770 // Blow away the conformances cache to get rid of any negative entries that
745771 // may now be obsolete.
746- C.Cache .clear ();
772+ C.Cache .clear ([&](ConcurrentFreeListNode *&freeListHead) {
773+ // The extended storage for conformance entries will need to be freed
774+ // eventually. Put it on the concurrent free list so the cache will do so.
775+ auto storageHead = C.ExtendedStorageHead .load (std::memory_order_relaxed);
776+ while (storageHead) {
777+ auto current = storageHead;
778+ auto newHead = current->next ;
779+ if (C.ExtendedStorageHead .compare_exchange_weak (
780+ storageHead, newHead, std::memory_order_release,
781+ std::memory_order_relaxed)) {
782+ ConcurrentFreeListNode::add (&freeListHead, current);
783+ }
784+ }
785+ });
747786}
748787
749788void swift::addImageProtocolConformanceBlockCallbackUnsafe (
0 commit comments