In the following example, the private _valuePublisher
has been exposed as a type-erased Publisher
through a nonisolated
property. Additionally, the current value of the _valuePublisher
is also exposed as a nonisolated
property. The compiler is happy with this arrangement but I'm not sure about the thread-safety aspect of this especially for the unwrappedValue
property. Any thoughts?
final actor MyActor {
private let _valuePublisher = CurrentValueSubject<Int, Never>(0)
nonisolated var valuePublisher: AnyPublisher<Int, Never> {
_valuePublisher.eraseToAnyPublisher()
}
nonisolated var unwrappedValue: Int {
_valuePublisher.value
}
func updateValue(_ value: Int) {
_valuePublisher.send(value)
}
}
You asked:
Does exposing a Combine publisher of an actor as a nonisolated property violate the thread-safety of the actor?
Technically, this actor has no mutable state and thus presents no thread-safety risk; the risk rests in the referenced CurrentValueSubject
.
However, if you give the actor a mutable state (by making the actor’s property a var
instead of let
) and try to expose that via a non-isolated property, that results in errors, as that would threaten the thread-safety of the actor, itself:
Actor-isolated property '_valuePublisher' can not be referenced from a non-isolated context
E.g.:
In short, in the original code snippet, the actor’s property was immutable, so the potential risk rests in the thread-safety of the referenced class, not in that of the actor.
That having been said, there is a thread-safety issue here.
To have the compiler perform more robust checks, I would advise changing the “Strict Concurrency Checking” setting to “Complete”:
When you do this, it will warn you of the problems:
Non-sendable type 'CurrentValueSubject<Int, Never>' in asynchronous access to actor-isolated property '_valuePublisher' cannot cross actor boundary
E.g.: