Skip to content
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

Fix crash when back-deploying concurrency to iOS 15.0/15.1-era OSs #65760

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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
47 changes: 47 additions & 0 deletions stdlib/toolchain/Compatibility56/Concurrency/Actor.cpp
Expand Up @@ -8,6 +8,9 @@
#include "swift/Runtime/Casting.h"
#include "Runtime/Threading/ThreadLocal.h"

#include <Availability.h>
#include <TargetConditionals.h>

#include <atomic>
#include <new>

Expand Down Expand Up @@ -146,3 +149,47 @@ void swift::adoptTaskVoucher(AsyncTask *task) {
void swift::restoreTaskVoucher(AsyncTask *task) {
ExecutorTrackingInfo::current()->restoreVoucher(task);
}

static swift_once_t voucherDisableCheckOnce;
static bool vouchersDisabled;

namespace {
struct _SwiftNSOperatingSystemVersion{
intptr_t majorVersion;
intptr_t minorVersion;
intptr_t patchVersion;
};
}

extern "C"
_SwiftNSOperatingSystemVersion
_swift_stdlib_operatingSystemVersion() __attribute__((const));

static void _initializeVouchersDisabled(void *ctxt) {
auto osVersion = _swift_stdlib_operatingSystemVersion();
#if TARGET_OS_WATCH
vouchersDisabled = (
osVersion.majorVersion == 8 &&
osVersion.minorVersion >= 0 && osVersion.minorVersion < 3
);
#elif TARGET_OS_IPHONE
vouchersDisabled = (
osVersion.majorVersion == 15 &&
osVersion.minorVersion >= 0 && osVersion.minorVersion < 2
);
#elif TARGET_OS_OSX
vouchersDisabled = (
osVersion.majorVersion == 12 &&
osVersion.minorVersion >= 0 && osVersion.minorVersion < 1
);
#else
vouchersDisabled = false;
#endif
}

bool VoucherManager::vouchersAreDisabled() {
swift_once(&voucherDisableCheckOnce,
&_initializeVouchersDisabled,
nullptr);
return vouchersDisabled;
}
Expand Up @@ -31,6 +31,12 @@ class VoucherManager {
/// async work.
llvm::Optional<voucher_t> OriginalVoucher;

/// Determine whether vouchers are disabled entirely. This evaluates
/// true on platforms whose concurrency library does not support the
/// propagation of vouchers, in which case all of the operations of
/// this class must be no-ops.
static bool vouchersAreDisabled();

public:
VoucherManager() {
SWIFT_TASK_DEBUG_LOG("[%p] Constructing VoucherManager", this);
Expand All @@ -41,6 +47,9 @@ class VoucherManager {
/// VoucherManager object is destroyed. It may also be called in other
/// places to restore the original voucher and reset the VoucherManager.
void leave() {
if (vouchersAreDisabled())
return;

if (OriginalVoucher) {
SWIFT_TASK_DEBUG_LOG("[%p] Restoring original voucher %p", this,
*OriginalVoucher);
Expand All @@ -62,6 +71,9 @@ class VoucherManager {
/// this is permanent. For Tasks, the voucher must be restored using
/// restoreVoucher if the task suspends.
void swapToJob(Job *job) {
if (vouchersAreDisabled())
return;

SWIFT_TASK_DEBUG_LOG("[%p] Swapping jobs to %p", this, job);
assert(job);
assert(job->Voucher != SWIFT_DEAD_VOUCHER);
Expand Down Expand Up @@ -99,6 +111,9 @@ class VoucherManager {
// Take the current thread's adopted voucher and place it back into the task
// that previously owned it, re-adopting the thread's original voucher.
void restoreVoucher(AsyncTask *task) {
if (vouchersAreDisabled())
return;

SWIFT_TASK_DEBUG_LOG("[%p] Restoring %svoucher on task %p", this,
OriginalVoucher ? "" : "missing ", task);
assert(OriginalVoucher);
Expand Down