[Accepted with Modification] SE-0306: Actors

The second review of SE-0306: Actors has concluded, and the Core Team has decided to accept the proposal with modification. For this second review, we had asked the community to explore the question of whether let bindings should be exceptional in their default actor isolation behavior, and based on community feedback, the Core Team has accepted the proposal with the modification that let bindings should be isolated by default, consistent with what is proposed for other declarations. Several topics, in addition to isolation of lets, came up in the second review:

Isolation of let bindings

Most participants in the thread agreed with Chris's argument that prompted the topic, citing the benefits to improving consistency, because every actor member follows a single rule, and to library evolution, because the outward-facing interface of let and var { get } members remain interchangeable in actors like they do elsewhere in the language. The Core Team concurs with these arguments, but will continue to keep an eye out for adopter feedback regarding the concern of annotation burden that prompted the originally-proposed exception.

Inheritance

Several participants raised concerns about the separation of inheritance from this proposal, citing the usefulness of inheritance for testing and modeling purposes, and for reducing roadblocks when adapting existing class hierarchies to become actors. The Core Team wants to reiterate that, by factoring inheritance out of this proposal, we are not making a decision to remove inheritance. Actor inheritance will be explored in its own proposal.

Naming

There was some discussion of whether the Actor type constraint ought to be named AnyActor, following the example of AnyObject. The name AnyObject was decided in the very early days of Swift, and in the time since, Any* has become a convention not for naming protocols and type constraints, but most commonly for type-erasing wrappers. Actor itself is a protocol constraint, and as such, the Core Team does not think the Any* naming convention makes sense to continue here.

Reentrancy

One of the unique aspects of the proposed actor design, compared to other actor systems, is the focus on call-and-response method calls instead of one-directional message passing. The goal is that this will interact well with structured concurrency and alleviate the need to manually manage send-and-receive state machines for common cases, while still allowing for one-directional communication with detached tasks. However, actors achieve their data isolation properties by only allowing one thing to run at a time, and if every async method on an actor locked that actor until its completion, it would be very easy to run into deadlocks.

The proposal minimizes the likelihood of these deadlocks by having async methods be reentrant, meaning that the actor can take on other work while other methods are suspended in the same actor. This guarantees forward progress, but also means that actor methods must be very careful not to leave the actor's invariants broken across potential await points, because other methods may start executing while those invariants are violated and leave the actor in an unexpected state. The Core Team believes reentrancy is still the right default, but acknowledges that this makes performing transactions and maintaining invariants very hard in methods that need to suspend. The proposal alludes to non-reentrant methods as a future direction here, and the Core Team would like to see this explored in another follow-up proposal, in order to address these concerns with the reentrant method model.

Thank you to everyone who participated in both review threads!

Joe Groff
Review Manager

45 Likes

Small typo there. Should be think.

1 Like