Skip to content

Make the lazy assignment of an assoociated type/wtable atomic #31768

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 32 additions & 13 deletions stdlib/public/runtime/Metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4645,6 +4645,8 @@ static StringRef findAssociatedTypeName(const ProtocolDescriptor *protocol,
return StringRef();
}

using AssociatedTypeWitness = std::atomic<const Metadata *>;

SWIFT_CC(swift)
static MetadataResponse
swift_getAssociatedTypeWitnessSlowImpl(
Expand All @@ -4668,8 +4670,8 @@ swift_getAssociatedTypeWitnessSlowImpl(

// Retrieve the witness.
unsigned witnessIndex = assocType - reqBase;
auto *witnessAddr = &((const Metadata **)wtable)[witnessIndex];
auto witness = *witnessAddr;
auto *witnessAddr = &((AssociatedTypeWitness*)wtable)[witnessIndex];
auto witness = witnessAddr->load(std::memory_order_acquire);

#if SWIFT_PTRAUTH
uint16_t extraDiscriminator = assocType->Flags.getExtraDiscriminator();
Expand Down Expand Up @@ -4773,8 +4775,14 @@ swift_getAssociatedTypeWitnessSlowImpl(
if (response.State == MetadataState::Complete) {
// We pass type metadata around as unsigned pointers, but we sign them
// in witness tables, which doesn't provide all that much extra security.
initAssociatedTypeProtocolWitness(witnessAddr, assocTypeMetadata,
*assocType);
auto valueToStore = assocTypeMetadata;
#if SWIFT_PTRAUTH
valueToStore = ptrauth_sign_unauthenticated(valueToStore,
swift_ptrauth_key_associated_type,
ptrauth_blend_discriminator(witnessAddr,
extraDiscriminator));
#endif
witnessAddr->store(valueToStore, std::memory_order_release);
}

return response;
Expand All @@ -4791,8 +4799,8 @@ swift::swift_getAssociatedTypeWitness(MetadataRequest request,

// If the low bit of the witness is clear, it's already a metadata pointer.
unsigned witnessIndex = assocType - reqBase;
auto *witnessAddr = &((const void* *)wtable)[witnessIndex];
auto witness = *witnessAddr;
auto *witnessAddr = &((const AssociatedTypeWitness *)wtable)[witnessIndex];
auto witness = witnessAddr->load(std::memory_order_acquire);

#if SWIFT_PTRAUTH
uint16_t extraDiscriminator = assocType->Flags.getExtraDiscriminator();
Expand All @@ -4812,6 +4820,8 @@ swift::swift_getAssociatedTypeWitness(MetadataRequest request,
reqBase, assocType);
}

using AssociatedConformanceWitness = std::atomic<void *>;

SWIFT_CC(swift)
static const WitnessTable *swift_getAssociatedConformanceWitnessSlowImpl(
WitnessTable *wtable,
Expand All @@ -4837,8 +4847,8 @@ static const WitnessTable *swift_getAssociatedConformanceWitnessSlowImpl(

// Retrieve the witness.
unsigned witnessIndex = assocConformance - reqBase;
auto *witnessAddr = &((void**)wtable)[witnessIndex];
auto witness = *witnessAddr;
auto *witnessAddr = &((AssociatedConformanceWitness*)wtable)[witnessIndex];
auto witness = witnessAddr->load(std::memory_order_acquire);

#if SWIFT_PTRAUTH
// For associated protocols, the witness is signed with address
Expand Down Expand Up @@ -4897,9 +4907,18 @@ static const WitnessTable *swift_getAssociatedConformanceWitnessSlowImpl(

// The access function returns an unsigned pointer for now.

// We can't just use initAssociatedConformanceProtocolWitness because we
// also use this function for base protocols.
initProtocolWitness(witnessAddr, assocWitnessTable, *assocConformance);
auto valueToStore = assocWitnessTable;
#if SWIFT_PTRAUTH
if (assocConformance->Flags.isSignedWithAddress()) {
uint16_t extraDiscriminator =
assocConformance->Flags.getExtraDiscriminator();
valueToStore = ptrauth_sign_unauthenticated(valueToStore,
swift_ptrauth_key_associated_conformance,
ptrauth_blend_discriminator(witnessAddr,
extraDiscriminator));
}
#endif
witnessAddr->store(valueToStore, std::memory_order_release);

return assocWitnessTable;
}
Expand All @@ -4920,8 +4939,8 @@ const WitnessTable *swift::swift_getAssociatedConformanceWitness(

// Retrieve the witness.
unsigned witnessIndex = assocConformance - reqBase;
auto *witnessAddr = &((const void* *)wtable)[witnessIndex];
auto witness = *witnessAddr;
auto *witnessAddr = &((AssociatedConformanceWitness*)wtable)[witnessIndex];
auto witness = witnessAddr->load(std::memory_order_acquire);

#if SWIFT_PTRAUTH
uint16_t extraDiscriminator = assocConformance->Flags.getExtraDiscriminator();
Expand Down
48 changes: 48 additions & 0 deletions test/Runtime/associated_witness_concurrency.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// RUN: %target-swiftc_driver %s -g %import-libdispatch -o %t
// RUN: %target-codesign %t
// RUN: %target-run %t
// REQUIRES: libdispatch
// REQUIRES: executable_test

import Dispatch

protocol P {
associatedtype Unused
}
struct A<T> : P {
// We never actually read this associated type, but its presence in the
// wtable should force it to be instantiated rather than shared for
// all specializations of A.
typealias Unused = A<A<T>>
}

protocol Q {
associatedtype Assoc: P

func foo()
}
struct B<T: P> : Q {
// Both the metadata and the wtable for this associated type require
// runtime instantiation and must be fetched lazily.
typealias Assoc = A<T>

func foo() {}
}

// It's possible that the optimizer might someday be able to recognize
// that this is a no-op.
func rundown<T: Q>(value: T, count: Int) {
guard count > 0 else { return }

value.foo()

// Assuming that T is B<U> for some U, this constructs B<A<U>>,
// which will be T in the recursive call; i.e. we should have a
// different metadata/wtable pair each time. In order to construct
// that, we have to read the associated metadata and wtable for T.Assoc.
rundown(value: B<T.Assoc>(), count: count - 1)
}

DispatchQueue.concurrentPerform(iterations: 5) { n in
rundown(value: B<A<()>>(), count: 1000)
}