Skip to content

Commit 307ec5a

Browse files
committed
[Concurrency] unownedExecutor can be declared not directly on actor
1 parent a81b0b6 commit 307ec5a

File tree

4 files changed

+159
-27
lines changed

4 files changed

+159
-27
lines changed

lib/AST/Decl.cpp

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9356,38 +9356,27 @@ Type TypeBase::getSwiftNewtypeUnderlyingType() {
93569356
}
93579357

93589358
const VarDecl *ClassDecl::getUnownedExecutorProperty() const {
9359-
auto &ctx = getASTContext();
9359+
auto &C = getASTContext();
9360+
auto module = getParentModule();
93609361

9361-
auto hasUnownedSerialExecutorType = [&](VarDecl *property) {
9362-
if (auto type = property->getInterfaceType())
9363-
if (auto td = type->getAnyNominal())
9364-
if (td == ctx.getUnownedSerialExecutorDecl())
9365-
return true;
9366-
return false;
9367-
};
9362+
if (!isAnyActor())
9363+
return nullptr;
93689364

9369-
VarDecl *candidate = nullptr;
9370-
for (auto member: getMembers()) {
9371-
// Instance properties called unownedExecutor.
9372-
if (auto property = dyn_cast<VarDecl>(member)) {
9373-
if (property->getName() == ctx.Id_unownedExecutor &&
9374-
!property->isStatic()) {
9375-
if (!candidate) {
9376-
candidate = property;
9377-
continue;
9378-
}
9365+
llvm::SmallVector<ValueDecl *, 2> results;
9366+
this->lookupQualified(getSelfNominalTypeDecl(),
9367+
DeclNameRef(C.Id_unownedExecutor),
9368+
NL_ProtocolMembers,
9369+
results);
93799370

9380-
bool oldHasRightType = hasUnownedSerialExecutorType(candidate);
9381-
if (oldHasRightType == hasUnownedSerialExecutorType(property)) {
9382-
// just ignore the new property, we should diagnose this eventually
9383-
} else if (!oldHasRightType) {
9384-
candidate = property;
9385-
}
9386-
}
9387-
}
9371+
for (auto candidate: results) {
9372+
if (isa<ProtocolDecl>(candidate->getDeclContext()))
9373+
continue;
9374+
9375+
if (VarDecl *var = dyn_cast<VarDecl>(candidate))
9376+
return var;
93889377
}
93899378

9390-
return candidate;
9379+
return nullptr;
93919380
}
93929381

93939382
bool ClassDecl::isRootDefaultActor() const {

stdlib/public/Concurrency/GlobalExecutor.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ void swift::swift_task_enqueueGlobalWithDeadline(
125125
swift_task_enqueueGlobalWithDeadlineImpl(sec, nsec, tsec, tnsec, clock, job);
126126
}
127127

128+
/*****************************************************************************/
129+
/****************************** MAIN EXECUTOR *******************************/
130+
/*****************************************************************************/
131+
128132
void swift::swift_task_enqueueMainExecutor(Job *job) {
129133
concurrency::trace::job_enqueue_main_executor(job);
130134
if (swift_task_enqueueMainExecutor_hook)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s
2+
3+
// REQUIRES: concurrency
4+
// REQUIRES: executable_test
5+
// UNSUPPORTED: freestanding
6+
7+
// UNSUPPORTED: back_deployment_runtime
8+
// REQUIRES: concurrency_runtime
9+
10+
11+
actor Simple {
12+
var count = 0
13+
func report() {
14+
print("simple.count == \(count)")
15+
count += 1
16+
}
17+
}
18+
19+
actor Custom {
20+
var count = 0
21+
let simple = Simple()
22+
23+
func report() async {
24+
print("custom.count == \(count)")
25+
count += 1
26+
27+
await simple.report()
28+
}
29+
}
30+
31+
@available(SwiftStdlib 5.1, *)
32+
@main struct Main {
33+
static func main() async {
34+
print("begin")
35+
let actor = Custom()
36+
await actor.report()
37+
await actor.report()
38+
await actor.report()
39+
print("end")
40+
}
41+
}
42+
43+
// CHECK: begin
44+
// CHECK-NEXT: custom.count == 0
45+
// CHECK-NEXT: simple.count == 0
46+
// CHECK-NEXT: custom.count == 1
47+
// CHECK-NEXT: simple.count == 1
48+
// CHECK-NEXT: custom.count == 2
49+
// CHECK-NEXT: simple.count == 2
50+
// CHECK-NEXT: end
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s
2+
3+
// REQUIRES: concurrency
4+
// REQUIRES: executable_test
5+
// REQUIRES: libdispatch
6+
// UNSUPPORTED: freestanding
7+
8+
// UNSUPPORTED: back_deployment_runtime
9+
// REQUIRES: concurrency_runtime
10+
11+
import Dispatch
12+
13+
func checkIfMainQueue(expectedAnswer expected: Bool) {
14+
dispatchPrecondition(condition: expected ? .onQueue(DispatchQueue.main)
15+
: .notOnQueue(DispatchQueue.main))
16+
}
17+
18+
protocol WithSpecifiedExecutor: Actor {
19+
nonisolated var executor: SpecifiedExecutor { get }
20+
}
21+
22+
protocol SpecifiedExecutor: SerialExecutor {}
23+
24+
extension WithSpecifiedExecutor {
25+
/// Establishes the WithSpecifiedExecutorExecutor as the serial
26+
/// executor that will coordinate execution for the actor.
27+
nonisolated var unownedExecutor: UnownedSerialExecutor {
28+
executor.asUnownedSerialExecutor()
29+
}
30+
}
31+
32+
final class InlineExecutor: SpecifiedExecutor, Swift.CustomStringConvertible {
33+
let name: String
34+
35+
init(_ name: String) {
36+
self.name = name
37+
}
38+
39+
public func enqueue(_ job: UnownedJob) {
40+
print("\(self): enqueue")
41+
job._runSynchronously(on: self.asUnownedSerialExecutor())
42+
print("\(self): after run")
43+
}
44+
45+
public func asUnownedSerialExecutor() -> UnownedSerialExecutor {
46+
return UnownedSerialExecutor(ordinary: self)
47+
}
48+
49+
var description: Swift.String {
50+
"InlineExecutor(\(name))"
51+
}
52+
}
53+
54+
actor MyActor: WithSpecifiedExecutor {
55+
56+
nonisolated let executor: SpecifiedExecutor
57+
58+
// Note that we don't have to provide the unownedExecutor in the actor itself.
59+
// We obtain it from the extension on `WithSpecifiedExecutor`.
60+
61+
init(executor: SpecifiedExecutor) {
62+
self.executor = executor
63+
}
64+
65+
func test(expectedExecutor: some SerialExecutor) {
66+
checkIfMainQueue(expectedAnswer: true)
67+
print("\(Self.self): on executor \(expectedExecutor)")
68+
}
69+
}
70+
71+
@main struct Main {
72+
static func main() async {
73+
print("begin")
74+
let one = InlineExecutor("one")
75+
let actor = MyActor(executor: one)
76+
await actor.test(expectedExecutor: one)
77+
await actor.test(expectedExecutor: one)
78+
await actor.test(expectedExecutor: one)
79+
print("end")
80+
}
81+
}
82+
83+
// CHECK: begin
84+
// CHECK-NEXT: InlineExecutor(one): enqueue
85+
// CHECK-NEXT: MyActor: on executor InlineExecutor(one)
86+
// CHECK-NEXT: MyActor: on executor InlineExecutor(one)
87+
// CHECK-NEXT: MyActor: on executor InlineExecutor(one)
88+
// CHECK-NEXT: InlineExecutor(one): after run
89+
// CHECK-NEXT: end

0 commit comments

Comments
 (0)