Skip to content

Commit 0b57c76

Browse files
authored
Merge pull request #39160 from mikeash/voucher-propagation-5.5
[5.5][Concurrency] Propagate Darwin vouchers across async tasks.
2 parents ad99f5c + 601c29a commit 0b57c76

File tree

9 files changed

+716
-114
lines changed

9 files changed

+716
-114
lines changed

include/swift/ABI/Task.h

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "swift/ABI/Metadata.h"
2424
#include "swift/ABI/MetadataValues.h"
2525
#include "swift/Runtime/Config.h"
26+
#include "swift/Runtime/VoucherShims.h"
2627
#include "swift/Basic/STLExtras.h"
2728
#include "bitset"
2829
#include "queue" // TODO: remove and replace with our own mpsc
@@ -72,7 +73,13 @@ class alignas(2 * alignof(void*)) Job :
7273
// Derived classes can use this to store a Job Id.
7374
uint32_t Id = 0;
7475

75-
void *Reserved[2] = {};
76+
/// The voucher associated with the job. Note: this is currently unused on
77+
/// non-Darwin platforms, with stub implementations of the functions for
78+
/// consistency.
79+
voucher_t Voucher = nullptr;
80+
81+
/// Reserved for future use.
82+
void *Reserved = nullptr;
7683

7784
// We use this union to avoid having to do a second indirect branch
7885
// when resuming an asynchronous task, which we expect will be the
@@ -88,23 +95,32 @@ class alignas(2 * alignof(void*)) Job :
8895
Job(JobFlags flags, JobInvokeFunction *invoke,
8996
const HeapMetadata *metadata = &jobHeapMetadata)
9097
: HeapObject(metadata), Flags(flags), RunJob(invoke) {
98+
Voucher = voucher_copy();
9199
assert(!isAsyncTask() && "wrong constructor for a task");
92100
}
93101

94102
Job(JobFlags flags, TaskContinuationFunction *invoke,
95-
const HeapMetadata *metadata = &jobHeapMetadata)
103+
const HeapMetadata *metadata = &jobHeapMetadata,
104+
bool captureCurrentVoucher = true)
96105
: HeapObject(metadata), Flags(flags), ResumeTask(invoke) {
106+
if (captureCurrentVoucher)
107+
Voucher = voucher_copy();
97108
assert(isAsyncTask() && "wrong constructor for a non-task job");
98109
}
99110

100111
/// Create a job with "immortal" reference counts.
101112
/// Used for async let tasks.
102113
Job(JobFlags flags, TaskContinuationFunction *invoke,
103-
const HeapMetadata *metadata, InlineRefCounts::Immortal_t immortal)
114+
const HeapMetadata *metadata, InlineRefCounts::Immortal_t immortal,
115+
bool captureCurrentVoucher = true)
104116
: HeapObject(metadata, immortal), Flags(flags), ResumeTask(invoke) {
117+
if (captureCurrentVoucher)
118+
Voucher = voucher_copy();
105119
assert(isAsyncTask() && "wrong constructor for a non-task job");
106120
}
107121

