Swift - Structured Concurrency - Actor
Tags: swift, structuredconcurrency, asyncawait, actor
Published: 2023-12-21 17:54:08

Actor - Way to serve the incoming requests with shared mutable state, protects data from inadvertently modified/accessed. These are otherwise enforced by Locks, serial queues, Semaphores.

1. Thread safe requests for the shared state - ex, Multiple reads during write
2. Serialize multiple write requests. ex, write-1 vs write-2 initiated at the same time.

- actor is similar to class nominal type, reference type, has methods, properties isolated to that actor, can have protocol conformance. NO inheritance - so no convenience initializers, no final or no override with actors.
- Properties and methods within actors are async in nature, so, any access to properties/methods outside actor should be marked with `await` and any references of other instances of actor within the actor should also be with `await` while internal references within actor of properties are synchronous in nature so, it could be direct access unless its within a `Task.detached` which would require `await`.
-- Every access request to actor's method or property creates a potential suspension point - meaning the request could be served by the actor right away or within a moment or 2,basically saying that "serve me as soon as you can"
- Internally, its a private serial queue processing 1 request at a time in the order in which the requests are received unless specified with priority escalation.
- There could be unprotected state within the actor which could be accessed without `await` but should be specifically marked so.
- Care should be taken that internally it will process only 1 request at a time by a single thread as actor is specifically to avoid ambiguity in shared mutable state and avoid data races, so performance consideration to handle multiple requests at a time is not the architecture design of actor. Creating multiple instances does not cause any perf issue rather the time spent on potential suspension points when called by the requests is something that's bound to happen.
- Function Parameter based isolation:
Ability for external functions to execute within the specified actor's isolated context as if this function was defined within the actor, meaning the function could access the actor's properties or methods freely/synchronously but this method could called only using `await`. Though not marked async, it would still be executed in actor's isolated context single thread.

```
actor CachedData {
 private var variable1, variable2, ...
 func doSomething() {
 }
}

class anotherClass {
func anotherFunc(data: isolated CachedData) {
print(data.variable1)
print(data.variable2)
}
init() {
  await anotherFunc(CachedData())
}
```
- Non isolated functions : An inverse of external isolated functional parameters.

-- Can access only constant properties which are thread safe in nature and other non-isolated methods. 

-- Could be accessed synchronously by external methods not within the actor without using `await`.

-- If this method requires accessing any actor isolated properties or methods, it would be as if this method is external to the actor and accessed through `await`.

```
actor SampleActor {
 private var str1 = ""
 private var int1 = 0
 private let str2 = "hi"
 func doSomething() {
 }
 nonisolated func doNonIsolatedCommonForActor() {
  print(str2)
 }
 nonisolated func doNonIsolatedCommonForSameActor() {
  await doSomething()
  print(await str1)
 }
}
let actorInstance = SampleActor()
actorInstance.doNonIsolatedCommonForActor()
actorInstance.doNonIsolatedCommonForSameActor()
await actorInstance.doSomething()
```

My Notes as understood from HackingWithSwift Structured Concurrency series.