Skip to content

Commit 1df455b

Browse files
committed
Allow building a concurrent libSwiftConcurrency without libdispatch
The goal here is not to eventually implement a concurrent thread pool ourselves. We're just making it easier for integrators who have their own pool and don't want to use Dispatch to build the Swift concurrency runtime. Just hook the right functions and you should be fine. The necessary functions to hook are: - swift_task_enqueueGlobal - swift_task_enqueueGlobalAfterDelay The following functions *would* be necessary to hook: - swift_task_enqueueMainExecutor - swift_task_asyncMainDrainQueue (only if you have an async main?) However, this configuration does not currently properly support the main executor, and so `@MainActor` should be avoided for now. rdar://83513751
1 parent 46063d3 commit 1df455b

File tree

7 files changed

+70
-25
lines changed

7 files changed

+70
-25
lines changed

include/swift/Runtime/Concurrency.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,22 @@
2525
#pragma clang diagnostic push
2626
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
2727

28+
// Does the runtime use a cooperative global executor?
29+
#if defined(SWIFT_STDLIB_SINGLE_THREADED_RUNTIME)
30+
#define SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR 1
31+
#else
32+
#define SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR 0
33+
#endif
34+
35+
// Does the runtime integrate with libdispatch?
36+
#ifndef SWIFT_CONCURRENCY_ENABLE_DISPATCH
37+
#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
38+
#define SWIFT_CONCURRENCY_ENABLE_DISPATCH 0
39+
#else
40+
#define SWIFT_CONCURRENCY_ENABLE_DISPATCH 1
41+
#endif
42+
#endif
43+
2844
namespace swift {
2945
class DefaultActor;
3046
class TaskOptionRecord;
@@ -661,10 +677,14 @@ void swift_task_enqueueGlobalWithDelay(unsigned long long delay, Job *job);
661677
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
662678
void swift_task_enqueueMainExecutor(Job *job);
663679

680+
#if SWIFT_CONCURRENCY_ENABLE_DISPATCH
681+
664682
/// Enqueue the given job on the main executor.
665683
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
666684
void swift_task_enqueueOnDispatchQueue(Job *job, HeapObject *queue);
667685

686+
#endif
687+
668688
/// A hook to take over global enqueuing.
669689
typedef SWIFT_CC(swift) void (*swift_task_enqueueGlobal_original)(Job *job);
670690
SWIFT_EXPORT_FROM(swift_Concurrency)
@@ -806,6 +826,17 @@ void swift_task_reportUnexpectedExecutor(
806826
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
807827
JobPriority swift_task_getCurrentThreadPriority(void);
808828

829+
#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
830+
831+
/// Donate this thread to the global executor until either the
832+
/// given condition returns true or we've run out of cooperative
833+
/// tasks to run.
834+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
835+
void swift_task_donateThreadToGlobalExecutorUntil(bool (*condition)(void*),
836+
void *context);
837+
838+
#endif
839+
809840
#ifdef __APPLE__
810841
/// A magic symbol whose address is the mask to apply to a frame pointer to
811842
/// signal that it is an async frame. Do not try to read the actual value of

stdlib/public/Concurrency/Actor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
#include "TaskPrivate.h"
4747
#include "VoucherSupport.h"
4848

49-
#if !SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
49+
#if SWIFT_CONCURRENCY_ENABLE_DISPATCH
5050
#include <dispatch/dispatch.h>
5151
#endif
5252

stdlib/public/Concurrency/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ endif()
3535
set(SWIFT_RUNTIME_CONCURRENCY_C_FLAGS)
3636
set(SWIFT_RUNTIME_CONCURRENCY_SWIFT_FLAGS)
3737

38+
if(NOT SWIFT_CONCURRENCY_USES_DISPATCH)
39+
list(APPEND SWIFT_RUNTIME_CONCURRENCY_C_FLAGS
40+
"-DSWIFT_CONCURRENCY_ENABLE_DISPATCH=0")
41+
endif()
42+
3843
if(NOT swift_concurrency_async_fp_mode)
3944
set(swift_concurrency_async_fp_mode "always")
4045
endif()

stdlib/public/Concurrency/GlobalExecutor.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
#include "TaskPrivate.h"
6060
#include "Error.h"
6161

62-
#if !SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
62+
#if SWIFT_CONCURRENCY_ENABLE_DISPATCH
6363
#include <dispatch/dispatch.h>
6464

6565
#if !defined(_WIN32)
@@ -178,15 +178,21 @@ static Job *claimNextFromJobQueue() {
178178
}
179179
}
180180

181-
void swift::donateThreadToGlobalExecutorUntil(bool (*condition)(void *),
182-
void *conditionContext) {
181+
void swift::
182+
swift_task_donateThreadToGlobalExecutorUntil(bool (*condition)(void *),
183+
void *conditionContext) {
183184
while (!condition(conditionContext)) {
184185
auto job = claimNextFromJobQueue();
185186
if (!job) return;
186187
swift_job_run(job, ExecutorRef::generic());
187188
}
188189
}
189190

191+
#elif !SWIFT_CONCURRENCY_ENABLE_DISPATCH
192+
193+
// No implementation. The expectation is that integrators in this
194+
// configuration will hook all the appropriate functions.
195+
190196
#else
191197

192198
// Ensure that Job's layout is compatible with what Dispatch expects.
@@ -330,6 +336,9 @@ static void swift_task_enqueueGlobalImpl(Job *job) {
330336

331337
#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
332338
insertIntoJobQueue(job);
339+
#elif !SWIFT_CONCURRENCY_ENABLE_DISPATCH
340+
swift_reportError(0, "operation unsupported without libdispatch: "
341+
"swift_task_enqueueGlobal");
333342
#else
334343
// We really want four things from the global execution service:
335344
// - Enqueuing work should have minimal runtime and memory overhead.
@@ -385,6 +394,9 @@ static void swift_task_enqueueGlobalWithDelayImpl(unsigned long long delay,
385394

386395
#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
387396
insertIntoDelayedJobQueue(delay, job);
397+
#elif !SWIFT_CONCURRENCY_ENABLE_DISPATCH
398+
swift_reportError(0, "operation unsupported without libdispatch: "
399+
"swift_task_enqueueGlobalWithDelay");
388400
#else
389401

390402
dispatch_function_t dispatchFunction = &__swift_run_job;
@@ -419,6 +431,9 @@ static void swift_task_enqueueMainExecutorImpl(Job *job) {
419431

420432
#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
421433
insertIntoJobQueue(job);
434+
#elif !SWIFT_CONCURRENCY_ENABLE_DISPATCH
435+
swift_reportError(0, "operation unsupported without libdispatch: "
436+
"swift_task_enqueueMainExecutor");
422437
#else
423438

424439
JobPriority priority = job->getPriority();
@@ -439,7 +454,7 @@ void swift::swift_task_enqueueMainExecutor(Job *job) {
439454
swift_task_enqueueMainExecutorImpl(job);
440455
}
441456

442-
#if !SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
457+
#if SWIFT_CONCURRENCY_ENABLE_DISPATCH
443458
void swift::swift_task_enqueueOnDispatchQueue(Job *job,
444459
HeapObject *_queue) {
445460
JobPriority priority = job->getPriority();
@@ -449,7 +464,8 @@ void swift::swift_task_enqueueOnDispatchQueue(Job *job,
449464
#endif
450465

451466
ExecutorRef swift::swift_task_getMainExecutor() {
452-
#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
467+
#if !SWIFT_CONCURRENCY_ENABLE_DISPATCH
468+
// FIXME: this isn't right for the non-cooperative environment
453469
return ExecutorRef::generic();
454470
#else
455471
return ExecutorRef::forOrdinary(
@@ -459,7 +475,8 @@ ExecutorRef swift::swift_task_getMainExecutor() {
459475
}
460476

461477
bool ExecutorRef::isMainExecutor() const {
462-
#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
478+
#if !SWIFT_CONCURRENCY_ENABLE_DISPATCH
479+
// FIXME: this isn't right for the non-cooperative environment
463480
return isGeneric();
464481
#else
465482
return Identity == reinterpret_cast<HeapObject*>(&_dispatch_main_q);

stdlib/public/Concurrency/Task.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
#include "Debug.h"
2828
#include "Error.h"
2929

30-
#if !SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
30+
#if SWIFT_CONCURRENCY_ENABLE_DISPATCH
3131
#include <dispatch/dispatch.h>
3232
#endif
3333

@@ -248,7 +248,7 @@ static void destroyTask(SWIFT_CONTEXT HeapObject *obj) {
248248
}
249249

250250
static ExecutorRef executorForEnqueuedJob(Job *job) {
251-
#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
251+
#if !SWIFT_CONCURRENCY_ENABLE_DISPATCH
252252
return ExecutorRef::generic();
253253
#else
254254
void *jobQueue = job->SchedulerPrivate[Job::DispatchQueueIndex];
@@ -1065,13 +1065,19 @@ void swift::swift_continuation_logFailedCheck(const char *message) {
10651065
swift_reportError(0, message);
10661066
}
10671067

1068+
SWIFT_RUNTIME_ATTRIBUTE_NORETURN
10681069
SWIFT_CC(swift)
10691070
static void swift_task_asyncMainDrainQueueImpl() {
10701071
#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
10711072
bool Finished = false;
1072-
donateThreadToGlobalExecutorUntil([](void *context) {
1073+
swift_task_donateThreadToGlobalExecutorUntil([](void *context) {
10731074
return *reinterpret_cast<bool*>(context);
10741075
}, &Finished);
1076+
#elif !SWIFT_CONCURRENCY_ENABLE_DISPATCH
1077+
// FIXME: consider implementing a concurrent global main queue for
1078+
// these environments?
1079+
swift_reportError(0, "operation unsupported without libdispatch: "
1080+
"swift_task_asyncMainDrainQueue");
10751081
#else
10761082
#if defined(_WIN32)
10771083
static void(FAR *pfndispatch_main)(void) = NULL;

stdlib/public/Concurrency/TaskGroup.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
#include "queue" // TODO: remove and replace with usage of our mpsc queue
3535
#include <atomic>
3636
#include <assert.h>
37-
#if !SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
37+
#if SWIFT_CONCURRENCY_ENABLE_DISPATCH
3838
#include <dispatch/dispatch.h>
3939
#endif
4040

stdlib/public/Concurrency/TaskPrivate.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -100,20 +100,6 @@ void asyncLet_addImpl(AsyncTask *task, AsyncLet *asyncLet,
100100
/// Clear the active task reference for the current thread.
101101
AsyncTask *_swift_task_clearCurrent();
102102

103-
#if defined(SWIFT_STDLIB_SINGLE_THREADED_RUNTIME)
104-
#define SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR 1
105-
#else
106-
#define SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR 0
107-
#endif
108-
109-
#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
110-
/// Donate this thread to the global executor until either the
111-
/// given condition returns true or we've run out of cooperative
112-
/// tasks to run.
113-
void donateThreadToGlobalExecutorUntil(bool (*condition)(void*),
114-
void *context);
115-
#endif
116-
117103
/// release() establishes a happens-before relation with a preceding acquire()
118104
/// on the same address.
119105
void _swift_tsan_acquire(void *addr);

0 commit comments

Comments
 (0)