Skip to content

Commit 8bc76e0

Browse files
committed
minor cleanup, add missing public init
WIP on distributed actors and executors wip-on-assume cleaned up assumeOnMainActorExecutor rebase cleanups
1 parent 4f199e9 commit 8bc76e0

14 files changed

+793
-209
lines changed

lib/AST/Decl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5220,7 +5220,7 @@ NominalTypeDecl::getExecutorOwnedEnqueueFunction() const {
52205220
continue;
52215221

52225222
auto params = funcDecl->getParameters();
5223-
if (params->get(0)->getSpecifier() == ParamSpecifier::Owned) {
5223+
if (params->get(0)->getSpecifier() == ParamSpecifier::LegacyOwned) { // TODO: make this Consuming
52245224
return funcDecl;
52255225
}
52265226
}

stdlib/public/Concurrency/Actor.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,10 +297,14 @@ JobPriority swift::swift_task_getCurrentThreadPriority() {
297297

298298
SWIFT_CC(swift)
299299
static bool swift_task_isCurrentExecutorImpl(ExecutorRef executor) {
300+
fprintf(stderr, "[%s:%d](%s) check executor:%p isMain:%d\n", __FILE_NAME__, __LINE__, __FUNCTION__, executor, executor.isMainExecutor() ? 1 : 0);
300301
if (auto currentTracking = ExecutorTrackingInfo::current()) {
302+
fprintf(stderr, "[%s:%d](%s) current tracking, active executor:%p\n", __FILE_NAME__, __LINE__, __FUNCTION__, currentTracking->getActiveExecutor());
301303
return currentTracking->getActiveExecutor() == executor;
302304
}
303305

306+
fprintf(stderr, "[%s:%d](%s) executor.isMainExecutor:%d\n", __FILE_NAME__, __LINE__, __FUNCTION__, executor.isMainExecutor() ? 1 : 0);
307+
fprintf(stderr, "[%s:%d](%s) isExecutingOnMainThread():%d\n", __FILE_NAME__, __LINE__, __FUNCTION__, isExecutingOnMainThread() ? 1 : 0);
304308
return executor.isMainExecutor() && isExecutingOnMainThread();
305309
}
306310

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import Swift
14+
15+
/// A safe way to synchronously assume that the current execution context belongs to the MainActor.
16+
///
17+
/// This API should only be used as last resort, when it is not possible to express the current
18+
/// execution context definitely belongs to the main actor in other ways. E.g. one may need to use
19+
/// this in a delegate style API, where a synchronous method is guaranteed to be called by the
20+
/// main actor, however it is not possible to annotate this legacy API with `@MainActor`.
21+
///
22+
/// If the current executor is *not* the MainActor's serial executor, this function will crash.
23+
///
24+
/// Note that this check is performed against the MainActor's serial executor, meaning that
25+
/// if another actor uses the same serial executor--by using ``MainActor/sharedUnownedExecutor``
26+
/// as its own ``Actor/unownedExecutor``--this check will succeed, as from a concurrency safety
27+
/// perspective, the serial executor guarantees mutual exclusion of those two actors.
28+
///
29+
/// - Parameters:
30+
/// - operation:
31+
/// - file:
32+
/// - line:
33+
/// - Returns:
34+
/// - Throws:
35+
@available(SwiftStdlib 5.9, *) // FIXME: use @backDeploy(before: SwiftStdlib 5.9)
36+
@_unavailableFromAsync(message: "await the call to the @MainActor closure directly")
37+
public
38+
func _assumeOnMainActorExecutor<T>(
39+
_ operation: @MainActor () throws -> T,
40+
file: StaticString = #fileID, line: UInt = #line
41+
) rethrows -> T {
42+
typealias YesMainActor = @MainActor () throws -> T
43+
typealias NoMainActor = () throws -> T
44+
45+
/// This is guaranteed to be fatal if the check fails,
46+
/// as this is our "safe" version of this API.
47+
guard _taskIsCurrentExecutor(Builtin.buildMainActorExecutorRef()) else {
48+
fatalError("Incorrect actor executor assumption; Expected 'MainActor' executor.", file: file, line: line)
49+
}
50+
51+
// To do the unsafe cast, we have to pretend it's @escaping.
52+
return try withoutActuallyEscaping(operation) {
53+
(_ fn: @escaping YesMainActor) throws -> T in
54+
let rawFn = unsafeBitCast(fn, to: NoMainActor.self)
55+
return try rawFn()
56+
}
57+
}
58+
59+
//@available(SwiftStdlib 5.9, *)
60+
//@_alwaysEmitIntoClient // FIXME: use @backDeploy(before: SwiftStdlib 5.9)
61+
//func _preconditionExpectedExecutor(
62+
// _ _executor: Builtin.Executor,
63+
// file: StaticString = #file, line: UInt = #line) {
64+
//
65+
//
66+
// let message = ""
67+
// precondition(false, , file: file, line: line)
68+
//
69+
// file.utf8CString.withUnsafeBufferPointer { (_ bufPtr: UnsafeBufferPointer<CChar>) in
70+
// let fileBasePtr: Builtin.RawPointer = bufPtr.baseAddress!._rawValue
71+
//
72+
// // string lengths exclude trailing \0 byte, which should be there!
73+
// let fileLength: Builtin.Word = (bufPtr.count - 1)._builtinWordValue
74+
//
75+
// // we're handing it UTF-8
76+
// let falseByte: Int8 = 0
77+
// let fileIsASCII: Builtin.Int1 = Builtin.trunc_Int8_Int1(falseByte._value)
78+
//
79+
// _reportUnexpectedExecutor(fileBasePtr, fileLength, fileIsASCII,
80+
// line._builtinWordValue, _executor)
81+
// }
82+
//}

stdlib/public/Concurrency/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I
7979
../CompatibilityOverride/CompatibilityOverride.cpp
8080
Actor.cpp
8181
Actor.swift
82+
AssumeActorExecutor.swift
8283
AsyncLet.cpp
8384
AsyncLet.swift
8485
CheckedContinuation.swift
@@ -104,9 +105,10 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I
104105
AsyncThrowingFlatMapSequence.swift
105106
AsyncThrowingMapSequence.swift
106107
AsyncThrowingPrefixWhileSequence.swift
108+
UncheckedContinuation.swift
107109
GlobalActor.swift
108110
MainActor.swift
109-
PartialAsyncTask.swift
111+
Job.swift
110112
SourceCompatibilityShims.swift
111113
Task.cpp
112114
Task.swift

stdlib/public/Concurrency/Executor.swift

Lines changed: 118 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public protocol SerialExecutor: Executor {
4949
@available(SwiftStdlib 5.9, *)
5050
extension Executor {
5151
public func enqueue(_ job: UnownedJob) {
52-
fatalError("Please implement \(Self.self).enqueueJob(_:)")
52+
self.enqueue(Job(job))
5353
}
5454

5555
public func enqueue(_ job: __owned Job) {
@@ -100,18 +100,6 @@ public struct UnownedSerialExecutor: Sendable {
100100
}
101101
}
102102

103-
/// Checks if the current task is running on the expected executor.
104-
///
105-
/// Generally, Swift programs should be constructed such that it is statically
106-
/// known that a specific executor is used, for example by using global actors or
107-
/// custom executors. However, in some APIs it may be useful to provide an
108-
/// additional runtime check for this, especially when moving towards Swift
109-
/// concurrency from other runtimes which frequently use such assertions.
110-
/// - Parameter executor: The expected executor.
111-
@available(SwiftStdlib 5.9, *)
112-
@_silgen_name("swift_task_isOnExecutor")
113-
public func _taskIsOnExecutor<Executor: SerialExecutor>(_ executor: Executor) -> Bool
114-
115103
/// Primarily a debug utility.
116104
///
117105
/// If the passed in Job is a Task, returns the complete 64bit TaskId,
@@ -134,22 +122,6 @@ where E: SerialExecutor {
134122
}
135123
}
136124

137-
@available(SwiftStdlib 5.1, *)
138-
@_transparent
139-
public // COMPILER_INTRINSIC
140-
func _checkExpectedExecutor(_filenameStart: Builtin.RawPointer,
141-
_filenameLength: Builtin.Word,
142-
_filenameIsASCII: Builtin.Int1,
143-
_line: Builtin.Word,
144-
_executor: Builtin.Executor) {
145-
if _taskIsCurrentExecutor(_executor) {
146-
return
147-
}
148-
149-
_reportUnexpectedExecutor(
150-
_filenameStart, _filenameLength, _filenameIsASCII, _line, _executor)
151-
}
152-
153125
#if !SWIFT_STDLIB_SINGLE_THREADED_CONCURRENCY && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
154126
// This must take a DispatchQueueShim, not something like AnyObject,
155127
// or else SILGen will emit a retain/release in unoptimized builds,
@@ -178,3 +150,120 @@ internal final class DispatchQueueShim: @unchecked Sendable, SerialExecutor {
178150
}
179151
}
180152
#endif
153+
154+
// ==== -----------------------------------------------------------------------
155+
// - MARK: Executor assertions
156+
157+
/// Checks if the current task is running on the expected executor.
158+
///
159+
/// Do note that if multiple actors share the same serial executor,
160+
/// this assertion checks for the executor, not specific actor instance.
161+
///
162+
/// Generally, Swift programs should be constructed such that it is statically
163+
/// known that a specific executor is used, for example by using global actors or
164+
/// custom executors. However, in some APIs it may be useful to provide an
165+
/// additional runtime check for this, especially when moving towards Swift
166+
/// concurrency from other runtimes which frequently use such assertions.
167+
@available(SwiftStdlib 5.9, *)
168+
public func preconditionOnSerialExecutor(
169+
_ executor: some SerialExecutor,
170+
_ message: @autoclosure () -> String = "",
171+
file: StaticString = #fileID, line: UInt = #line) {
172+
preconditionOnSerialExecutor(executor.asUnownedSerialExecutor(), file: file, line: line)
173+
}
174+
175+
@available(SwiftStdlib 5.9, *)
176+
public func preconditionOnSerialExecutor(
177+
_ unowned: UnownedSerialExecutor,
178+
_ message: @autoclosure () -> String = "",
179+
file: StaticString = #fileID, line: UInt = #line) {
180+
if _taskIsCurrentExecutor(unowned.executor) {
181+
return
182+
}
183+
184+
// TODO: log on what executor it was instead of the expected one
185+
let message = "Expected executor \(unowned); \(message())"
186+
preconditionFailure(
187+
message,
188+
file: file, line: line)
189+
}
190+
191+
/// Same as ``preconditionOnSerialExecutor(_:_:file:line)`` however only in DEBUG mode.
192+
@available(SwiftStdlib 5.9, *)
193+
public func assertOnSerialExecutor(
194+
_ executor: some SerialExecutor,
195+
_ message: @autoclosure () -> String = "",
196+
file: StaticString = #fileID, line: UInt = #line) {
197+
assertOnSerialExecutor(executor.asUnownedSerialExecutor(), file: file, line: line)
198+
}
199+
200+
@available(SwiftStdlib 5.9, *)
201+
public func assertOnSerialExecutor(
202+
_ unowned: UnownedSerialExecutor,
203+
_ message: @autoclosure () -> String = "",
204+
file: StaticString = #fileID, line: UInt = #line) {
205+
if _isDebugAssertConfiguration() {
206+
if _taskIsCurrentExecutor(unowned.executor) {
207+
return
208+
}
209+
210+
// TODO: log on what executor it was instead of the expected one
211+
// TODO: fixme use assertion here?
212+
fatalError("Expected executor \(unowned); \(message())", file: file, line: line)
213+
}
214+
}
215+
216+
/// Checks if the current task is running on the expected executor.
217+
///
218+
/// Generally, Swift programs should be constructed such that it is statically
219+
/// known that a specific executor is used, for example by using global actors or
220+
/// custom executors. However, in some APIs it may be useful to provide an
221+
/// additional runtime check for this, especially when moving towards Swift
222+
/// concurrency from other runtimes which frequently use such assertions.
223+
/// - Parameter executor: The expected executor.
224+
@available(SwiftStdlib 5.9, *)
225+
@_silgen_name("swift_task_isOnExecutor")
226+
func _taskIsOnExecutor(_ executor: some SerialExecutor) -> Bool
227+
228+
@available(SwiftStdlib 5.1, *)
229+
@_transparent
230+
public // COMPILER_INTRINSIC
231+
func _checkExpectedExecutor(_filenameStart: Builtin.RawPointer,
232+
_filenameLength: Builtin.Word,
233+
_filenameIsASCII: Builtin.Int1,
234+
_line: Builtin.Word,
235+
_executor: Builtin.Executor) {
236+
if _taskIsCurrentExecutor(_executor) {
237+
return
238+
}
239+
240+
_reportUnexpectedExecutor(
241+
_filenameStart, _filenameLength, _filenameIsASCII, _line, _executor)
242+
}
243+
244+
@available(SwiftStdlib 5.9, *)
245+
@_alwaysEmitIntoClient // FIXME: use @backDeploy(before: SwiftStdlib 5.9)
246+
func _checkExpectedExecutor(
247+
_ _executor: Builtin.Executor,
248+
file: String,
249+
line: Int) {
250+
if _taskIsCurrentExecutor(_executor) {
251+
return
252+
}
253+
254+
file.utf8CString.withUnsafeBufferPointer { (_ bufPtr: UnsafeBufferPointer<CChar>) in
255+
let fileBasePtr: Builtin.RawPointer = bufPtr.baseAddress!._rawValue
256+
257+
// string lengths exclude trailing \0 byte, which should be there!
258+
let fileLength: Builtin.Word = (bufPtr.count - 1)._builtinWordValue
259+
260+
// we're handing it UTF-8
261+
let falseByte: Int8 = 0
262+
let fileIsASCII: Builtin.Int1 = Builtin.trunc_Int8_Int1(falseByte._value)
263+
264+
_reportUnexpectedExecutor(
265+
fileBasePtr, fileLength, fileIsASCII,
266+
line._builtinWordValue,
267+
_executor)
268+
}
269+
}

0 commit comments

Comments
 (0)