Skip to content

Commit 91e2246

Browse files
committed
quick-and-dirty implementation of MainActor in the runtime system
it is "dirty" in the sense that we don't have proper support for custom executors right now.
1 parent 10160cb commit 91e2246

File tree

6 files changed

+100
-23
lines changed

6 files changed

+100
-23
lines changed

include/swift/ABI/Executor.h

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,18 @@
1919

2020
#include <inttypes.h>
2121
#include "swift/ABI/HeapObject.h"
22+
#include "swift/Runtime/Casting.h"
2223

2324
namespace swift {
2425
class AsyncContext;
2526
class AsyncTask;
2627
class DefaultActor;
2728
class Job;
2829

30+
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
31+
SWIFT_EXPORT_FROM(swift_Concurrency)
32+
Metadata* MainActorMetadata;
33+
2934
/// An ExecutorRef isn't necessarily just a pointer to an executor
3035
/// object; it may have other bits set.
3136
class ExecutorRef {
@@ -45,6 +50,13 @@ class ExecutorRef {
4550
return ExecutorRef(0);
4651
}
4752

53+
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
54+
/// NOTE: I didn't go with Executor::forMainActor(DefaultActor*) because
55+
/// __swift_run_job_main_executor can't take more than one argument.
56+
constexpr static ExecutorRef mainExecutor() {
57+
return ExecutorRef(2);
58+
}
59+
4860
/// Given a pointer to a default actor, return an executor reference
4961
/// for it.
5062
static ExecutorRef forDefaultActor(DefaultActor *actor) {
@@ -57,6 +69,20 @@ class ExecutorRef {
5769
return Value == 0;
5870
}
5971

72+
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
73+
bool isMainExecutor() const {
74+
if (Value == ExecutorRef::mainExecutor().Value)
75+
return true;
76+
77+
HeapObject *heapObj = reinterpret_cast<HeapObject*>(Value & ~PointerMask);
78+
79+
if (heapObj == nullptr || MainActorMetadata == nullptr)
80+
return false;
81+
82+
Metadata const* metadata = swift_getObjectType(heapObj);
83+
return metadata == MainActorMetadata;
84+
}
85+
6086
/// Is this a default-actor executor reference?
6187
bool isDefaultActor() const {
6288
return Value & IsDefaultActor;
@@ -77,10 +103,12 @@ class ExecutorRef {
77103
}
78104

79105
bool operator==(ExecutorRef other) const {
80-
return Value == other.Value;
106+
return Value == other.Value
107+
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
108+
|| (isMainExecutor() && other.isMainExecutor());
81109
}
82110
bool operator!=(ExecutorRef other) const {
83-
return Value != other.Value;
111+
return !(*this == other);
84112
}
85113
};
86114

include/swift/Runtime/Concurrency.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,14 @@ void swift_task_enqueue(Job *job, ExecutorRef executor);
299299
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
300300
void swift_task_enqueueGlobal(Job *job);
301301

302+
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
303+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
304+
void swift_task_enqueueMainExecutor(Job *job);
305+
306+
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
307+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
308+
void swift_MainActor_register(HeapObject *actor);
309+
302310
/// A hook to take over global enqueuing.
303311
/// TODO: figure out a better abstraction plan than this.
304312
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)

stdlib/public/Concurrency/Actor.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "swift/Runtime/Concurrency.h"
1919

2020
#include "swift/Runtime/Atomic.h"
21+
#include "swift/Runtime/Casting.h"
2122
#include "swift/Runtime/Mutex.h"
2223
#include "swift/Runtime/ThreadLocal.h"
2324
#include "swift/ABI/Actor.h"
@@ -1223,6 +1224,18 @@ void swift::swift_defaultActor_enqueue(Job *job, DefaultActor *_actor) {
12231224
asImpl(_actor)->enqueue(job);
12241225
}
12251226

1227+
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
1228+
namespace swift {
1229+
Metadata* MainActorMetadata = nullptr;
1230+
}
1231+
1232+
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
1233+
void swift::swift_MainActor_register(HeapObject *actor) {
1234+
assert(actor);
1235+
MainActorMetadata = const_cast<Metadata*>(swift_getObjectType(actor));
1236+
assert(MainActorMetadata);
1237+
}
1238+
12261239
/*****************************************************************************/
12271240
/****************************** ACTOR SWITCHING ******************************/
12281241
/*****************************************************************************/
@@ -1355,6 +1368,10 @@ void swift::swift_task_enqueue(Job *job, ExecutorRef executor) {
13551368
if (executor.isGeneric())
13561369
return swift_task_enqueueGlobal(job);
13571370

1371+
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
1372+
if (executor.isMainExecutor())
1373+
return swift_task_enqueueMainExecutor(job);
1374+
13581375
if (executor.isDefaultActor())
13591376
return asImpl(executor.getDefaultActor())->enqueue(job);
13601377

stdlib/public/Concurrency/Actor.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,16 @@ public func _defaultActorDestroy(_ actor: AnyObject)
3939
public func _defaultActorEnqueue(partialTask: PartialAsyncTask,
4040
actor: AnyObject)
4141

42+
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
43+
@_silgen_name("swift_MainActor_register")
44+
fileprivate func _registerMainActor(actor: AnyObject)
45+
4246
/// A singleton actor whose executor is equivalent to
4347
/// \c DispatchQueue.main, which is the main dispatch queue.
4448
@globalActor public final class MainActor {
4549
public static let shared = _Impl()
4650

4751
public actor class _Impl {
48-
@actorIndependent
49-
public func enqueue(partialTask: PartialAsyncTask) {
50-
// TODO: implement this.
51-
_ = (nil as String?)! + "MainActor is not implemented yet."
52-
}
52+
init() { _registerMainActor(actor: self) }
5353
}
5454
}

stdlib/public/Concurrency/GlobalExecutor.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,15 @@ static void __swift_run_job(void *_job) {
114114
Job *job = (Job*) _job;
115115
job->run(ExecutorRef::generic());
116116
}
117+
118+
/// A specialized version of __swift_run_job to execute the job on the main
119+
/// executor.
120+
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
121+
static void __swift_run_job_main_executor(void *_job) {
122+
Job *job = (Job*) _job;
123+
job->run(ExecutorRef::mainExecutor());
124+
}
125+
117126
#endif
118127

119128
void swift::swift_task_enqueueGlobal(Job *job) {
@@ -167,3 +176,25 @@ void swift::swift_task_enqueueGlobal(Job *job) {
167176
dispatch_async_f(queue, dispatchContext, dispatchFunction);
168177
#endif
169178
}
179+
180+
181+
/// Enqueues a task on the main executor.
182+
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
183+
void swift::swift_task_enqueueMainExecutor(Job *job) {
184+
assert(job && "no job provided");
185+
186+
#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
187+
insertIntoJobQueue(job);
188+
#else
189+
190+
dispatch_function_t dispatchFunction = &__swift_run_job_main_executor;
191+
void *dispatchContext = job;
192+
193+
// TODO: cache this to avoid the extra call
194+
auto mainQueue = dispatch_get_main_queue();
195+
196+
dispatch_async_f(mainQueue, dispatchContext, dispatchFunction);
197+
198+
#endif
199+
200+
}

test/Concurrency/Runtime/mainactor.swift

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
// REQUIRES: executable_test
44
// REQUIRES: concurrency
55
// REQUIRES: libdispatch
6-
// REQUIRES: rdar72105129
7-
8-
// XFAIL: *
96

107
#if canImport(Darwin)
118
import Darwin
@@ -17,29 +14,25 @@ import Foundation
1714

1815
// CHECK: starting
1916
// CHECK-NOT: ERROR
20-
// CHECK: hello from main actor!
17+
// CHECK: launched
2118
// CHECK-NOT: ERROR
22-
// CHECK: ending
19+
// CHECK: hello from main actor!
2320

24-
@MainActor func helloMainActor() {
21+
@MainActor func helloMainActor() -> Never {
2522
if Thread.isMainThread {
2623
print("hello from main actor!")
24+
exit(EXIT_SUCCESS)
2725
} else {
2826
print("ERROR: not on correct thread!")
27+
exit(EXIT_FAILURE)
2928
}
3029
}
3130

3231
func someFunc() async {
3332
await helloMainActor()
3433
}
3534

36-
runAsyncAndBlock {
37-
print("starting")
38-
let handle = Task.runDetached(operation: someFunc)
39-
do {
40-
try await handle.get()
41-
} catch {
42-
print("ERROR: exception was thrown while waiting for task to complete")
43-
}
44-
print("ending")
45-
}
35+
print("starting")
36+
let _ = Task.runDetached(operation: someFunc)
37+
print("launched")
38+
dispatchMain() // give up the main queue.

0 commit comments

Comments
 (0)