jayhickey
(Jay Hickey)
1
Hello! I came across an interesting case lately where actor isolation is not respected or warned when conforming to a delegate from an extension on an @MainActor class. For example, the below code compiles without warnings:
protocol MyDelegate {
func doThing()
}
@MainActor
class MyClass {
var myCounter = 0
}
extension MyClass: MyDelegate {
func doThing() {
myCounter += 1
print("Count incremented to \(myCounter)")
}
}
var v: MyDelegate?
Task { @MainActor in
v = MyClass()
}
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 1) {
v?.doThing()
}
This seems problematic as I am calling doThing() in a nonisolated context and mutating myCounter outside of the MainActor. I would expect to get a warning here telling me to mark doThing() as nonisolated or an error around my nonisolated call to doThing(). If I annotate the extension or the protocol with @MainActor I do get warnings and errors, but with the above code I don't.
Can someone help me understand what's going on here? Is this a bug, or am I just missing something? Thanks in advance!
3 Likes
filiplazov
(Filip Lazov)
2
MainActor only applies when using swift concurrency, not dispatch queues.
jayhickey
(Jay Hickey)
3
Thanks for the reply, this issue happens when using Task as well. For example:
protocol MyDelegate {
func doThing()
}
@MainActor
class MyClass {
var myCounter = 0
}
extension MyClass: MyDelegate {
func doThing() {
print("Is on main: \(Thread.isMainThread)")
myCounter += 1
print("Count incremented to \(myCounter)")
}
}
var v: MyDelegate?
Task { @MainActor in
v = MyClass()
}
Task {
sleep(1)
v?.doThing()
}
I get no warnings or errors and myCounter is incremented from a non-main thread:
Is on main: false
Count incremented to 1
I would expect at least a warning here telling me to mark doThing() as nonisolated, no?
filiplazov
(Filip Lazov)
4
ah I see what you mean, and this is not an issue if the conformance is in the type definition scope?
jayhickey
(Jay Hickey)
5
That's correct. If I move the conformance up within the type definition scope, I get a "Main actor-isolated instance method 'doThing()' cannot be used to satisfy nonisolated protocol requirement" warning.
Explicitly marking the extension as @MainActor also causes the same warning to surface, but it's not clear to me why that isn't implicitly happening.
1 Like
filiplazov
(Filip Lazov)
6
I agree with you, I would assume the same behavior, this looks like a bug.
mbrandonw
(Brandon Williams)
7
I filed a bug recently that seems to be similar. You can compile some unsafe code with no warnings, and Xcode will even trigger a data race warning when you run it.
I only came across this while messing with static properties on protocols, but your example shows it's a bigger problem.
jayhickey
(Jay Hickey)
8
Oh interesting, yes that looks quite similar. In case they have a different root cause I'll file a separate bug and tag yours. Thanks!
1 Like
jayhickey
(Jay Hickey)
9
2 Likes