122+
~Job() { swift_voucher_release(Voucher); }
123+
108124
bool isAsyncTask() const {
109125
return Flags.isAsyncTask();
110126
}
@@ -229,8 +245,9 @@ class AsyncTask : public Job {
229245
/// Private.initialize separately.
230246
AsyncTask(const HeapMetadata *metadata, JobFlags flags,
231247
TaskContinuationFunction *run,
232-
AsyncContext *initialContext)
233-
: Job(flags, run, metadata),
248+
AsyncContext *initialContext,
249+
bool captureCurrentVoucher)
250+
: Job(flags, run, metadata, captureCurrentVoucher),
234251
ResumeContext(initialContext) {
235252
assert(flags.isAsyncTask());
236253
Id = getNextTaskId();
@@ -243,8 +260,9 @@ class AsyncTask : public Job {
243260
AsyncTask(const HeapMetadata *metadata, InlineRefCounts::Immortal_t immortal,
244261
JobFlags flags,
245262
TaskContinuationFunction *run,
246-
AsyncContext *initialContext)
247-
: Job(flags, run, metadata, immortal),
263+
AsyncContext *initialContext,
264+
bool captureCurrentVoucher)
265+
: Job(flags, run, metadata, immortal, captureCurrentVoucher),
248266
ResumeContext(initialContext) {
249267
assert(flags.isAsyncTask());
250268
Id = getNextTaskId();

include/swift/Runtime/VoucherShims.h

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//===--- VoucherShims.h - Shims for OS vouchers --------------------*- C++ -*-//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Shims for interfacing with OS voucher calls.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef SWIFT_CONCURRENCY_VOUCHERSHIMS_H
18+
#define SWIFT_CONCURRENCY_VOUCHERSHIMS_H
19+
20+
#include "Config.h"
21+
22+
// swift-corelibs-libdispatch has os/voucher_private.h but it doesn't work for
23+
// us yet, so only look for it on Apple platforms.
24+
#if __APPLE__ && __has_include(<os/voucher_private.h>)
25+
#define SWIFT_HAS_VOUCHER_HEADER 1
26+
#include <os/voucher_private.h>
27+
#endif
28+
29+
// A "dead" voucher pointer, indicating that a voucher has been removed from
30+
// a Job, distinct from a NULL voucher that could just mean no voucher was
31+
// present. This allows us to catch problems like adopting a voucher from the
32+
// same Job twice without restoring it.
33+
#define SWIFT_DEAD_VOUCHER ((voucher_t)-1)
34+
35+
// The OS has voucher support if it has the header or if it has ObjC interop.
36+
#if SWIFT_HAS_VOUCHER_HEADER || SWIFT_OBJC_INTEROP
37+
#define SWIFT_HAS_VOUCHERS 1
38+
#endif
39+
40+
#if SWIFT_HAS_VOUCHERS
41+
42+
#if SWIFT_HAS_VOUCHER_HEADER
43+
44+
#else
45+
46+
// If the header isn't available, declare the necessary calls here.
47+
48+
#include <os/object.h>
49+
50+
#pragma clang diagnostic push
51+
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
52+
OS_OBJECT_DECL_CLASS(voucher);
53+
#pragma clang diagnostic pop
54+
55+
extern "C" voucher_t _Nullable voucher_copy(void);
56+
57+
// Consumes argument, returns retained value.
58+
extern "C" voucher_t _Nullable voucher_adopt(voucher_t _Nullable voucher);
59+
60+
#endif // __has_include(<os/voucher_private.h>)
61+
62+
static inline void swift_voucher_release(voucher_t _Nullable voucher) {
63+
// This NULL check isn't necessary, but NULL vouchers will be common, so
64+
// optimize for that.
65+
if (!voucher)
66+
return;
67+
if (voucher == SWIFT_DEAD_VOUCHER)
68+
return;
69+
os_release(voucher);
70+
}
71+
72+
#else // __APPLE__
73+
74+
// Declare some do-nothing stubs for OSes without voucher support.
75+
typedef void *voucher_t;
76+
static inline voucher_t _Nullable voucher_copy(void) { return nullptr; }
77+
static inline voucher_t _Nullable voucher_adopt(voucher_t _Nullable voucher) {
78+
return nullptr;
79+
}
80+
static inline void swift_voucher_release(voucher_t _Nullable voucher) {}
81+
#endif // __APPLE__
82+
83+
// Declare our own voucher_needs_adopt for when we don't get it from the SDK.
84+
// This declaration deliberately takes `void *` instead of `voucher_t`. When the
85+
// SDK provides one that takes `voucher_t`, then C++ overload resolution will
86+
// favor that one. When the SDK does not provide a declaration, then the call
87+
// site will invoke this stub instead.
88+
static inline bool voucher_needs_adopt(void * _Nullable voucher) {
89+
return true;
90+
}
91+
92+
static inline bool swift_voucher_needs_adopt(voucher_t _Nullable voucher) {
93+
#if __APPLE__
94+
if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *))
95+
return voucher_needs_adopt(voucher);
96+
return true;
97+
#else
98+
return voucher_needs_adopt(voucher);
99+
#endif
100+
}
101+
102+
#endif // SWIFT_CONCURRENCY_VOUCHERSHIMS_H

0 commit comments

Comments
 (0)