Participants raised questions about how this feature's potential role in inhibiting actor reentrancy, for code where allowing reentrance is not desired. The language workgroup agrees with the proposal authors that, while that is an important future direction, it is not this proposal's problem to solve.
There was some discussion about the naming of the preconditionIsolated, assertIsolated, and assumeIsolated APIs. Although precondition is not a verb, the language workgroup believes the naming association with precondition() and assert() is important to underline that they have analogous behavior in whether they check or not in debug or release builds. assumeIsolated doesn't have as clear of a precedent, but there wasn't a clearly better alternative, and the proposed name seems acceptable as is.
There was some concern about UnownedJobs unsafety, and questions as to whether its variation of runSynchronously and other APIs should have unsafe in the name to make the potential for undefined behavior more apparent. Although UnownedJob unfortunately doesn't have Unsafe in the type name (and it can't be added because of existing ABI constraints), the language workgroup believes the type itself should be seen as inherently unsafe, and like other fundamentally unsafe types such as Unmanaged, it doesn't need to have that unsafety reiterated on every method name. Also, although SE-0392 doesn't formally deprecate UnownedJob yet, the ExecutorJob type will be more prominent, which is safe and uses noncopyability to enforce safety, and developers will be encouraged to use it over UnownedJob as much as possible. When the expressivity of noncopyable types reaches the point that most uses of UnownedJob can be avoided, then we would like to see it formally deprecated.
Thank you to everyone who participated in both rounds of review!
This proposal is not enough to ensure strong order.
Suspension points still may interleave.
Youâd need ânon-reentrant actorsâ to achieve this. This was alluded to in the first ever actors proposal but no implementation progress was made towards them yet.
Workarounds include building your own queue using an async stream⌠itâs not great. We could at the very least publish a pattern for it, but itâs a topic that should be revisited properly sometime in the future.
Thank you very much for the quick response. I did the workaround in the past and they where confusing and error prone for me. So I now unfortunately abuse @MainActor because especially on linux I noticed inconsistent behaviour when writing to files in an async context, meaning sometimes the files where empty.
This happened in our pipeline that runs on linux but on our dev machines that are macos it never happened. So falling back to @MainActor to solve this issue is dirty. But if like you suggest a best practices could get published somewhere, maybe swift blog, that would be helpful thanks.
The files topic I suspect might be about flush behavior differences between the platforms⌠but thatâd be a different topic rather than this thread. If you have some reproducible sample thatâd help investigate.
import ArgumentParser // https://github.com/apple/swift-argument-parser
import Files // https://github.com/doozMen/Files
struct Write: AsyncParsableCommand {
func run() async throws {
let files = try ["foo.swift", "bar.swift"]
.map { try Folder.current.createFileIfNeeded(withName: $0) }
try await withThrowingTaskGroup(of: Void.self) { group in
for file in files {
group.addTask {
try await Task {
try file.write("// some data")
}.value
}
}
try await group.waitForAll()
}
}
}
Basically if you use version of Files 4.2.1 that does not use @MainActor for the file write you will get 1 out of 3 times on Ubuntu 22.04 arm64 one of the files that is empty. If you use the version on main from Files it is slower but I never have it.