Skip to content

Commit 9a1d18a

Browse files
authored
Merge pull request #38421 from gottesmm/track-task-is-running-with-exclusivity-runtime-fixes
[exclusivity] Add support for Task local exclusivity access sets.
2 parents ee6dca2 + dae605a commit 9a1d18a

File tree

12 files changed

+2538
-18
lines changed

12 files changed

+2538
-18
lines changed

include/swift/ABI/Task.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,10 @@ class AsyncTask : public Job {
279279
void flagAsSuspended();
280280
void flagAsSuspended_slow();
281281

282+
/// Flag that this task is now completed. This normally does not do anything
283+
/// but can be used to locally insert logging.
284+
void flagAsCompleted();
285+
282286
/// Check whether this task has been cancelled.
283287
/// Checking this is, of course, inherently race-prone on its own.
284288
bool isCancelled() const;

include/swift/Runtime/Exclusivity.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,30 @@ void swift_dumpTrackedAccesses();
9191

9292
#endif
9393

94+
/// Called when a task inits, resumes and returns control to caller synchronous
95+
/// code to update any exclusivity specific state associated with the task.
96+
///
97+
/// State is assumed to point to a buffer of memory with
98+
/// swift_task_threadLocalContextSize bytes that was initialized with
99+
/// swift_task_initThreadLocalContext.
100+
///
101+
/// We describe the algorithm in detail on SwiftTaskThreadLocalContext in
102+
/// Exclusivity.cpp.
103+
SWIFT_RUNTIME_EXPORT
104+
void swift_task_enterThreadLocalContext(char *state);
105+
106+
/// Called when a task suspends and returns control to caller synchronous code
107+
/// to update any exclusivity specific state associated with the task.
108+
///
109+
/// State is assumed to point to a buffer of memory with
110+
/// swift_task_threadLocalContextSize bytes that was initialized with
111+
/// swift_task_initThreadLocalContext.
112+
///
113+
/// We describe the algorithm in detail on SwiftTaskThreadLocalContext in
114+
/// Exclusivity.cpp.
115+
SWIFT_RUNTIME_EXPORT
116+
void swift_task_exitThreadLocalContext(char *state);
117+
94118
} // end namespace swift
95119

96120
#endif

stdlib/public/Concurrency/PartialAsyncTask.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,23 @@
1313
import Swift
1414
@_implementationOnly import _SwiftConcurrencyShims
1515

16+
@available(SwiftStdlib 5.5, *)
17+
@_silgen_name("swift_job_run")
18+
@usableFromInline
19+
internal func _swiftJobRun(_ job: UnownedJob,
20+
_ executor: UnownedSerialExecutor) -> ()
21+
1622
/// A job is a unit of scheduleable work.
1723
@available(SwiftStdlib 5.5, *)
1824
@frozen
1925
public struct UnownedJob {
2026
private var context: Builtin.Job
27+
28+
@_alwaysEmitIntoClient
29+
@inlinable
30+
public func _runSynchronously(on executor: UnownedSerialExecutor) {
31+
_swiftJobRun(self, executor)
32+
}
2133
}
2234

2335
@available(SwiftStdlib 5.5, *)

stdlib/public/Concurrency/Task.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ static void destroyJob(SWIFT_CONTEXT HeapObject *obj) {
225225
}
226226

227227
AsyncTask::~AsyncTask() {
228+
flagAsCompleted();
229+
228230
// For a future, destroy the result.
229231
if (isFuture()) {
230232
futureFragment()->destroy();

stdlib/public/Concurrency/TaskPrivate.h

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@
1717
#ifndef SWIFT_CONCURRENCY_TASKPRIVATE_H
1818
#define SWIFT_CONCURRENCY_TASKPRIVATE_H
1919

20-
#include "swift/Runtime/Concurrency.h"
21-
#include "swift/ABI/Task.h"
20+
#include "Error.h"
2221
#include "swift/ABI/Metadata.h"
22+
#include "swift/ABI/Task.h"
2323
#include "swift/Runtime/Atomic.h"
24-
#include "swift/Runtime/HeapObject.h"
24+
#include "swift/Runtime/Concurrency.h"
2525
#include "swift/Runtime/Error.h"
26-
#include "Error.h"
26+
#include "swift/Runtime/Exclusivity.h"
27+
#include "swift/Runtime/HeapObject.h"
2728

2829
#define SWIFT_FATAL_ERROR swift_Concurrency_fatalError
2930
#include "../runtime/StackAllocator.h"
@@ -282,14 +283,19 @@ struct AsyncTask::PrivateStorage {
282283
/// Currently one word.
283284
TaskLocal::Storage Local;
284285

286+
/// State inside the AsyncTask whose state is only managed by the exclusivity
287+
/// runtime in stdlibCore. We zero initialize to provide a safe initial value,
288+
/// but actually initialize its bit state to a const global provided by
289+
/// libswiftCore so that libswiftCore can control the layout of our initial
290+
/// state.
291+
uintptr_t ExclusivityAccessSet[2] = {0, 0};
292+
285293
PrivateStorage(JobFlags flags)
286-
: Status(ActiveTaskStatus(flags)),
287-
Local(TaskLocal::Storage()) {}
294+
: Status(ActiveTaskStatus(flags)), Local(TaskLocal::Storage()) {}
288295

289296
PrivateStorage(JobFlags flags, void *slab, size_t slabCapacity)
290-
: Status(ActiveTaskStatus(flags)),
291-
Allocator(slab, slabCapacity),
292-
Local(TaskLocal::Storage()) {}
297+
: Status(ActiveTaskStatus(flags)), Allocator(slab, slabCapacity),
298+
Local(TaskLocal::Storage()) {}
293299

294300
void complete(AsyncTask *task) {
295301
// Destroy and deallocate any remaining task local items.
@@ -347,7 +353,10 @@ inline void AsyncTask::flagAsRunning() {
347353
while (true) {
348354
assert(!oldStatus.isRunning());
349355
if (oldStatus.isLocked()) {
350-
return flagAsRunning_slow();
356+
flagAsRunning_slow();
357+
swift_task_enterThreadLocalContext(
358+
(char *)&_private().ExclusivityAccessSet[0]);
359+
return;
351360
}
352361

353362
auto newStatus = oldStatus.withRunning(true);
@@ -358,8 +367,11 @@ inline void AsyncTask::flagAsRunning() {
358367

359368
if (_private().Status.compare_exchange_weak(oldStatus, newStatus,
360369
std::memory_order_relaxed,
361-
std::memory_order_relaxed))
370+
std::memory_order_relaxed)) {
371+
swift_task_enterThreadLocalContext(
372+
(char *)&_private().ExclusivityAccessSet[0]);
362373
return;
374+
}
363375
}
364376
}
365377

@@ -368,7 +380,10 @@ inline void AsyncTask::flagAsSuspended() {
368380
while (true) {
369381
assert(oldStatus.isRunning());
370382
if (oldStatus.isLocked()) {
371-
return flagAsSuspended_slow();
383+
flagAsSuspended_slow();
384+
swift_task_exitThreadLocalContext(
385+
(char *)&_private().ExclusivityAccessSet[0]);
386+
return;
372387
}
373388

374389
auto newStatus = oldStatus.withRunning(false);
@@ -379,11 +394,24 @@ inline void AsyncTask::flagAsSuspended() {
379394

380395
if (_private().Status.compare_exchange_weak(oldStatus, newStatus,
381396
std::memory_order_relaxed,
382-
std::memory_order_relaxed))
397+
std::memory_order_relaxed)) {
398+
swift_task_exitThreadLocalContext(
399+
(char *)&_private().ExclusivityAccessSet[0]);
383400
return;
401+
}
384402
}
385403
}
386404

405+
// READ ME: This is not a dead function! Do not remove it! This is a function
406+
// that can be used when debugging locally to instrument when a task actually is
407+
// dealloced.
408+
inline void AsyncTask::flagAsCompleted() {
409+
#if SWIFT_TASK_PRINTF_DEBUG
410+
fprintf(stderr, "[%lu] task completed %p\n",
411+
_swift_get_thread_id(), this);
412+
#endif
413+
}
414+
387415
inline void AsyncTask::localValuePush(const HeapObject *key,
388416
/* +1 */ OpaqueValue *value,
389417
const Metadata *valueType) {

stdlib/public/runtime/EnvironmentVariables.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,11 @@ VARIABLE(SWIFT_DEBUG_ENABLE_SHARED_CACHE_PROTOCOL_CONFORMANCES, bool, true,
6262

6363
#endif
6464

65+
#ifndef NDEBUG
66+
67+
VARIABLE(SWIFT_DEBUG_RUNTIME_EXCLUSIVITY_LOGGING, bool, false,
68+
"Enable the an asserts runtime to emit logging as it works.")
69+
70+
#endif
71+
6572
#undef VARIABLE

0 commit comments

Comments
 (0)