Skip to content

Commit bbda706

Browse files
committed
[Concurrency] Add a unique Task ID to AsyncTask
This commit changes JobFlags storage to be 32bits, but leaves the runtime API expressed in terms of size_t. This allows us to pack an Id in the 32bits we freed up. The offset of this Id in the AsyncTask is an ABI constant. This way introspection tools can extract the currently running task identifier without any need for special APIs.
1 parent 420afdc commit bbda706

File tree

15 files changed

+67
-43
lines changed

15 files changed

+67
-43
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2000,7 +2000,7 @@ enum class JobPriority : size_t {
20002000
};
20012001

20022002
/// Flags for schedulable jobs.
2003-
class JobFlags : public FlagSet<size_t> {
2003+
class JobFlags : public FlagSet<uint32_t> {
20042004
public:
20052005
enum {
20062006
Kind = 0,
@@ -2019,7 +2019,7 @@ class JobFlags : public FlagSet<size_t> {
20192019
Task_IsContinuingAsyncTask = 27,
20202020
};
20212021

2022-
explicit JobFlags(size_t bits) : FlagSet(bits) {}
2022+
explicit JobFlags(uint32_t bits) : FlagSet(bits) {}
20232023
JobFlags(JobKind kind) { setKind(kind); }
20242024
JobFlags(JobKind kind, JobPriority priority) {
20252025
setKind(kind);

include/swift/ABI/Task.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ class alignas(2 * alignof(void*)) Job :
6868

6969
JobFlags Flags;
7070

71+
// Derived classes can use this to store a Job Id.
72+
uint32_t Id = 0;
73+
7174
// We use this union to avoid having to do a second indirect branch
7275
// when resuming an asynchronous task, which we expect will be the
7376
// common case.
@@ -121,8 +124,13 @@ class alignas(2 * alignof(void*)) Job :
121124
};
122125

123126
// The compiler will eventually assume these.
127+
#if defined(__LP64__) || defined(_WIN64)
124128
static_assert(sizeof(Job) == 6 * sizeof(void*),
125129
"Job size is wrong");
130+
#else
131+
static_assert(sizeof(Job) == 8 * sizeof(void*),
132+
"Job size is wrong");
133+
#endif
126134
static_assert(alignof(Job) == 2 * alignof(void*),
127135
"Job alignment is wrong");
128136

@@ -229,6 +237,7 @@ class AsyncTask : public Job {
229237
Status(ActiveTaskStatus()),
230238
Local(TaskLocal::Storage()) {
231239
assert(flags.isAsyncTask());
240+
Id = getNextTaskId();
232241
}
233242

234243
/// Create a task with "immortal" reference counts.
@@ -242,6 +251,7 @@ class AsyncTask : public Job {
242251
Status(ActiveTaskStatus()),
243252
Local(TaskLocal::Storage()) {
244253
assert(flags.isAsyncTask());
254+
Id = getNextTaskId();
245255
}
246256

247257
~AsyncTask();
@@ -505,13 +515,24 @@ class AsyncTask : public Job {
505515
return reinterpret_cast<AsyncTask *&>(
506516
SchedulerPrivate[NextWaitingTaskIndex]);
507517
}
518+
519+
/// Get the next non-zero Task ID.
520+
uint32_t getNextTaskId() {
521+
static std::atomic<uint32_t> Id(1);
522+
uint32_t Next = Id.fetch_add(1, std::memory_order_relaxed);
523+
if (Next == 0) Next = Id.fetch_add(1, std::memory_order_relaxed);
524+
return Next;
525+
}
508526
};
509527

510528
// The compiler will eventually assume these.
511529
static_assert(sizeof(AsyncTask) == 14 * sizeof(void*),
512530
"AsyncTask size is wrong");
513531
static_assert(alignof(AsyncTask) == 2 * alignof(void*),
514532
"AsyncTask alignment is wrong");
533+
// Libc hardcodes this offset to extract the TaskID
534+
static_assert(offsetof(AsyncTask, Id) == 4 * sizeof(void *) + 4,
535+
"AsyncTask::Id offset is wrong");
515536

516537
inline void Job::runInFullyEstablishedContext() {
517538
if (auto task = dyn_cast<AsyncTask>(this))

include/swift/Runtime/Concurrency.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ struct AsyncTaskAndContext {
3838
/// Create a task object with no future which will run the given
3939
/// function.
4040
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
41-
AsyncTaskAndContext swift_task_create_f(JobFlags flags,
41+
AsyncTaskAndContext swift_task_create_f(size_t flags,
4242
ThinNullaryAsyncSignature::FunctionType *function,
4343
size_t initialContextSize);
4444

@@ -51,7 +51,7 @@ using FutureAsyncSignature =
5151
/// closure.
5252
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
5353
AsyncTaskAndContext swift_task_create_future(
54-
JobFlags flags,
54+
size_t flags,
5555
const Metadata *futureResultType,
5656
void *closureEntryPoint,
5757
HeapObject * /* +1 */ closureContext);
@@ -60,7 +60,7 @@ AsyncTaskAndContext swift_task_create_future(
6060
/// function.
6161
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
6262
AsyncTaskAndContext swift_task_create_future_f(
63-
JobFlags flags,
63+
size_t flags,
6464
const Metadata *futureResultType,
6565
FutureAsyncSignature::FunctionType *function,
6666
size_t initialContextSize);
@@ -69,7 +69,7 @@ AsyncTaskAndContext swift_task_create_future_f(
6969
/// closure, and offer its result to the task group
7070
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
7171
AsyncTaskAndContext swift_task_create_group_future(
72-
JobFlags flags, TaskGroup *group,
72+
size_t flags, TaskGroup *group,
7373
const Metadata *futureResultType,
7474
void *closureEntryPoint,
7575
HeapObject * /* +1 */ closureContext);
@@ -78,7 +78,7 @@ AsyncTaskAndContext swift_task_create_group_future(
7878
/// function, and offer its result to the task group
7979
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
8080
AsyncTaskAndContext swift_task_create_group_future_f(
81-
JobFlags flags,
81+
size_t flags,
8282
TaskGroup *group,
8383
const Metadata *futureResultType,
8484
FutureAsyncSignature::FunctionType *function,

lib/IRGen/IRGenFunction.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -527,8 +527,8 @@ static llvm::Value *unsafeContinuationFromTask(IRGenFunction &IGF,
527527
static llvm::Value *emitLoadOfResumeContextFromTask(IRGenFunction &IGF,
528528
llvm::Value *task) {
529529
auto addr = Address(task, IGF.IGM.getPointerAlignment());
530-
auto resumeContextAddr =
531-
IGF.Builder.CreateStructGEP(addr, 5, 6 * IGF.IGM.getPointerSize());
530+
auto resumeContextAddr = IGF.Builder.CreateStructGEP(
531+
addr, 6, (5 * IGF.IGM.getPointerSize()) + Size(8));
532532
llvm::Value *resumeContext = IGF.Builder.CreateLoad(resumeContextAddr);
533533
if (auto &schema = IGF.getOptions().PointerAuth.TaskResumeContext) {
534534
auto info = PointerAuthInfo::emit(IGF, schema,

lib/IRGen/IRGenModule.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,8 @@ IRGenModule::IRGenModule(IRGenerator &irgen,
610610
SwiftTaskTy = createStructType(*this, "swift.task", {
611611
RefCountedStructTy, // object header
612612
Int8PtrTy, Int8PtrTy, // Job.SchedulerPrivate
613-
SizeTy, // Job.Flags
613+
Int32Ty, // Job.Flags
614+
Int32Ty, // Job.ID
614615
FunctionPtrTy, // Job.RunJob/Job.ResumeTask
615616
SwiftContextPtrTy, // Task.ResumeContext
616617
IntPtrTy // Task.Status
@@ -629,7 +630,8 @@ IRGenModule::IRGenModule(IRGenerator &irgen,
629630
SwiftJobTy = createStructType(*this, "swift.job", {
630631
RefCountedStructTy, // object header
631632
Int8PtrTy, Int8PtrTy, // SchedulerPrivate
632-
SizeTy, // flags
633+
Int32Ty, // flags
634+
Int32Ty, // ID
633635
FunctionPtrTy, // RunJob/ResumeTask
634636
});
635637
SwiftJobPtrTy = SwiftJobTy->getPointerTo(DefaultAS);

stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ OVERRIDE_ACTOR(task_switch, void,
9797
(resumeToContext, resumeFunction, newExecutor))
9898

9999
OVERRIDE_TASK(task_create_group_future_common, AsyncTaskAndContext, , , ,
100-
(JobFlags flags, TaskGroup *group,
100+
(size_t flags, TaskGroup *group,
101101
const Metadata *futureResultType,
102102
FutureAsyncSignature::FunctionType *function,
103103
void *closureContext, bool isAsyncLetTask,

stdlib/public/Concurrency/AsyncLet.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ static void swift_asyncLet_startImpl(AsyncLet *alet,
106106
flags.task_setIsChildTask(true);
107107

108108
auto childTaskAndContext = swift_task_create_async_let_future(
109-
flags,
109+
flags.getOpaqueValue(),
110110
futureResultType,
111111
closureEntryPoint,
112112
closureContext);

stdlib/public/Concurrency/Task.cpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -386,11 +386,12 @@ static void task_wait_throwing_resume_adapter(SWIFT_ASYNC_CONTEXT AsyncContext *
386386
/// Also, async-let tasks are not heap allcoated, but allcoated with the parent
387387
/// task's stack allocator.
388388
static AsyncTaskAndContext swift_task_create_group_future_commonImpl(
389-
JobFlags flags, TaskGroup *group,
389+
size_t rawFlags, TaskGroup *group,
390390
const Metadata *futureResultType,
391391
FutureAsyncSignature::FunctionType *function,
392392
void *closureContext, bool isAsyncLetTask,
393393
size_t initialContextSize) {
394+
JobFlags flags(rawFlags);
394395
assert((futureResultType != nullptr) == flags.task_isFuture());
395396
assert(!flags.task_isFuture() ||
396397
initialContextSize >= sizeof(FutureAsyncContext));
@@ -568,32 +569,32 @@ static AsyncTaskAndContext swift_task_create_group_future_commonImpl(
568569
}
569570

570571
static AsyncTaskAndContext swift_task_create_group_future_common(
571-
JobFlags flags, TaskGroup *group, const Metadata *futureResultType,
572+
size_t flags, TaskGroup *group, const Metadata *futureResultType,
572573
FutureAsyncSignature::FunctionType *function,
573574
void *closureContext, bool isAsyncLetTask,
574575
size_t initialContextSize);
575576

576577
AsyncTaskAndContext
577-
swift::swift_task_create_f(JobFlags flags,
578+
swift::swift_task_create_f(size_t flags,
578579
ThinNullaryAsyncSignature::FunctionType *function,
579580
size_t initialContextSize) {
580581
return swift_task_create_future_f(
581582
flags, nullptr, function, initialContextSize);
582583
}
583584

584585
AsyncTaskAndContext swift::swift_task_create_future_f(
585-
JobFlags flags,
586+
size_t flags,
586587
const Metadata *futureResultType,
587588
FutureAsyncSignature::FunctionType *function, size_t initialContextSize) {
588-
assert(!flags.task_isGroupChildTask() &&
589+
assert(!JobFlags(flags).task_isGroupChildTask() &&
589590
"use swift_task_create_group_future_f to initialize task group child tasks");
590591
return swift_task_create_group_future_f(
591592
flags, /*group=*/nullptr, futureResultType,
592593
function, initialContextSize);
593594
}
594595

595596
AsyncTaskAndContext swift::swift_task_create_group_future_f(
596-
JobFlags flags, TaskGroup *group,
597+
size_t flags, TaskGroup *group,
597598
const Metadata *futureResultType,
598599
FutureAsyncSignature::FunctionType *function,
599600
size_t initialContextSize) {
@@ -621,7 +622,7 @@ getAsyncClosureEntryPointAndContextSize(void *function,
621622
fnPtr->ExpectedContextSize};
622623
}
623624

624-
AsyncTaskAndContext swift::swift_task_create_future(JobFlags flags,
625+
AsyncTaskAndContext swift::swift_task_create_future(size_t flags,
625626
const Metadata *futureResultType,
626627
void *closureEntry,
627628
HeapObject * /* +1 */ closureContext) {
@@ -640,7 +641,7 @@ AsyncTaskAndContext swift::swift_task_create_future(JobFlags flags,
640641
initialContextSize);
641642
}
642643

643-
AsyncTaskAndContext swift::swift_task_create_async_let_future(JobFlags flags,
644+
AsyncTaskAndContext swift::swift_task_create_async_let_future(size_t flags,
644645
const Metadata *futureResultType,
645646
void *closureEntry,
646647
void *closureContext) {
@@ -661,7 +662,7 @@ AsyncTaskAndContext swift::swift_task_create_async_let_future(JobFlags flags,
661662

662663
AsyncTaskAndContext
663664
swift::swift_task_create_group_future(
664-
JobFlags flags, TaskGroup *group,
665+
size_t flags, TaskGroup *group,
665666
const Metadata *futureResultType,
666667
void *closureEntry,
667668
HeapObject * /*+1*/closureContext) {
@@ -853,7 +854,7 @@ void swift::swift_task_runAndBlockThread(const void *function,
853854
// Set up a task that runs the runAndBlock async function above.
854855
auto flags = JobFlags(JobKind::Task, JobPriority::Default);
855856
auto pair = swift_task_create_f(
856-
flags,
857+
flags.getOpaqueValue(),
857858
reinterpret_cast<ThinNullaryAsyncSignature::FunctionType *>(
858859
&runAndBlock_start),
859860
sizeof(RunAndBlockContext));

stdlib/public/Concurrency/Task.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -253,12 +253,12 @@ extension Task {
253253
/// This is a port of the C++ FlagSet.
254254
struct JobFlags {
255255
/// Kinds of schedulable jobs.
256-
enum Kind: Int {
256+
enum Kind: Int32 {
257257
case task = 0
258258
}
259259

260260
/// The actual bit representation of these flags.
261-
var bits: Int = 0
261+
var bits: Int32 = 0
262262

263263
/// The kind of job described by these flags.
264264
var kind: Kind {
@@ -277,11 +277,11 @@ extension Task {
277277
/// The priority given to the job.
278278
var priority: Priority? {
279279
get {
280-
Priority(rawValue: (bits & 0xFF00) >> 8)
280+
Priority(rawValue: (Int(bits) & 0xFF00) >> 8)
281281
}
282282

283283
set {
284-
bits = (bits & ~0xFF00) | ((newValue?.rawValue ?? 0) << 8)
284+
bits = (bits & ~0xFF00) | Int32((newValue?.rawValue ?? 0) << 8)
285285
}
286286
}
287287

@@ -410,7 +410,7 @@ public func detach<T>(
410410
flags.isFuture = true
411411

412412
// Create the asynchronous task future.
413-
let (task, _) = Builtin.createAsyncTaskFuture(flags.bits, operation)
413+
let (task, _) = Builtin.createAsyncTaskFuture(Int(flags.bits), operation)
414414

415415
// Enqueue the resulting job.
416416
_enqueueJobGlobal(Builtin.convertTaskToJob(task))
@@ -463,7 +463,7 @@ public func detach<T>(
463463
flags.isFuture = true
464464

465465
// Create the asynchronous task future.
466-
let (task, _) = Builtin.createAsyncTaskFuture(flags.bits, operation)
466+
let (task, _) = Builtin.createAsyncTaskFuture(Int(flags.bits), operation)
467467

468468
// Enqueue the resulting job.
469469
_enqueueJobGlobal(Builtin.convertTaskToJob(task))
@@ -533,7 +533,7 @@ public func async<T>(
533533
flags.isContinuingAsyncTask = true
534534

535535
// Create the asynchronous task future.
536-
let (task, _) = Builtin.createAsyncTaskFuture(flags.bits, operation)
536+
let (task, _) = Builtin.createAsyncTaskFuture(Int(flags.bits), operation)
537537

538538
// Copy all task locals to the newly created task.
539539
// We must copy them rather than point to the current task since the new task
@@ -578,7 +578,7 @@ public func async<T>(
578578
flags.isContinuingAsyncTask = true
579579

580580
// Create the asynchronous task future.
581-
let (task, _) = Builtin.createAsyncTaskFuture(flags.bits, operation)
581+
let (task, _) = Builtin.createAsyncTaskFuture(Int(flags.bits), operation)
582582

583583
// Enqueue the resulting job.
584584
_enqueueJobGlobal(Builtin.convertTaskToJob(task))

stdlib/public/Concurrency/TaskGroup.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ public struct TaskGroup<ChildTaskResult> {
269269

270270
// Create the asynchronous task future.
271271
let (childTask, _) = Builtin.createAsyncTaskGroupFuture(
272-
flags.bits, _group, operation)
272+
Int(flags.bits), _group, operation)
273273

274274
// Attach it to the group's task record in the current task.
275275
_taskGroupAttachChild(group: _group, child: childTask)
@@ -332,7 +332,7 @@ public struct TaskGroup<ChildTaskResult> {
332332

333333
// Create the asynchronous task future.
334334
let (childTask, _) = Builtin.createAsyncTaskGroupFuture(
335-
flags.bits, _group, operation)
335+
Int(flags.bits), _group, operation)
336336

337337
// Attach it to the group's task record in the current task.
338338
_taskGroupAttachChild(group: _group, child: childTask)
@@ -558,7 +558,7 @@ public struct ThrowingTaskGroup<ChildTaskResult, Failure: Error> {
558558

559559
// Create the asynchronous task future.
560560
let (childTask, _) = Builtin.createAsyncTaskGroupFuture(
561-
flags.bits, _group, operation)
561+
Int(flags.bits), _group, operation)
562562

563563
// Attach it to the group's task record in the current task.
564564
_taskGroupAttachChild(group: _group, child: childTask)
@@ -621,7 +621,7 @@ public struct ThrowingTaskGroup<ChildTaskResult, Failure: Error> {
621621

622622
// Create the asynchronous task future.
623623
let (childTask, _) = Builtin.createAsyncTaskGroupFuture(
624-
flags.bits, _group, operation)
624+
Int(flags.bits), _group, operation)
625625

626626
// Attach it to the group's task record in the current task.
627627
_taskGroupAttachChild(group: _group, child: childTask)

stdlib/public/Concurrency/TaskPrivate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ void runJobInEstablishedExecutorContext(Job *job);
5959
/// Clear the active task reference for the current thread.
6060
AsyncTask *_swift_task_clearCurrent();
6161

62-
AsyncTaskAndContext swift_task_create_async_let_future(JobFlags flags,
62+
AsyncTaskAndContext swift_task_create_async_let_future(size_t flags,
6363
const Metadata *futureResultType,
6464
void *closureEntry,
6565
void *closureContext);

test/IRGen/async/builtins.sil

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ bb0(%0 : $Int, %1: @guaranteed $@async @callee_owned @substituted <τ_0_0> () ->
6363
sil hidden [ossa] @resume_nonthrowing_continuation : $(@in Builtin.NativeObject, Builtin.RawUnsafeContinuation) -> () {
6464
bb0(%0 : $*Builtin.NativeObject, %1 : $Builtin.RawUnsafeContinuation):
6565
// CHECK: [[CONT:%.*]] = bitcast i8* %1 to %swift.task*
66-
// CHECK-NEXT: [[CONTEXT_SLOT:%.*]] = getelementptr inbounds %swift.task, %swift.task* [[CONT]], i32 0, i32 5
66+
// CHECK-NEXT: [[CONTEXT_SLOT:%.*]] = getelementptr inbounds %swift.task, %swift.task* [[CONT]], i32 0, i32 6
6767
// CHECK-NEXT: [[CONTEXT_OPAQUE:%.*]] = load %swift.context*, %swift.context** [[CONTEXT_SLOT]], align
6868
// CHECK-NEXT: [[CONTEXT:%.*]] = bitcast %swift.context* [[CONTEXT_OPAQUE]] to %swift.continuation_context*
6969
// CHECK-NEXT: [[RESULT_ADDR_SLOT:%.*]] = getelementptr inbounds %swift.continuation_context, %swift.continuation_context* [[CONTEXT]], i32 0, i32 3
@@ -82,7 +82,7 @@ bb0(%0 : $*Builtin.NativeObject, %1 : $Builtin.RawUnsafeContinuation):
8282
sil hidden [ossa] @resume_throwing_continuation : $(@in Builtin.NativeObject, Builtin.RawUnsafeContinuation) -> () {
8383
bb0(%0 : $*Builtin.NativeObject, %1 : $Builtin.RawUnsafeContinuation):
8484
// CHECK: [[CONT:%.*]] = bitcast i8* %1 to %swift.task*
85-
// CHECK-NEXT: [[CONTEXT_SLOT:%.*]] = getelementptr inbounds %swift.task, %swift.task* [[CONT]], i32 0, i32 5
85+
// CHECK-NEXT: [[CONTEXT_SLOT:%.*]] = getelementptr inbounds %swift.task, %swift.task* [[CONT]], i32 0, i32 6
8686
// CHECK-NEXT: [[CONTEXT_OPAQUE:%.*]] = load %swift.context*, %swift.context** [[CONTEXT_SLOT]], align
8787
// CHECK-NEXT: [[CONTEXT:%.*]] = bitcast %swift.context* [[CONTEXT_OPAQUE]] to %swift.continuation_context*
8888
// CHECK-NEXT: [[RESULT_ADDR_SLOT:%.*]] = getelementptr inbounds %swift.continuation_context, %swift.continuation_context* [[CONTEXT]], i32 0, i32 3

0 commit comments

Comments
 (0)