Skip to content

Commit f7fbc7b

Browse files
authored
Merge pull request swiftlang#35330 from kavon/quick-n-dirty-mainactor
2 parents fa7c53b + dc218bb commit f7fbc7b

File tree

6 files changed

+178
-29
lines changed

6 files changed

+178
-29
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+
}
Lines changed: 87 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,110 @@
1-
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch) | %FileCheck %s
1+
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency) | %FileCheck %s
22

33
// REQUIRES: executable_test
44
// REQUIRES: concurrency
5-
// REQUIRES: libdispatch
6-
// REQUIRES: rdar72105129
75

8-
// XFAIL: *
6+
// REQUIRES: OS=macosx || OS=ios
7+
// FIXME: should not require Darwin to run this test once we have async main!
98

9+
// for exit(:Int)
1010
#if canImport(Darwin)
1111
import Darwin
1212
#elseif canImport(Glibc)
1313
import Glibc
1414
#endif
1515

16-
import Foundation
16+
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
17+
import Dispatch
18+
#endif
1719

18-
// CHECK: starting
19-
// CHECK-NOT: ERROR
20-
// CHECK: hello from main actor!
21-
// CHECK-NOT: ERROR
22-
// CHECK: ending
20+
/// @returns true iff the expected answer is actually the case, i.e., correct.
21+
/// If the current queue does not match expectations, this function may return
22+
/// false or just crash the program with non-zero exit code, depending on SDK.
23+
func checkIfMainQueue(expectedAnswer expected: Bool) -> Bool {
24+
// FIXME: until we start using dispatch on Linux, we only check
25+
// which queue we're on with Darwin platforms.
26+
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
27+
if #available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *) {
28+
dispatchPrecondition(condition: expected ? .onQueue(DispatchQueue.main)
29+
: .notOnQueue(DispatchQueue.main))
30+
31+
}
32+
#endif
33+
return true
34+
}
35+
36+
actor class A {
37+
func onCorrectQueue() -> Bool {
38+
if checkIfMainQueue(expectedAnswer: false) {
39+
print("on actor instance's queue")
40+
return true
41+
}
42+
print("ERROR: not on actor instance's queue")
43+
return false
44+
}
45+
}
46+
47+
@MainActor func exitTest(success: Bool) -> Never {
48+
if !success {
49+
exit(EXIT_FAILURE)
50+
}
51+
52+
if checkIfMainQueue(expectedAnswer: true) {
53+
print("on main queue again!")
54+
} else {
55+
print("ERROR: left the main queue?")
56+
}
57+
58+
exit(EXIT_SUCCESS)
59+
}
2360

24-
@MainActor func helloMainActor() {
25-
if Thread.isMainThread {
61+
@MainActor func enterMainActor() async -> Never {
62+
var ok = checkIfMainQueue(expectedAnswer: true)
63+
if ok {
2664
print("hello from main actor!")
2765
} else {
28-
print("ERROR: not on correct thread!")
66+
print("ERROR: not on correct queue!")
2967
}
68+
69+
// try calling a function on another actor.
70+
let someActor = A()
71+
let successfulActorSwitch = await someActor.onCorrectQueue()
72+
ok = ok && successfulActorSwitch
73+
74+
exitTest(success: ok)
3075
}
3176

3277
func someFunc() async {
33-
await helloMainActor()
78+
guard checkIfMainQueue(expectedAnswer: false) else {
79+
print("ERROR: did not expect detatched task to run on main queue!")
80+
exit(EXIT_FAILURE)
81+
}
82+
await enterMainActor()
3483
}
3584

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")
85+
86+
// CHECK: starting
87+
// CHECK-NOT: ERROR
88+
// CHECK: hello from main actor!
89+
// CHECK-NOT: ERROR
90+
// CHECK: on actor instance's queue
91+
// CHECK-NOT: ERROR
92+
// CHECK: on main queue again!
93+
94+
import CoreFoundation
95+
96+
print("starting")
97+
Task.runDetached(operation: someFunc)
98+
CFRunLoopRun()
99+
100+
// FIXME: remove the use of CFRunLoopRun and the CoreFoundation import
101+
// in favor of the below once we have async main support.
102+
// don't forget to add -parse-as-library to the RUN line
103+
/*
104+
@main struct RunIt {
105+
static func main() async {
106+
print("starting")
107+
await someFunc()
43108
}
44-
print("ending")
45109
}
110+
*/

0 commit comments

Comments
 (0)