Hello folks,
I am working on a Swift binding to a third party system (Godot - GitHub - migueldeicaza/SwiftGodot: New Godot bindings for Swift), and I am trying to make my binding be strict-concurrent safe.
I expose an object-oriented wrapper in Swift that matches the underlying object model of Godot, this is basically a 1:1 mapping to what they have to offer.
They have an entire hierarchy of objects, starting with Object, and while some of the classes are thread-safe and can be called from any thread, others are not.
For example these chains of classes are thread safe:
Object > Input
Object > CameraServer
Object > RefCounted > TextServer > TextServerExtension > TextServerAdvanced
But other chains are not, for example:
Object > Node
Object > Node > Viewport > Window
So I do need things like Node, Viewport and Window to be annotated as @MainActor, but do not want Object, or RefCounted to be annotated with it.
But the compiler informs me that I am not able to do this:
public class Object {
}
@MainActor
public class Node: Object {
}
Main actor-isolated class 'Node' has different actor isolation from nonisolated superclass 'Object'; this is an error in the Swift 6 language mode
I am at a loss on how to proceed.
5 Likes
hborla
(Holly Borla)
2
You might be interested in SE-0434: Usability of global-actor-isolated types. Specifically this section: swift-evolution/proposals/0434-global-actor-isolated-types-usability.md at main · apple/swift-evolution · GitHub
5 Likes
Whoa!
Thank you so much Holly!
You folks are always five steps ahead.
Trying it out now.
1 Like
hborla
(Holly Borla)
4
...well, I forgot to actually commit the patch that implements this section of the proposal. Sorry about that, and here it is! [Concurrency] Allow isolation on subclasses of non-Sendable, non-isolated superclasses. by hborla · Pull Request #73497 · apple/swift · GitHub
3 Likes
Thanks, I will try out the daily builds.
The 6.0 toolchain from a day or two ago is crashing for me for an issue unrelated to this, so I will keep my eye on the fresh builds, hoping this gets fixed.
In the meantime, I came up with an ugly hack, and I am wondering if this is a terrible idea, or if we think this is an acceptable workaround:
@MainActor
class Base {
// This allows me to construct this from any actors:
nonisolated init () { }
nonisolated func demo () {
// No MainActor affinity
}
}
@MainActor
class Derived: Base {
public override init () {
// Instances of derived run on MainActor
}
func regular () {
// This method only runs in MainActor
}
}
Task {
let a = Base () // This works, no await necessary
let b = await Derived () // Need to use await here due to @MainActor
}
This seemed to work without warnings, but I have a feeling that I might have stublmed into a loophole.
best,
Miguel
1 Like