Goal: make concurrent programs simpler to reason about and safer (no data races) Data race = two threads access the same data Data races in memory concurrently (at least one writer)

Increasing the priority of a task The mechanism to prevent data can automatically increase the Because the child tasks are races proactively priority of the child tasks known Data isolation Actor-local data, and each actor is single- When a higher-priority task waits on threaded (or serial-queued) internally a lower-priority task, the priority of all child tasks can be escalated An actor performs local computation on its own private state This is the timeout problem Nathaniel Smith wrote about in In the form of calling async Examples of systemic Limiting the total time spent on a his blog posts on structured instance methods task management task (including its child tasks) concurrency "by only allowing asynchronous instance problems this can solve Cancelling a task also cancels Actors send messages methods of actor classes to be invoked from "This eliminates the need for any queuing or its child tasks to other actors outside the actor, we ensure that all synchronization within the synchronous Semantics synchronous methods are already inside the code, making such code more efficient and Structured concurrency = concurrent "Task-local storage" can be actor when they are called" simpler to write" programs are organized into tasks and picked up by child tasks child tasks rather than simply a sea of Messages can't be sync It's possible this restriction could be lifted under formally-unrelated threads and jobs Child tasks can communicate up certain circumstances, e.g. when the caller is in the task hierarchy, e.g. when an async context and waits the sync call they get flooded and need the A cooperative approach to parent to slow down handling pressure Actors act on messages received from other actors The proposal essentially reads like NSOperation, but with Syntax: `actor class BankAccount { … }` compiler support Like classes, but with compiler- = actor isolation Slava Pestov: "I’m considering dying on the enforced protection from data races Allow the programmer to compose for the actor's state multiple async (and non-async) The compiler translates an async “Well Actually async/await is a control flow There are holes in the Properties of mutable reference operations with the language's function into an appropriate set of structure and not a concurrency primitive” hill" protection, though Examples types (classes, pointers) normal control flow structures closures and state machines. Async functions can be suspended, which Every actor has a private serial Async instance methods are Sequentially, but not necessarily queue/exclusive executor executed on this executor in enqueuing order Document will eventually be at Difficult to read Pyramid of doom implies that another task can run on the same docs/ConcurrencyRoadmap.md thread. This sounds like concurrency to me Becomes nested/duplicated Actor classes can't inherit from Exception: inheriting from I don't fully understand this The proposal uses the word non-actor classes NSObject is allowed Goal: make concurrent programming Error handling Actor classes in Swift convenient, efficient, and safe Not compatible with throws "concurrency" more as a synonym for syntax what Jakob Jenkov calls "parallelism" Actor classes are a restricted form of class Manifesto might describe Problems with typical callback- "async/await does not introduce multiple possible directions Conditional execution is hard concurrency per se: ignoring the Example: makeDinner() function in proposal motivation based async code and error-prone section: Should actors (being a new Is inheritance required to bring Not a manifesto suspension points within an asynchronous Support inheritance concept) even support inheritance? UIKit etc. into the actor world? This describes a single plan function, it will execute in essentially the Forget to call callback in one or Where actor classes act like same manner as a synchronous function" func makeDinner() async throws -> Meal { Satisfy AnyObject constraints make asynchronous programming Easy to make mistakes more code paths let veggies = await try chopVegetables() classes convenient and clear at the point of use Too many APIs are not defined let meat = await marinateMeat() But classes are not the only Motivation: async/await alone Are reference types reference types in Swift provide a standard set of asynchronously because To execute an async function's let oven = await try preheatOven(temperature: 350) does not provide concurrency! Why call them "actor classes", language tools and techniques callbacks are awkward They mean that a single async subtasks in parallel, we need The first three subtasks should It's about control flow! function still executes another mechanism to build on let dish = Dish(ingredients: [veggies, meat]) not merely "actors"? Chris Lattner: "I think it is reasonable for run in parallel actors to not have static members and class Intended end state improve the performance of asynchronous Allow async code to be written What's the point of "ignoring the sequentially, even if it can be top of async/await = structured return await try oven.cook(dish, duration: .hours(3)) methods though, as the whole idea is to get rid code through better knowledge at compile time as straight-line code suspension points" suspended in between. concurrency } The subtasks also have dependencies: `cook` can't begin of global state" eliminate data races and deadlocks in the (1) better performance for until the first three are done "The similarities end at being "a same way Swift eliminates memory unsafety asynchronous code reference rather than a value", Advantages of async functions Allow the compiler to directly Even if there's only one CPU, Introduction will span multiple Swift releases (2) better tooling to provide a ktoso: conflating actors and however the semantic E.g. an actor could also be reason about the execution pattern "Concurrency means that an switching between multiple tasks more consistent experience classes may be capabilities the two expose are distributed (living on another of the code, allowing callbacks to application is making progress Will allow uses to organize their while debugging, profiling, and "concurrency is related to how counterproductive very different" machine) run far more efficiently on more than one task at the code around actors; will reduce, exploring code an application handles multiple but not eliminate, data races same time (concurrently)" tasks it works on: one task at at Other code that has a reference (3) a foundation for future time (sequentially) or multiple to the actor can't modify state async/await concurrency features like task tasks at the same time Mutable stored properties Task API and Structured Concurrency priority and cancellation (concurrently)" Actor-isolated Computed properties Ability to protect global variables with "In practice, asynchronous "Parallelism is related to how an members by default Phase 1: async syntax and actor global actors functions are compiled to not Jakob Jenkov: concurrency vs. application handles each Subscripts types the function and all of its depend on the thread during an parallelism "Parallelism means that an application splits individual task: process the task Protect class members by converting asynchronous callers asynchronous call, so that only Synchronous instance methods Safety benefits covered in its tasks up into smaller subtasks which can serially from start to end, or split the class to an actor class It only gives up its thread at a simultaneously abandon the the innermost function needs to phase one be processed in parallel, for instance on the task up into subtasks which = External access is allowed Async function = "think of an asynchronous suspension point (marked by await) thread. do any extra work." multiple CPUs at the exact same time" can be completed in parallel" Frameworks that require access on a function as an ordinary function that has the Types with value semantics that particular queue can define a global actor A synchronous function can't call an special power to give up its thread" By this definition, it's a question of where This is only safe for value types are mutably captured by a Actors and (partial) Actor Isolation and default protocols to it async function because the former you draw the line between "multiple Proposals (better: types with value semantics) closure are also problematic doesn't know how to give up its thread. tasks" and "one task split into subtasks" Global variables will not be A `let` to a (potentially nested, Race conditions not covered in threadsafe by default "This is an interesting question that I'm not sure we Like a thread, a task runs one hidden) mutable reference type is See "Escaping reference types" phase one have a clear answer to. There's an ambiguity between async function at a time Actor-independent Non-actor reference types a function that's "generic" about its executor (e.g. by default Constants (`let`) unsafe in the proposal When a function makes an async call, Actors only allow access to their Concurrency interop with Obj-C because it takes an argument that can only be safely "a task is to asynchronous This is a problem for library evolution the called function is still running as part members directly on `self` called or otherwise used on that executor) and a Every async functions runs in a functions, what a thread is to because turning a `let` into a computed You can use of the same task (and the caller waits Using the @asyncHandler function that's apathetic about its executor (e.g. task synchronous functions" `var` is no longer a resilient change `@actorIndependent var` attribute because it's in an I/O library and is just kicking off work for it to return) and waiting for a response)" Declarations marked with ability to declare a synchronous "So e.g. you can have the event when a function returns from an Can async functions that don't belong `@actorIndependent` actor function as an handler for a button press just Actor async func will always run async call, the caller resumes Two phases Every async function has an to an actor run on their caller's Async handlers asynchronous handler "It means you can call arbitrary immediately make and await a on the actor's executor running on the same task Async instance methods associated executor executor without context switch? async functions without having network request and then Tasks are scheduled by the Reasoning: an escaping closure to manually create a detached update the UI appropriately, Will resume on the caller's system to run on a system thread can potentially escape to a task for them to run in." without any boilerplate." Currently; non-actor async executor if it's "executor- generic", e.g. because it uses a different thread functions aren't formalized yet Plan This creates a hierarchy of tasks, and Non-escaping closures can Eliminates data races parameter that can only be Tasks can make child tasks that information can flow up and down the access actor-isolated members, "We might need to introduce yet accessed on an actor escaping closures can't Allow efficient interoperation of will perform work concurrently hierarchy, making it convenient to manage Problem: another kind of function type for actors (needed to make isolation Non-actor async func If actor-apathetic (doesn't care (= in parallel) the whole thing holistically DispatchQueue.concurrentPerform "concurrent, non-escaping"" practical) about the caller's constraints), Tasks become the primary units is non-escaping can resume on a global pool Does not care where it resumes withoutActuallyEscaping would Reduces the data race problem to the problem of Reduces to an analysis of how the of concurrency, rather than also provide a loophole ensuring that all ordinary mutable memory is memory is accessed and who can Requiring await follows the lower-level concepts like threads Special rules "This restriction prevents exclusivity violations Phase 2: enforce full actor accessed only by a particular actor or task access it in the first place precedent set by try = more work to do but not where the modification of the actor-isolated balance isolation Will never be implicit, always The compiler will prevent an syntactically marked Allows developers to be sure currently running is initiated by passing it as inout to a call that is Provide a full set of tools for Ability to restrict a type to be actor-local type from being that code between suspension then suspended, and another task executed on the suspended schedulable = ready to run working with fully isolated actors "actor local" passed between actors points will not be interrupted Actor-isolated data can't be same actor then fails with an exclusivity violation in waiting = not ready to run passed inout to async functions trying to access balance itself." "Code that touches a mutable global variable or shares a The most important suspension point: a Semantics class reference across actor boundaries fundamentally call to an async function associated = will run until it completes (by Actor isolation is instance-based, Change the defaults for global variables cannot be shown to be safe from data races, and will with a different execution context returning from its initial function) access control is type-based and variables of reference type to be Will be a source-breaking need to change to ensure that it (and code written in the or reaches a suspension point I don't think there is a mechanism Task states "The proposed design will end up producing unsafe or actor local by default change future) is safe from data races" and becomes suspended Suspension always occurs when like "if there's no work to be done, a lot of async wrappers for sync functions "One of the main drivers for the two-phase a suspension point is reached continue immediately" running At a suspension point, it may just to allow those sync functions being approach is that of wanting to give Swift "It is expected that the experience of porting Suspension become immediately called across actor boundaries" users time to become accustomed to async large working code bases to actors and Suspension enqueues the remaining schedulable again "The compiler would synthesize the functions and actors before moving to a full async functions will inform the functionality partial task on the function's executor Separate dimension from thunk just like it does reabstraction isolation model." needed to enforce full actor isolation." Pitch: Asynchronous functions (10/2020) completed The end state access control Threads are mostly an implementation thunks." managing isolation queues and mechanism, not a part of the intended "From the perspective of the function, it is "This realigns the `async` modifier on callbacks manually obscures When control returns, the async But not necessarily on the same interface to concurrency suspended, waiting for the call to return. "when an async function calls another async From the perspective of the task, it may actor methods to be about the behavior what functions are really doing function picks up exactly where it was thread Use actors to guarantee a Async function becomes function, we say that the calling function is have continued running in the callee, or it of the method, not about whether it can consistent executor And cancellation should be suspended ≠ task becomes suspended, but that doesn’t mean the entire may have been suspended in order to, say, be called by other actors" deterministic so that developers Counterargument: even good Semantics Libraries that use threads directly for suspended task is suspended" change to a different execution context." Chris Lattner thinks this is a Chris Lattner thinks actors can be certain that everything is Swift programmers use [weak state isolation should generally model mistake should not conform to non- self] to avoid extending the cancelled when an actor deinits Resuming from a suspension Tasks carry scheduling Too much ceremony Suspension points those threads as actors in Swift async protocol requirements lifetime of an object: there's no point information (e.g. task priority) John McCall: "There is really Or annotate them with a global actor, e.g.: need to run the completion Tasks serve as a handle for cancelling, Conforming to e.g. just no way that a task calling an High-level purposes of tasks Conforming an actor class to a sync People use [weak self] handler if the view controller has querying, manipulating the task Pitch: Actors CustomStringConvertible is important async method on an object is @MainActor // or UIActor protocol like reflexively, even if it's not been deallocated; ideally, the To associate async functions with a to interact with other Swift code! not going to keep that object func onRefreshButtonClick() async { CustomStringConvertible needed request should be cancelled particular execution context, make Tasks can carry task-local data alive while the task is active. self.data = await jsonAPI.fetchData() This makes sure that Maybe it shouldn't be possible. The general pattern them part of an actor Joe Groff: tie cancellation of a There's no way to express what tableView.reloadData() `reloadData` resumes on the By the time the scope exits, the child Recommendation: use access How useful is it really if the Critique of the current state of (isolation queues and task to the lifetime of an object you want with async functions." } main thread task must either have completed, or it control to prevent cross-actor conforming actor can't access its concurrent programming callbacks) is good, but: will be implicitly cancelled calls, and make cross-actor mutable state? Isn't structured concurrency "async functions can be used outside of actors. sync calls implicitly async supposed to solve this? However, the assumption is that actors are "so if a function isn’t an actor makes it easier to reason about Providing actor-identifying the concurrent tasks that are But @actorIndependent is information, e.g. for distributed it's not clear what queue some what impose correctness requirements on function, there must not be any where code must be running" such requirements" executing within a given scope important for other things Examples equality checks Easy to introduce bugs code/callback runs on Intentionally more restricted than Actor isolation futures: the child task can't outlive its unlocks numerous optimization @actorIndependent(unsafe) Separate allocation of multiple "if an async function is running within a given context Benefits parent, doesn't persists beyond the opportunities for the compiler let concurrentHashMap = ... function objects, extra Hard/impossible for the compiler that is protected by a serial queue, reaching a scope in which it was created and runtime Needlessly inefficient refcounting to optimize suspension point means that other code can be interleaved on that same serial queue" actor Hello { "`async let` does not introduce Reduces boilerplate This could also make @actorIndependent can also be // OK, no executor hop involved Concurrency Roadmap This is similar to traditional callback-based any new type, because then you @actorIndependent redundant applied to globals to mark them func hi() { concurrentHashMap.put(...) } Actor classes have a private code: when the callback runs, the calling Pitch (10/2020): Structured Intentionally, "you cannot, without a Task.Group, could pass it around into some safe to access from any actor } queue; they synchronize all function/object must assume that its state Concurrency Like a future, but you can't spawn dynamic numbers of things. And you cannot, other function which is the access to their private state Developers can no longer forget Suspension points interrupt has changed pass the unresolved value without Task.Handle just pass around not yet precise thing structured ktoso: In the proposal's model, such through their queue to use the queue atomicity around completed values -- you must await on them" concurrency wants to prevent" "why would these things be defined on information must live "in the actor type" Safer because the compiler can Developers will need to take care to maintain the actor instances if they are An example could be a resource because we don't want wrappers like prevent bugs Eliminates deadlocks because you invariants at every suspension point "The proposal describes async independent of it? Isn't this just a static ID by which the actor can be `Reference` where such These wrappers are what other can only communicate with other The fact that `async let` "futures" Should `async T` or `Async` function results and async actor member?" identified an identifier could be attached actor implementations use actors through async functions Intentionally, developers can't opt out of are not first-class values means be an explicit type so that you immutable variables. My concern is this. Allowing interleaving is important to refactoring can sometimes be can pass such a value to the lack of async function Doug Gregor likes it The compiler can be smart about optimizing prevent deadlocks difficult another function? parameters and async properties" Better performance because away synchronization, like when a method starts Isolated to a specific instance of an actor class easier to optimize by calling an async function on a different actor It's important that async = "a non-escaping future" functions don't the thread Isolated to a specific global actor E.g. Pierre Habouzit doesn't recommend one reading a variable defined by an @actorIndependent private queue per class, at least not unless the Including beginning accesses for any inout `async let` is treated as a queues are linked in a dependency tree parameters. This means the duration of inout You can later `await` on suspension point, and therefore 1. Arguments are evaluated accesses can potentially become much allows the declaration to be the result must be marked with `await` accessed from anywhere Solution: bring the "They point out that, aside from using the normal rules longer. patterns we already having lower absolute This ensures that child tasks 2. Determine the callee’s executor. See actors interrupts the implicit propagation of use into the language Dave Abrahams: "Some of the systems performance costs than queues, aren't being created and actor isolation from context implicitly cancelled needlessly programming folks we've been talking with in a lock-based system there are 3. If the callee’s executor is different from the would dispute whether [using queues instead proven tools for deadlock An @actorIndependent instance method in caller’s executor, a suspension occurs and the = Async calls on the same executor If the scope of an async let exits by throwing Actor-independent of locks] is good." detection and debugging" `async let` creates a "At least one of the variables for a given an actor class can't access the actor's state partial task to resume execution in the callee is (= on the same actor) does not an error, the child task corresponding to the child task `async let` clause must be awaited at least The roadmap document enqueued on the callee’s executor. create a suspension point Is this true? async let is implicitly cancelled. If the child "Actors also seem to make it super once along all execution paths (that don't extension BankAccount: CustomStringConvertible { recommends to use queues task has already completed, its result (or easy to dispatch very small tasks, such 4. Execute the callee with the given arguments throw an error) before it goes out of scope" @actorIndependent instead of locks Why? Is this good practice? as protecting shared state and thrown error) is discarded. var description: String { Semantics of an async call on its executor. Actor isolation properties. When this is out there, Use @actorIndependent on "Bank account of \"\(ownerName)\"" Consequence: if one component categories "My experience is that developers will people will use it and it will be 5. During the return, if the callee’s executor is protocol witnesses with } I.e. tuple return types are of the tuple throws, awaiting on use way too many actors, just as they impossible to explain to them that they different from the caller’s executor, a suspension unknown actor isolation } considered as "completing another tuple element also use way too many queues." should really not use it that much." occurs and the partial task to resume execution atomically together" requires try in the caller is enqueued on the caller’s @actorIndependent(unsafe) But Erlang has had great executor. What is the static type of the allowed to refer to actor-isolated state Advice from Apple: use few queues success with its actor model variables declared `async let`? (coarse-grained concurrency). Does 6. The caller resumes execution on its executor Actor-independent (unsafe) intended to be used sparingly for "don't assume that the current the proposed actor model contradict with the return value from the callee (or an error People are discussing whether Where projectedValue would interoperability with existing libdispatch and pthread this? I'm not sure. thrown by the callee) `async let` could be modeled as a give you access to the task synchronization mechanisms or primitives are all we have property wrapper for local variables handle not subject to actor isolation checking low-level performance tuning David Smith: "We're working available" Might be required if we wanted all closely with the libdispatch team Asynchronous functions aka async functions to be cancellable If an async function throws an error before its = might depend on shared mutable state Critique of the solution Joe Groff: "yeah, one of the Child tasks on making sure the level of real async/await (and cancelling throws an error) first suspension, does the error get that hasn't been modeled by any actor "Having put a lot of effort into underappreciated aspects of concurrency is appropriate for propagated from the `async let` initializer or removing multithreading from having lightweight tasks is being People reading "async" might not Yeah, but wouldn't people learn the underlying system." from the eventual `await` call? Which is the better model? Can be called from anywhere, allows incremental adoption of systems to make them go faster able to write concurrent code Should all async functions be expect it to be an unwind point this quickly? like @actorIndependent actors into existing code bases over the last few years, this is without inflicting multithreading throwing? "if the closure passed to Task.withGroup Jens Ayton: making async Unknown something I'm trying to keep a on yourself when one thread will exits without having completed all child "Actor code (which by definition throwing "essentially turn Swift close eye on" do" tasks, any remaining child tasks will is all new code) can call into Does this mean every new class error handling into a C++-like automatically be cancelled" existing Swift code with must become an actor class or "By starting from the middle exception system" unknown actor isolation" actor-annotated? Dave Abrahams: "concerned (e.g. without updating the E.g. for dynamically sized arrays that this proposal may be memory and ownership By extension, key paths cannot Actor isolation The compiler will check that code follows the ignoring the foundations that models), choices incompatible Properties and subscripts be async because they work The task group will implicitly await all checking rules for what can access what proven concurrent programming with systems programming may cannot be async with properties and subscripts subtasks at the end of its scope But they can only access is built upon." be baked into the language." "We intentionally do not want to "but you can create a detached Terminology comes from Nathaniel isolated/actor-independent state "it provides a [good] design pattern to make that very easy because it task to do it" Smith's Structured concurrency blog Actors can conform to But if actors can fulfill sync requirements, "A major point of introducing actors is to get rid of help structure concurrent code, but is I.e. can I call an async function undermines structured `Task.withGroup` to create child posts (and Python library, Trio) synchronous protocol "and the as-yet-unpitched "It is important for actors to be able to that "would allow existentials and generics shared mutable state – and the corresponding bugs not far enough IMO. Also, taking a Fire-and-forget async functions without awaiting on it? concurrency" tasks dynamically Nursery requirements asyncHandler feature is meant Old name for task group conform to protocols, since protocols to "escape" an internal unsychronized that go with them. This is the key to providing a safe half step here will introduce serious to support certain use-patterns Chris Lattner is not sure this is a are the primary abstraction method over pointer the the actor internals, which breaks concurrent programming model, and one of the problems with getting to the memory that typically favor this" If a single task in a task group good idea which generics and existentials work." actor isolation." Thinks the two-stage approach major failures of actor implementations like Akka." isolation and race safety." throws an error, all remaining is concerning because stage 1 If I have a sync and async function with non-completed tasks will be All actor classes automatically "The proposed "Stage 2" solution to memory doesn't achieve the promise of the same name, can I call the sync one wrap the call in a (non-async) cancelled conform to `protocol Actor` safety isolation (actorlocal, mutableIfUnique, et al) doesn't from the async one? closure, e.g. `{ foo() }()` solve the general use case (it covers a few specific Attempting to add more tasks to PartialAsyncTask is an efficient Wrapping it up as a normal John McCall: "I can certainly imagine Chris Lattner is happy that the subcases) which means that "stage 2" will be less Cancelling a task group cancels a cancelled group will throw representation of the unit of function value would introduce a situations where it might be useful, but roadmap follows his and Joe expressive than "Stage 1"" all tasks in the group CancellationError work to be scheduled lot of overhead Is there an equivalent to I'm not sure there are enough to justify Groff's document from 2017 ktoso: "@actorIndependent and rethrows for async functions? the [substantial] complexity cost" For tasks that should outlive the "If enqueue(partialTask:) is not explicitly The roadmap misses "the extremely important @actorIndependent(unsafe) exist to address current scope provided, the Swift compiler will provide case [of] data structures that use fine grain this concern and allow developers to step out Async functions still return a value, but they have protocol Actor: AnyObject { a default implementation for the actor, locking and lock free structures internally" (for of the rigid rules and use external one or more suspension points where they can `runDetached` returns a task Implementing enqueue(partialTask:) func enqueue(partialTask: PartialAsyncTask) with its own (hidden) queue." expert programmers) synchronization whenever necessary" allow the thread to perform other work handle, which is essentially a allows actor classes to provide their } future own executor "The default actor executor will be more I.e. tasks that deliver more than Every caller of an async function efficient than enqueuing something as a one result, like a Combine async is "contagious" (like throws) must also be async suitable for operations for which block on a DispatchQueue." publisher the program does not need to The point of async/await: "you map Actor protocol Detached tasks observe completion extension Actor { Open points that are not yet fully John McCall: "Asynchronous streams are an concurrent code to a simplified sequential The actor interleaving problem To explicitly enqueue work on an func run(operation: () async throws -> T) async rethrows -> T designed Asynchronous streams active point of design. I would expect that at the view where it's easy for humans to reason destroys this simplified view "If fn obviously wants to start on some actor, use the run method: } basic level there would be some sort of about what happens" somewhat actor, the task will start there; if not, or interaction between tasks there, but not an "`Task.runDetached(fn)` is fundamentally just Swift can't statically figure it out, it'll "one could take a class that "We could then say that calls to async The goal is not to create a Swift- inherent connection to actors" creating a standalone task object and then submit the task to a generic global might be hard to turn into an functions on such a class would get specific thread pool submitting a partial task that starts running fn thread pool." actor, e.g., because it's scheduled on the actor, so you're getting synchronous function Use an existing thread pool "On Darwin, we'll be sitting on to some appropriate executor, depending on Non-actor classes can conform to the enmeshed with a non-actor the hop-to-queue behavior for async calls I.e. you probably don't want to protocol, but they don't get the actor class hierarchy, and conform to (== less boilerplate) for free, but not the thread provided by the system if top of Dispatch's thread pool. what fn does" customize Task.runDetached (and it's isolation benefits the Actor protocol." advantages of enforced data isolation." possible On other platforms it's less not customizable currently). You're asynchronous function certain what we'll do" more likely to customize the executors Having both an Actor protocol suspending an async function and and actor keyword is It might be possible for developers to For interop with non-async code select a different underlying thread pool confusing suspension point Implementation (e.g. completion handlers) "any userland thread library that "A global actor is still an actor, it's just globally "So @MyGlobalActor means that a Task = the analogue of a thread Allows for wrapping callback-based scoped — there's always exactly one that function is supposed to execute on for asynchronous functions On an ABI level, Swift's async follows that ABI should be code and presenting them to clients as Task = an operation that runs functions will be split into (more perfectly capable of hosting That would include green actor, and you don't have to have a specific that specific global actor, whereas an async function Singleton actors, often defined reference to it to use it, which gives us a lot actor-independent means it doesn't asynchronously. All asynchronous When an asynchronous function calls or less) C ABI functions Swift async functions." threads by libraries more language flexibility to tie things to it." care what actor it executes on." task functions run as part of some task. another asynchronous function, the call is the resume function must be called still part of the the same task, even if the Will async/await be back- John McCall: "we anticipate exactly-once on each execution path deployable to older Apple being able to deploy backwards Canonical example: @UIActor call has to change actors. the operation may take (including any for the main thread platforms? to at least some extent" UnsafeContinuation error handling paths) An async function can create a child task. However, this concurrency is What are the naming guidelines The proposal doesn't Without having to define all Child tasks inherit some of the structure of bounded: a function that creates or else it will be impossible to these functions in a single class their parent task, including its priority, but a child task must wait for it to for async functions? recommend an Asynx suffix define useful semantics for Annotated functions, methods, variables captures in the operation child task can run concurrently with it. end before returning Overload resolution will favor always execute on the specified actor "A function, property, subscript, or initializer function, which could otherwise declared with a global actor attribute becomes Document has a Detached task = a task that can The pitch allows overloading the sync version in sync You call Task.withUnsafeContinuation, run concurrently with the actor-isolated to the given global actor." very good glossary detached task outlast its spawning task Designing with Should sync and async versions between sync and async contexts and the async version the resume function must be and then continuation.resume from the continuation async/await of the same operation have the functions (unlike throws and in async contexts called exactly at the end of the The type must provide a static Partial task = the basic unit of closure this unavoidably introduces @globalActor same name? non-throws) Chris Lattner fears this will operation function's execution shared property that provides schedulable work (from one some overhead to the use of struct UIActor { overcomplicate the type checker the singleton actor instance partial task suspension point to the next) these continuations static let shared = SomeActorInstance() Make sure your async functions Global actors Defining a global actor } Another potential syntax: global public global actor UIActor { Like the GCD scheduler "Developers must carefully place the resume are cancellable if possible actor declaration implicitly func enqueue(...) {} calls guarantee the proper resumption semantics declares the singleton variable } a service which accepts the executor The compiler doesn't provide of unsafe continuations, lack of consideration for submission of partial tasks and diagnostics for these rules a case where resume should have been called @UIActor arranges for some thread to run them (hence "unsafe") will result in a task hanging forever" func touchesEnded(_ touches: Set, with event: UIEvent?) { // Executes on UIActor, has access to UIActor state Like a serial Dispatch queue not a perfect cure for task starvation–if } Should this be called "interleaved executor" or the task is the highest-priority task in the exclusive executor types "interleaving executor" to make it clear that tasks system, it might go immediately back to can be interleaved at every suspension point? Allows a task to suspend itself executing–however it can be useful extensions Task.yield() voluntarily specific patterns of long running tasks Declarations that propagate the actor acts as an exclusive executor protocols Suspends the task until a global attribute to their members actor isolation deadline by default (i.e. members can still Mandatory propagation to override) witnesses if declared in the actor class Task.sleep(until:) Like sleeping a thread, but does Using a global actor protocol requirements same module not block the thread global actor a class to its subclasses E.g. @UIActor class UIView: NSObject { … } Inspired by Trio (Python) and Kotlin an overridden declaration to its overrides (mandatory propagation) How does cancellation of a child task Yes, they can be sure. Control work? Can the programmer be sure does not return until the child Actor classes that cancellation has completed when task actually completes (e.g. by Declarations that can't have a Stored instance properties of a scope exits, not just signalled? throwing CancellationError()) global actor attribute actor classes Functions are expected to respond promptly to cancellation deinit Common reaction: throw "The difference between a well- CancellationError() John McCall: "To me, something can be encapsulated actor class and an logically be an "actor" — that is, it fills a ordinary class with primarily Since cancellable tasks must generally similar role as a language-supported actor async methods is very little, John McCall: "Lightweight threads are be called with `try`, you can always see — without necessarily being a good fit for basically just the semantics of pretty contrary to our interoperation the places where cancellation can occur the language-supported actor features." extension methods" System threads vs. Lightweight goals (with C/Obj-C/C++). Swift needs Swift's actor classes are not the threads (green threads) to be running on a system thread." Tasks that perform synchronous work E.g. GPU code may be a good fit for Translate Objective-C completion- To avoid source breaks, the clang only way to define actors must check (repeatedly) for cancellation using the actor model, but not handler methods into async Swift importer can import an Obj-C API in One of the main Akka developers (ktoso) Cancellation is cooperative and necessarily using Swift's actor classes methods both forms works on the Swift implementation synchonous If the task has a cancellation handler, that is called immediately Actors are not limited to Dispatch queues. Allow async Swift methods to be Was Swift's model Phase 1 of the Swift plan They only need any form of mutually @objc (exported as completion- Actor classes can also be marked inspired by Akka? introduces an unsafe actor I think this is only true for tasks exclusive scheduling mechanism handler methods) @objc model (without full isolation) akin cancelled via Task.Handle or as cancellation is not introducing to Akka actors part of a task group. An async any new control flow -- it is just a "Actors are an important design pattern Control over how completion- Structured Concurrency function or `async let` you await chance to check for it and throw Actors and a good "safe default" for most (but not Provide Objective-C attributes to handler-based APIs are translated Two variants: Akka Untyped and Chris Lattner wants this model: "actor Akka on can still return a normal or return before or during all) concurrent programming abstractions." tune the translation of Objective-C into async Swift functions. Akka Typed result performing some work declarations are islands of single threaded-ness Task that never check for cancellation "I don't see why this is a better model APIs Specify when an Objective-C APIs is Akka actors have "behaviors", with a queue that protects them, and which are Executor = a service which accepts may run to completion, but their result Mathew Johnson: Zio (Scala) cancels "I see actors as living among Is the proposal advocating for a – to me, it is muddling the behavior part of a particular global actor. i.e. they can dynamically change This is spelled This was easier in Akka Untyped always communicated with asynchronously" the submission of partial tasks and will be ignored because they have tasks automatically at a suspension There is an `uninterruptible` other types of concurrency model where the "other and responsibility of "real actors" by what message they respond to "actor.become(newReceiveFunc because actors were not typed arranges for some thread to run them already been cancelled point and considers it a feature feature for tasks to opt out (pthreads!) and other forms of synchronization" possibilities pulling things that are out of model into "A Swift async function will always depending on state tion)" over their message type synchronization" become part of the actor model. their design." suspend, return, or (if it throws) Because of this, the function can avoid "it is a design that has worked produce an error. For completion- Matthew Johnson mentioned Zio unnecessarily suspending when well for them. It ensures If an actor calls an async function, the current handler APIs, it is important that the a couple of times as a I don't know if the Swift team making a call to the same executor Other libraries do this differently unnecessary work is skipped context gets suspended. It's then possible that completion handler block be called Scala Zio successful model. took inspiration from Zio An async function always knows Another post by Matthew when there is no demand for a the same actor is invoked from elsewhere while exactly once on all paths" the executor it's running on allows the function to resume Completion handlers must be called Structured Concurrency idea Johnson on Zio: Cancelled Zio task’s result" the first function is suspended executing on the same executor tasks automatically stop at the exactly once The compiler can insert code for Python comes from Python community it started on next suspension point This would imply that all `async` "As soon as you await on detecting such bugs at runtime Compared to other languages functions are `throws` and Exclusive executor = runs its Executors can still reorder tasks async/await is syntactic sugar "Each async call is potentially a suspension something, you need to think `await` would imply `try` The pitch proposes force- partial tasks sequentially, not in that are submitted to them (e.g. Misc JavaScript for promises point where other code could be interleaved hard about things that you didn't unwrapping, i.e. passing a nil value Executors on the actor" need to think about before" parallel by priority) Pioneered(?) the actor model Joe Groff is concerned that "every potential await would crash becomes a potential unwind point, which makes any How to handle nil, i.e. when the Obj- Swift provides a default executor Different actor runtimes in different In the Swift model, actors don't sort of transactional logic across async calls C implementation calls the "A default of optional when true implementation, but both actor classes languages take different approaches Erlang Erlang actors form a dependency tree, have parent/child relationships, Should cancellation be potentially problematic" completion handler with (value: nil, optionals are rare is essentially a and global actors can suppress this and where child actors can fail and parent but such a model can be built on cooperative or should it be "We are taking a step into a more Pitch: Concurrency Interoperability error: nil) technical debt generator. It provide their own implementation. Interoperability with with Objective-C (10/2020) actors can restart them top possible to cancel a task at "we should make async and permissive direction than other actor encourages code to not be audited, Chris Lattner: "I am pretty skeptical that throwing orthogonal from each I.e. actors are reentrant by end-users need not interact with executors every suspension point? runtimes here in the design." Objective-C "I think it's a mischaracterization to and to just stick with the optional Invented async/await implicit cancelation would scale and compose other, and cancelation would default directly, but rather use them implicitly by Ben Cohen: this is open for call defaulting to optional in this case default, because that's "safe". But Cancellation well. I think that explicit is more predictable only be possible in throwing ktoso: "I do think we'll need the ability Alternatively, a pretty "actor way to invoking actors and functions which happen discussion "conservative"." this leads to an anti-pattern" async/await is sugar for and reasonable" contexts" to uninterrupted/atomically { ... } or think about it" is to spawn more child to use executors C# promises something like that... but this has not actors for every "linear" execution "the proposal doesn't have any Actor messages being async ktoso: "it would be optimal if Swift had panics been designed yet" one needs special rule for it. What will happen: Orleans: .NET distributed Introduced the Virtual Actor prevents deadlocks, but it also or "soft faults" which a task handle could means actor functions can be you pass a queue (whatever queue computing library Model "While reentrant actors still technically isolate; I'm not in love with errors for interleaved you want) that you'll get called back eliminate data races, they create a very Discussions about concurrency This is why non-throwing tasks [cancellation]. Embracing a let-it-crash mindset on, but then we'll immediately get a racy programming experience, akin to have been ongoing since at The community had ample time don't really exist, i.e. Task what we use to model these things." queue" across multiple threads" Cancellation is modeled as an "That makes it something of a This means task can't really "The compiler-generated "hop back error thrown by the task and you have to be prepared for Dave Abrahams: "a massive fait accompli, which nobody can handle cancellation "nicely", e.g. to the right executor" code (which Porgrammers must take care to the actor's mutable properties to document set is being exposed reasonably challenge at a by returning a partial best-effort Dave Abrahams: It does prevent Some Objective-C APIs allow you to can be optimized away if the leave `self` in a consistent state have “shifted underneath you” This requires similar care as for the first time, with very little fundamental level without result deadlocks, but you can still write specify a callback queue. Will this be compiler realizes that it doesn't The proposal process whenever they call `await` when the await is over writing multithreaded code prior opportunity to discuss the implying a huge cost to the "racy" code somehow integrated with actors? matter where the code executes "We are considering if we should offer some To handle this differently, "we'd need approach here in the forums" proposers." "IIUC part of the reason async/await has been before its next suspension point or How much input can the way to annotate functions as such “this to wrap up whatever executor we so successful in other languages is that it lets us return) is likely to be more efficient community give when the core This is a massive change, requiring function can only be in async context, but I have as either a DispatchQueue or reason about async code using the same tools than wrapping up the executor in a team dumps several massive massive implementation work. This work `Task.isCancelled()` is an async function to guarantee I will never suspend in this an OperationQueue, and then that apply to synchronous code, but reentrancy DispatchQueue or OperationQueue." proposals at once? has to be ongoing for months or years. make sure it can only be invoked in an function” which then would not need to be suppress the normal "hop back to the async context – but it will never suspend awaited on" seems to substantially break that illusion" right executor" code." An option could be to drop the queue We need to start somewhere "These actors provide only weak parameter from the imported API and But can the core team handle Deadline ("point in time") vs. Intentionally using deadlines Is this a desirable/ Greg Parker: "proposal omits the protection against other data- pass nil this differently? Doug Gregor: "That Swift would pursue async/ timeout ("duration of time") because they compose correctly understandable programming await with actors should not come as a model disclaimer that "data race" is narrowly- corrupting race conditions because of Doug Gregor: "it's a bit tricky to make surprise, and without the level of detail Task can query the current deadline defined: a single access to a single the threat of re-entrancy at any await." this work. The issue is that the provided by these proposals I'm not sure we'd and throw immediately if they know field is protected from concurrent Situations the pitch doesn't handle "IMO actors should not be re-entrant by translation is a two-way street: we be able to have a meaningful discussion." they won't have enough time access to that same field" could import an NSProgress- default. Fewer deadlocks but more data- returning method as async and Exceeding the deadline means corrupting race conditions is a bad trade-off." ignore the progress (for example), the task gets cancelled Chris Lattner is also concerned, but we don't have a way to Tasks can have a deadline Using API like receive(on:) with a Dispatch but "there are good reasons why "not only does it define away implement a corresponding @objc queue meant that you could pass through the proposed direction is deadlocks, it composes to large method in Swift and produce a dispatch-specific types, which support theoretically better" scale designs better" reasonable NSProgress result for different kinds of clocks, or one of our APIs that return an NSProgress in Objective-C" standard convertible ones. "For example, you could write code that is Tony Parker: "In Combine we generic over another actor, but must have addition to having a completion async functions can't return one sync Deadline and time types are all not decided to abstract time using a "This turned out people can define a test scheduler with an the same execution context as Self, or a handler and one async result, so this Apple folks have this on their radar designed yet, needs discussion with protocol because we wanted to to have a lot of integer time. It allows control of the execution concrete known execution context" NSProgress pattern doesn't have a as an important use case that should Foundation and Dispatch teams avoid defining a new type." great benefits." timeline, making testing instantaneous and Should actor types be able to be "If the Actor protocol included an Matthew Johnson with an direct correspondence be solved deterministic. One really cool thing about this generic over their executor/ associated type representing the actor’s example of what he's thinking "The compiler could take advantage of this to is that you can accurately reproduce a race serialization context queue this would become possible" about allow synchronous access to synchronous condition. API of actors from other actors in generic code (as long as they are constrained to async functions are always share the same ActorContext)" running as part of a task This goes against Apple's Unless actors can target the The async function's frame is recommendations to use dispatch queues of "parent" This is where the the stack allocated off the C stack from By encouraging developers to relatively few queues actors frame of async functions is the start, not copied off the stack define many actors, each with allocated on suspension their own private queue Serialization contexts that could be shared among multiple "Our current implementation design actors could mitigate this for the task-local allocator (in theory, "That allocator will be fully torn Tasks Tasks have a local allocator not actually implemented) is that it's down when the task completes, "Applying concurrency without care a stack-discipline small-slab allocator so a task handle that's now just leads to terrible performance" that generally won't return memory to a satisfied future does not pin "Actors encourage to protect shared state This seems to be an the general allocator until the task any memory associated with with queues instead of locks. Dispatching implementation detail of the completes" running the task" small tasks to queues is inefficient." default actor implementation tclementdev You have to write a while loop to "Actors encourage developers to get the completed items and put not think about threads" them e.g. in an array The primitive for creating task The re-entrancy aspect is another groups `Task.withGroup` Something like: argument that actors can be overused requires a lot of boilerplate It would be nice to have a library extension Collection { Because only async functions API that worked in terms of a func map(_ transform: (Element) async -> T) async -> [T] Making things async prematurely can call async code Collection } forces big chunks of the codebase to Concrete example of how `await taskGroup.next()` returns become async as well seemingly innocuous code can the next completed task, not in Does having actors as a become a problem because it order of enqueuing language feature promote bad must be async concurrency design patterns? Task groups John McCall: "There is a goal that "Swift actors don't normally initiate work creating a child task via a nursery will themselves, they act in response to requests by be somewhat responsive to system tasks, and tasks wait for their requests to load, which is why it can suspend. We’ll complete by default. So the places that can "go have to see how successful that is." wide" are the places that can create new tasks, not the places that can create actors." "Therefore, while suspension in add() This means you can theoretically will help to control concurrency, it won't "This creates an extremely important optimization Child tasks start running overwhelm the system by starting help with peak memory usage. The opportunity, because the most basic operation in immediately when added thousands of tasks application would need to accumulate a scheduling is not an always-asynchronous But is it effective? "Say the code is million of task results in memory before "dispatch" but a synchronous-to-the-task "switch"" taskGroup.add is an async operation attempting to add a million tasks to a group. it starts consuming them." John McCall: "Introducing a new to allow the task group to only keep a It would be suspended on the add() call, actor does not, in itself, add any Only if there's contention — if "dispatch" vs "switch" limited number of child tasks but would not yet reach the next() call to "Maybe a better approach for back new concurrency" the new executor is an already- enqueued at any given time actually consume the results." pressure is to let the caller immediately the scheduler will attempt to follow running actor — will we actually know that they can't add more tasks the task across actor boundaries suspend computation now, and instead should focus on consuming the results?" "we also hope to avoid some of "Since Swift will not need to block threads the thread-explosion problems except on currently-running work (e.g. Example: the UI thread on Apple that can plague systems like when waiting on a mutex), the scheduler platforms is a high-priority executor; Dispatch when many tasks are does not need to spawn new threads in any task submitted to it will be run with runnable at once" the hopes of unblocking the queue" The priority of a task does not high priority for the duration of its time necessarily match the priority of on the thread. This does not affect the The enforcement is static via the type system, so Task priority its executor formal priority of the task. values of actor-local type cannot be shared across actor boundaries, including by being stored in "Futures" still exist though – they are called memory that can be accessed by multiple actors, Future values which are free to "pass Task.Handle but should be used rarely because such as a property of a non-actor-local class. And around" undermine structured- they miss out on some cool automatic context/ Types are actor-local, not you cannot "escape" a value of actor-local type into concurrency and thus are a bit priority/deadline propagation as well as bounded Actor-local types variables/instances a type that's not actor-local discouraged by this design concurrency features Where full actor isolation can't be John McCall: "We really want to achieved, "we will continue to rely on discourage people from doing the undefined behavior of such "In no case do we have any intention higher-order manipulations on "If you're fetching something and accesses, and so again we will be of defining semantics for concurrent task handles as is common with then doing something with the able to assume a single-threaded conflicting accesses to the same non- a lot of functionally-oriented result, you should be structuring model for non-atomic memory" atomic memory" futures libraries" those into the same task." Swift offers only extremely modest atomic facilities "Cancellation through the handle right now, but we expect to fill these out in time. Futures is a notable exception, but With full actor isolation, "we will Those general atomic facilities will almost certainly cancellation is a slightly different be able to assume a single- be declaration-driven. beast than just succeed/fail." Continues to be threaded model for non-atomic single-threaded memory" All memory is non-atomic except The restrictions that we intend to impose as part "Those two "sides of the same as accessed through explicitly of data isolation will also be declaration-driven. coin" (read/write) must not be When is memory "non-atomic"? atomic facilities. exposed on the same type, as it So we expect that it will be straightforward to "Task.Handle is a "future", and is gets increadibly confusing who identify a general principle that allows atomic not a "promise". It is the "read is responsible to complete it." declarations to be safely accessed from multiple side", it is not the "write side" of That then leaves us with two actors/threads when other declarations can't be. the contract" The promise-shape API is basic questions: "Unsafe Continuation" if they are part of the same containing object, which is to say, a local variable, a global or static variable, or a Task handles "The model Swift is attempting to class property. recommend and push towards is more Memory model about `async let` and async functions, and When are two locations in Different properties of the same struct are generally less about "randomly spawn a bunch of memory "the same"? "the same memory" for the purposes of judging this. The naming of Task.Handle is futures and never await on them", which Note that this is, not coincidentally, strongly (partly) to discourage its use futures can be used for" analogous to the rule used by exclusivity. "Such cancellation is not possible with "our actor isolation model does the structured concurrency APIs or Task ktoso: "unlike structured concurrency, not depend on deep-copying "We intend to allow memory to it could be statically a unique reference task handles give up control of the in general. One always has be be a task objects and preventing any be shared as long as there is Working with Task.Handle structure of tasks, and things can be to set cancellation (or deadlines)" memory from being shared something ensuring that it is it could be immutable if we don't know leaves the Structured cancelled on the specific task object by Any later call to `await try handle.get()` between actors." used safely" dynamically that it's a unique reference Concurrency world anyone (other tasks/actors/threads)" When someone calls will throw CancellationError, even if the it could have only fields that are `handle.cancel()`, the task sets task itself has never checked for somehow safe its isCancelled flag cancellation (it may run to completion) What's the target number of actors expected to run in a given system (order of magnitude per Designing with actors CPU)?