Search code examples
thread-safetyswift-concurrency

Does exposing a Combine publisher of an actor as a nonisolated property violate the thread-safety of the actor?


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)
    }
}

Solution

  • 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.:

    enter image description here

    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”:

    “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.:

    compiler warnings