Skip to content

Commit 158e170

Browse files
authored
Merge pull request #38598 from gottesmm/release/5.5/rdar80492364
[5.5][concurrency] Implement support for Task local exclusivity access sets.
2 parents a94b2c1 + cdac1ea commit 158e170

File tree

12 files changed

+2540
-17
lines changed

12 files changed

+2540
-17
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,6 +13,12 @@
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 unit of scheduleable work.
1723
///
1824
/// Unless you're implementing a scheduler,
@@ -21,6 +27,12 @@ import Swift
2127
@frozen
2228
public struct UnownedJob {
2329
private var context: Builtin.Job
30+
31+
@_alwaysEmitIntoClient
32+
@inlinable
33+
public func _runSynchronously(on executor: UnownedSerialExecutor) {
34+
_swiftJobRun(self, executor)
35+
}
2436
}
2537

2638
/// A mechanism to interface

stdlib/public/Concurrency/Task.cpp

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

221221
AsyncTask::~AsyncTask() {
222+
flagAsCompleted();
223+
222224
// For a future, destroy the result.
223225
if (isFuture()) {
224226
futureFragment()->destroy();

stdlib/public/Concurrency/TaskPrivate.h

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +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"
2220
#include "swift/ABI/Metadata.h"
21+
#include "swift/ABI/Task.h"
2322
#include "swift/Runtime/Atomic.h"
24-
#include "swift/Runtime/HeapObject.h"
23+
#include "swift/Runtime/Concurrency.h"
2524
#include "swift/Runtime/Error.h"
25+
#include "swift/Runtime/Exclusivity.h"
26+
#include "swift/Runtime/HeapObject.h"
27+
2628
#include "../runtime/StackAllocator.h"
2729

2830
namespace swift {
@@ -255,14 +257,19 @@ struct AsyncTask::PrivateStorage {
255257
/// Currently one word.
256258
TaskLocal::Storage Local;
257259

260+
/// State inside the AsyncTask whose state is only managed by the exclusivity
261+
/// runtime in stdlibCore. We zero initialize to provide a safe initial value,
262+
/// but actually initialize its bit state to a const global provided by
263+
/// libswiftCore so that libswiftCore can control the layout of our initial
264+
/// state.
265+
uintptr_t ExclusivityAccessSet[2] = {0, 0};
266+
258267
PrivateStorage(JobFlags flags)
259-
: Status(ActiveTaskStatus(flags)),
260-
Local(TaskLocal::Storage()) {}
268+
: Status(ActiveTaskStatus(flags)), Local(TaskLocal::Storage()) {}
261269

262270
PrivateStorage(JobFlags flags, void *slab, size_t slabCapacity)
263-
: Status(ActiveTaskStatus(flags)),
264-
Allocator(slab, slabCapacity),
265-
Local(TaskLocal::Storage()) {}
271+
: Status(ActiveTaskStatus(flags)), Allocator(slab, slabCapacity),
272+
Local(TaskLocal::Storage()) {}
266273

267274
void complete(AsyncTask *task) {
268275
// Destroy and deallocate any remaining task local items.
@@ -320,7 +327,10 @@ inline void AsyncTask::flagAsRunning() {
320327
while (true) {
321328
assert(!oldStatus.isRunning());
322329
if (oldStatus.isLocked()) {
323-
return flagAsRunning_slow();
330+
flagAsRunning_slow();
331+
swift_task_enterThreadLocalContext(
332+
(char *)&_private().ExclusivityAccessSet[0]);
333+
return;
324334
}
325335

326336
auto newStatus = oldStatus.withRunning(true);
@@ -331,8 +341,11 @@ inline void AsyncTask::flagAsRunning() {
331341

332342
if (_private().Status.compare_exchange_weak(oldStatus, newStatus,
333343
std::memory_order_relaxed,
334-
std::memory_order_relaxed))
344+
std::memory_order_relaxed)) {
345+
swift_task_enterThreadLocalContext(
346+
(char *)&_private().ExclusivityAccessSet[0]);
335347
return;
348+
}
336349
}
337350
}
338351

@@ -341,7 +354,10 @@ inline void AsyncTask::flagAsSuspended() {
341354
while (true) {
342355
assert(oldStatus.isRunning());
343356
if (oldStatus.isLocked()) {
344-
return flagAsSuspended_slow();
357+
flagAsSuspended_slow();
358+
swift_task_exitThreadLocalContext(
359+
(char *)&_private().ExclusivityAccessSet[0]);
360+
return;
345361
}
346362

347363
auto newStatus = oldStatus.withRunning(false);
@@ -352,11 +368,24 @@ inline void AsyncTask::flagAsSuspended() {
352368

353369
if (_private().Status.compare_exchange_weak(oldStatus, newStatus,
354370
std::memory_order_relaxed,
355-
std::memory_order_relaxed))
371+
std::memory_order_relaxed)) {
372+
swift_task_exitThreadLocalContext(
373+
(char *)&_private().ExclusivityAccessSet[0]);
356374
return;
375+
}
357376
}
358377
}
359378

379+
// READ ME: This is not a dead function! Do not remove it! This is a function
380+
// that can be used when debugging locally to instrument when a task actually is
381+
// dealloced.
382+
inline void AsyncTask::flagAsCompleted() {
383+
#if SWIFT_TASK_PRINTF_DEBUG
384+
fprintf(stderr, "[%lu] task completed %p\n",
385+
_swift_get_thread_id(), this);
386+
#endif
387+
}
388+
360389
inline void AsyncTask::localValuePush(const HeapObject *key,
361390
/* +1 */ OpaqueValue *value,
362391
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)