# Why do not actor-isolated properties support 'await' setter?

**URL:** <https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867>\
**Category:** Using Swift\
**Tags:** concurrency, asyncawait, actors\
**Created:** [August 9, 2024, 4:17pm UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867 "2024-08-09T16:17:39Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![vns](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/vns/32/37396_2.png) [@vns](https://forums.swift.org/u/vns)\
**Post date:** [August 15, 2024, 10:48am UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/23 "2024-08-15T10:48:19Z")

</div>

> [@Andropov](#):
>
> I don't think locks are the best tool for the job. You get zero compiler help when making the properties of a class thread-safe with locks. Forgot to acquire the lock before accessing or modifying a property?

That's easily addressable by proper structuring accesses to the property. Plus we have `Mutex` now.

> [@Andropov](#):
>
> But that doesn't change with an async setter. Before setting a property, you hop to the actor, modify the property (while the caller `await`s), and then hop back to the original context. You still have _"one entity that handles everything related to this state"_.

Imagine actor in current version, without async setters as a box (mailbox? heh). Inside that box lies the state. You can take a look — like maybe this box is transparent – on what's inside that box. But we can't change what's inside by ourselves — instead we put a "request" to make a modification, that will be processed by the box.

Async setters change this perception, as we can now take something out of this box by ourselves, play with it in any way, and put it back. Box only now can guard that we only one who play with the item, but in general it becomes kinda meaningless, as overseeing can be done by anything, so why to use the box?

Actors propose different outlook on the state in the app. Instead of scattered across various types and domains, it is now isolated, and only in this isolation it gets changed.

> [@Andropov](#):
>
> ```swift
> let totalBalance = await bank.mainAccount.balance + bank.savingsAccount.balance
> 
> ```
> 
> And this code has almost the exact same problem as your example with async setters.

Don't you think that this only proves point that async setters are _bad_? Exactly because you already can have such behaivour with getters. Just with the difference that getters in overall will bring a bit less harm potentially, while still can give undesired behaviour.

And you further prove several points as well with this example

> [@Andropov](#):
>
> The correct approach here would be to avoid composing `totalBalance` from outside the actor, with many tiny separate hops to the actor to fetch individual properties, and instead have a single method in the actor to perform the operation:
> 
> ```swift
> extension BankActor {
> var totalBalance: Double {
> return mainAccount.balance + savingsAccount.balance
> }
> }
> 
> ```

by isolating action to the actor's computed property. You've just improved code by isolating on actor, so why we should promote bad practices introducing async setters?

**We still have unresolved question how async setters will behave here**

```swift
await other.balance = await other.balance + amount

```

or here

```swift
await other.balance = other.balance + amount

```

_(note that both these cases cannot be mitigated by some computed property, because `amount` is external to actor state)_

or even with your example of computed property

```swift
actor ATM {
    // ...
    var withdrawAmount: Int?
}

let atm = ATM()
await atm.withdrawAmount = bank.totalBalance

```

**In which way any of these cases would be resolved?** How clear it would be that there are several suspension points involved?

Compared to (not perfect) isolating this action:

```swift
extension BankActor {
    func withdrawAll(from atm: ATM) {
        await atm.request(amount: totalBalance)
    }
}

```

_(note that this probably also a bit wrong from design point, but isolation makes me think about how better communicate the action, not just use async setter and don't think about it)_

> [@Andropov](#):
>
> Also, to circle back to my point about access control: if you were trying to prevent the former code from being written (where balances can be accessed separately), the way to do it would be... to mark the properties as `private`

That's still orthogonal to isolation. Yes, you might be better hiding `mainAccount` and `savingsAccount`, but not necessarily hiding them makes sense, while isolating does.

---

<div class="post-metadata">

**Author:** ![Andropov](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/andropov/32/35955_2.png) [@Andropov](https://forums.swift.org/u/Andropov)\
**Post date:** [August 15, 2024, 11:36am UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/24 "2024-08-15T11:36:11Z")

</div>

> [@vns](#):
>
> Imagine actor in current version, without async setters as a box (mailbox? heh). Inside that box lies the state. You can take a look — like maybe this box is transparent – on what's inside that box. But we can't change what's inside by ourselves — instead we put a "request" to make a modification, that will be processed by the box.
> 
> Async setters change this perception, as we can now take something out of this box by ourselves, play with it in any way, and put it back. Box only now can guard that we only one who play with the item, but in general it becomes kinda meaningless, as overseeing can be done by anything, so why to use the box?

But this is not a good metaphor. We're not taking anything outside this "box", we're still putting a _request_ to make modifications to the contents of the box, that will still be processed by the box. The syntax for this request may be different (with async setters), but it's still a request, performed inside the box. If the property had an effectful setter (like `didSet`), that code would also run inside the box.

> [@vns](#):
>
> Don't you think that this only proves point that async setters are _bad_? Exactly because you already can have such behaivour with getters. Just with the difference that getters in overall will bring a bit less harm potentially, while still can give undesired behaviour.

It proves that both async getters and async setters can be misused (which I think we both agree on), not that they are intrinsically bad. If the behavior was deemed acceptable for getters, why not for setters? I'm not convinced that the potential for creating this kind of harmful effects of is much higher in setters vs getters.

> [@vns](#):
>
> And you further prove several points as well with this example

The point I'm trying to make is that it's not the compiler's job to police the granularity of the writes to an actor's properties, just like it doesn't police the granularity of the reads to an actor's properties. And if it were (which I strongly disagree it is) then it's already failing spectacularly at that job: you can bypass it with a 3-line setter.

Not all small writes to an actor property are wrong, just like not all big "unwieldy" methods that modify several actor properties at once are correct. The compiler can't possibly know which ones are correct without domain knowledge that only the programmer has.

> [@vns](#):
>
> by isolating action to the actor's computed property. You've just improved code by isolating on actor, so why we should promote bad practices introducing async setters?

Because not all async setters are bad practices. Again, this same argument can be made to remove async getters from actors, as you can also misuse them in subtle and confusing ways.

> [@vns](#):
>
> **We still have unresolved question how async setters will behave here**
> 
> ```swift
> await other.balance = await other.balance + amount
> 
> ```
> 
> or here
> 
> ```swift
> await other.balance = other.balance + amount
> 
> ```

Well, this is definitely something worth discussing, but my intuition is that it should do the same thing a manual setter does. What does this do?

```swift
await other.setBalance(await other.balance + amount)

```

Or this:

```swift
await other.setBalance(other.balance + amount)

```

It must do something, because you can already write that code today.

In any case, if reading and then modifying an actor-protected property in the same line is deemed to confusing (due to the implicit second suspension point between the read and the write), it's within the realm of possibility to have the compiler force splitting the two suspension points into separate lines, so it's clear that the property can change in the middle:

```swift
await other.balance = other.balance + amount
// ` error: suspension point between actor read and actor write requires explicit await

```

With a fix-it to:

```swift
let balance = await other.balance
await other.balance = balance + amount

```

Where it's obvious that `balance` can change between the two suspension points.

---

<div class="post-metadata">

**Author:** ![vns](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/vns/32/37396_2.png) [@vns](https://forums.swift.org/u/vns)\
**Post date:** [August 15, 2024, 12:41pm UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/25 "2024-08-15T12:41:35Z")

</div>

> [@Andropov](#):
>
> But this is not a good metaphor. We're not taking anything outside this "box", we're still putting a _request_ to make modifications to the contents of the box, that will still be processed by the box.

Let's use regular OOP for the sake of an example (_even though actors are not the same as classes at all_)

```swift
final class BankAccount {
    var amount: Int

    init(amount: Int) {
        self.amount = amount
    }
}

let myAccount = BankAccount(amount: 1_000_000)
myAccount -= 10_000

```

That seems legitimate with classes, arguable — but let's put aside encapsulation details here. Is this modification still processed within `BankAccount`? I'd say no, because modification happens _somewhere_ outside of the class. There is a little control with `didSet`, and that's all.

Now, back to actors, with async setters you don't put request for modification with async setter, you ask to put new modified state. Not that the box changed its state, you put new state into it outside. You first processed, then ask box to apply this change internally.

That's may seem like just a matter of words choice, yet this is a significant implication on how you structure code: actors should isolate their state, allowing modifications of this state outside of an actor breaks this model.

> [@Andropov](#):
>
> The point I'm trying to make is that it's not the compiler's job to police the granularity of the writes to an actor's properties, just like it doesn't police the granularity of the reads to an actor's properties. And if it were (which I strongly disagree it is) then it's already failing spectacularly at that job: you can bypass it with a 3-line setter.

We are talking about two different perspectives: I say that you need to think and design actors in the way that you don't need setters, not seek for bypassing; in the way where async setters simply doesn't needed.

Compiler doesn't police you from granularity of the writes, it just makes you think twice. If you really need granular update, writing 3 lines _occasionally_ isn't a problem, while the entire point of allowing based on the fact of their often use.

> [@Andropov](#):
>
> Well, this is definitely something worth discussing, but my intuition is that it should do the same thing a manual setter does. What does this do?
> 
> ```swift
> await other.setBalance(await other.balance + amount)
> 
> ```
> 
> Or this:
> 
> ```swift
> await other.setBalance(other.balance + amount)
> 
> ```
> 
> It must do something, because you can already write that code today.

Let's assume async setter would transform into legit code with explicit setter as we can write manually today, that's reasonable choice of behaviour. How safe are this modification of a state? What is in between of getting `balance` and setting it, it gets modified? We end up with the wrong transaction! And that's exactly the issue: by permitting such modifications in easy way (write `=` is much simpler than define method and call it), we also would make it much easier to write wrong code.

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [August 15, 2024, 12:59pm UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/26 "2024-08-15T12:59:51Z")

</div>

I’d actually claim that actors are the most true to the actual intent and principles behind OOP, and the fact that you can grab innards of a class and do a += 1 on it is the doing model “breaking“.

Turns out actors outright ban such loose treatment of the model — encapsulating is _enforced_ and what previously sloppy accessing another objects inner state you could have gotten away with, now you don’t.

This goes all the way back To Allan Kay terming the term “object” and OOP and SmallTalk, and a pretty interesting response he [wrote in 1998](http://lists.squeakfoundation.org/pipermail/squeak-dev/1998-October/017019.html).

> I'm sorry that I long ago coined the term "objects" for this topic because it gets many people to focus on the lesser idea.
> 
> The big idea is "messaging" -- that is what the kernal of Smalltalk/Squeak  
> is all about (and it's something that was never quite completed in our  
> Xerox PARC phase).

—

But anyway, in practical terms looking at all this from the perspective of “what it looks like to be doing” and “what it’s actually doing” is helpful imho. both the `await other.balance = other.balance + something.balance` as well as _chained_ writes which are quite scary IMHO such as: `someActor.anotherActor.something = bla` are IMHO very difficult to understand what kind of transactionallity we’ll be getting here. And the answer is going to be “almost no guarantees, this code is very likely broken”.

Because mutations often are _more_ than a trivial +1, but include coordination between various domains and a number of operations that should perhaps be performed atomically etc… async setters are a footgun that we’d rather avoid IMHO.

It’s the same reasons why not every actor has a `func run(whatever: (isolated Self) -> T) async -> T` on it, because actors (and OOP!), are a way to organize your code/logic, and not just `bob.run { tons of code here }` solve everything without actually centralizing logic. If all your actors have is boring “setX” methods… maybe they’re not actually containing any logic that’s important to isolate the state/logic with — to me that’s a _smell_ to act on in the code which ends up having to write this, and not something to resolve by “just” allowing the property setters on actors.

At the other hand, I am curious if perhaps this is somehow showing up here specifically because “so much of stuff is on main actor” **AND** “a lot of existing libraries require setting properties”. I would definitely welcome more real world examples of the problem showing up. The 3-10 line snippets we have so far in the thread IMHO (personal opinion) are not really justifying exposing the huge footgun to avoid a few lines of code that _should_ often make you step back and rethink if you’re doing what you should be doing.

---

<div class="post-metadata">

**Author:** ![Andropov](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/andropov/32/35955_2.png) [@Andropov](https://forums.swift.org/u/Andropov)\
**Post date:** [August 15, 2024, 2:58pm UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/28 "2024-08-15T14:58:09Z")

</div>

> [@ktoso](#):
>
> I would definitely welcome more real world examples of the problem showing up. The 3-10 line snippets we have so far in the thread IMHO (personal opinion) are not really justifying exposing the huge footgun to avoid a few lines of code that _should_ often make you step back and rethink if you’re doing what you should be doing.

I recall one particular case in which I ended up needing to write lots of setters by hand.

I'm writing a small application that renders the 3D structure of proteins using Metal. This had some interesting requirements:

- During a `draw()` call, the state of the renderer must not change (for example: can't change the rotation, or the number of objects to draw mid-call).
- Since the app can show multiple (independent) windows, it must be possible to run different `draw()` calls in parallel if they belong to different windows.
- Rendering must not happen in the main thread.
- The user is able to change some properties of the renderer's state (rotation, color...) from the UI. Ideally, these properties would take effect on ~the next frame, but it's fine for them to take much longer.

So I created a `Renderer` actor to back each window. The properties protected by this `Renderer` actor conform the shared mutable state of the renderer.

- `draw()` is a synchronous method of this renderer, so while a draw call is in progress, nothing can modify the state of the renderer.
- Different actors can run `draw()` calls for different windows in parallel.
- All this happens outside the main thread.
- When a user makes some changes in the UI, it calls a method on the relevant instance of this actor. Here's where setters started to appear.

Something like this:

```swift
actor Renderer {
    private var modelBuffers: [MTLBuffer]
    // ...
    func draw(in layer: CAMetalLayer) {
        // Somewhat long operation, state must not change here!
        // ...
    }
}

```

This actor is designed with lots of "big" methods that change many properties of the actor at once. For example, when the user loads a new model, there's a (synchronous) method that populates all the Metal buffers:

```swift
actor Renderer {
    private var modelBuffers: [MTLBuffer]
    private var frameData: FrameData
    private var needsRedraw: Bool
    // ...
    func populateBuffers(_ models: [Model], config: VisualizationConfig) async {
        // Some stuff is computed asynchronously first...
        let newModelBuffer = await createModelBuffers(
            models: models
            config: config
        )
        // Once everything is ready, other properties are updated all
        // at once.
        self.modelBuffers = newModelBuffer
        createColorBufferIfNeeded(models: models)
        self.frameData.atomCount = config.atomCount
        self.needsRedraw = true
    }
}

```

This has worked great in practice 😁 The state of the renderer is always consistent, thanks to compiler checks. However, some properties, while being part of the shared mutable state, are safe to modify by themselves without needing to make changes to any other actor-protected properties. So I ended up with (lots) of setters like these:

```swift
actor Renderer {
    // ...
    func setAutoRotating(to autorotating: Bool) {
        self.scene.autorotating = autorotating
    }
    // ...
    func setColorFill(_ color: CGColor) {
        self.scene.colorFill = color
    }
    // ...
    func setSunDirection(_ sunDirection: SunDirection) {
        self.scene.sunDirection = sunDirection
    }
}

```

It's still important for these properties to not change during a draw call. For example, it would be **bad** if the shadow map generation render pass saw a different sun direction than the main rendering pass. And yet, as long as you don't change this property in the middle of a draw, it's actually fine for the property to be changed from anywhere.

* * *

In fact, I must say that this limitation related to setters confused me for a while, and led me to wrong design decisions before I realized I could just write a few small setters. The errors I got were something like:

> Actor-isolated property 'colorFill' can not be mutated from the main actor

So I just assumed that this meant that it was **fundamentally wrong** to mutate this actor's properties from another actor, while all I actually needed was... a 3 line setter.

---

<div class="post-metadata">

**Author:** ![j-f1](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/j-f1/32/12287_2.png) [@j-f1](https://forums.swift.org/u/j-f1)\
**Post date:** [August 15, 2024, 3:42pm UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/29 "2024-08-15T15:42:19Z")

</div>

Could you represent all of the view-controllable state (except the model choice) as a struct stored on an `@MainActor` view model, then synchronously grab a copy of that state before each render pass? That way, the UI will immediately see any changes the user makes reflected back in the controls, and value semantics mean it is impossible for that configuration to change out from under the renderer. For things like the 3D model, you’d still need an actor to manage fetching the updated model choice from disk, but that could be a separate actor from the renderer which could just grab a struct containing the current Metal buffers at the start of each render pass.

---

<div class="post-metadata">

**Author:** ![Andropov](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/andropov/32/35955_2.png) [@Andropov](https://forums.swift.org/u/Andropov)\
**Post date:** [August 15, 2024, 4:21pm UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/30 "2024-08-15T16:21:11Z")

</div>

> [@j-f1](#):
>
> Could you represent all of the view-controllable state (except the model choice) as a struct stored on an `@MainActor` view model, then synchronously grab a copy of that state before each render pass? That way, the UI will immediately see any changes the user makes reflected back in the controls, and value semantics mean it is impossible for that configuration to change out from under the renderer. For things like the 3D model, you’d still need an actor to manage fetching the updated model choice from disk, but that could be a separate actor from the renderer which could just grab a struct containing the current Metal buffers at the start of each render pass.

Oh, the UI already has its own copy of the values, stored on a few `@MainActor` view models, so all UI controls see changes reflected immediately, even if the visualization takes a few ms longer to update. So when the UI is updated -\> `@MainActor` View Model is updated -\> a task to mutate the `renderer` is spawned. It's even possible to keep track of those tasks to disable the specific control while the action takes place.

There are a few reasons I went with shared state with reference semantics instead of copying all state from the `@MainActor` View Models at the start of every frame:

- The combined state is not lightweight enough to copy all of it up to 120 times per second.
- It allows rendering to happen only when a value has changed, using minimal energy if there are no changes.
- It completely avoids requiring a switch to the main thread to render new frames. So even if the main thread is blocked for a few milliseconds, no frames are dropped.

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [August 16, 2024, 12:36pm UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/31 "2024-08-16T12:36:33Z")

</div>

Thanks for the writeup @Andropov ! I'll have to re-read deeper again soon.

Meanwhile I wanted to share a PR which may be of interest to this thread. It's not changing any semantics, but the thread got me thinking that perhaps we can offer some more notes, here's an idea to suggest the method route when hitting the property setter restriction: [[Concurrency] Suggest adding a method, when mutating actor property cross isolation by ktoso · Pull Request #75922 · swiftlang/swift · GitHub](https://github.com/swiftlang/swift/pull/75922) I'll also look into offering "long form" educational notes, which explain the "why" somewhat deeper.

---

<div class="post-metadata">

**Author:** ![taylorswift](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/taylorswift/32/43413_2.png) [@taylorswift](https://forums.swift.org/u/taylorswift)\
**Post date:** [August 16, 2024, 1:31pm UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/32 "2024-08-16T13:31:58Z")

</div>

> [@ktoso](#):
>
> If all your actors have is boring “setX” methods… maybe they’re not actually containing any logic that’s important to isolate the state/logic with — to me that’s a _smell_ to act on in the code which ends up having to write this, and not something to resolve by “just” allowing the property setters on actors.

i understand this argument but i am unconvinced of how useful it is in practice. a lot of actors are born simply because there is a piece of state (which may be as trivial as a monotonically increasing timestamp) that is shared between concurrency domains, and an actor is needed to resolve compiler errors. these named setters add boilerplate to the project and don’t always provide a lot of value in understanding the code.

i’ll also say that i’ve run into more than a few instances where i’ve had the opposite problem where the file containing the actor definition simply got _way too long_ because there was so much code that “had” to be written as methods on the actor due to the “run on” pattern being discouraged.

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [August 16, 2024, 1:41pm UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/33 "2024-08-16T13:41:49Z")

</div>

> [@taylorswift](#):
>
> i understand this argument but i am unconvinced of how useful it is in practice. a lot of actors are born simply because there is a piece of state (which may be as trivial as a **monotonically increasing timestamp** ) that is shared between concurrency domains, and **an actor** is **needed to resolve compiler errors**. [emphasis mine - ktoso]

I see this viewpoint onto concurrency a lot, and it's the root cause of the frustrations, not the actual limitations.

These warnings, errors and limitations are not here to annoy you, and you don't put them into place to "make the compiler happy".

They're there to make you stop and think about your problem facing with concurrency, and resolve it in an appropriate way. They're there to "make the future you happy, who doesn't have to debug irreproducible races and weird states".

Whenever people bring up "just a counter" or "just a variable" examples, I quite honestly say don't use an actor for those (and that's coming from someone entire career with actors pretty much). It's _fine_ to use a lock or atomics, they're there for a reason. Not _everything_ has to be an actor, a lot of things are, but not everything - in Swift at least (and not in some hypothetical actors-only language). I see people getting frustrated because they assume everything _must_ be actors now, but that's not true. After all, we introduced [Mutex](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0433-mutex.md) and low-level atomics, just now, and there may be more things to follow.

You do have to think about concurrency (you always should have but you might have gotten away with it), and sloppy programming won't fly anymore, but that's it, other synchronization mechanisms are viable as well -- especially for "just contains a bunch of values" things. It would not make sense at all to make a sophisticated concurrent datastructure an actor, there's a reason they're highly specialized for high performance using advanced locking or lock free programming techniques.

Yes, we should be pushing the envelope of the ergonomics story and making it easier, but the setters are a touchy subject as they would almost definitely result in breaking assumptions about atomicity and result in _more_ mysterious hard to debug bugs rather than in those situations having to rethink things.

I really don't want to be dismissive here, but out of all the ergonomic improvements I'm personally just not sure we get the most gain, and least pain from the specific async setters topic. More specific real world examples would definitely help so we can think them through together. @Andropov's writeup was very interesting for example.

---

<div class="post-metadata">

**Author:** ![CharlesS](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/charless/32/6488_2.png) [@CharlesS](https://forums.swift.org/u/CharlesS)\
**Post date:** [August 16, 2024, 10:13pm UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/34 "2024-08-16T22:13:15Z")

</div>

In regards to all the comments recommending the use of a mutex, do we really want to use them in a Swift Concurrency context? A mutex or lock blocks the thread while it waits; blocking threads in the cooperative thread pool is something we're explicitly supposed to avoid.

An actor can do much more than a mutex, yes, but AFAIK there's no real reason _not_ to use it as a mutex if that's what you need, and it's a lot safer on the cooperative thread pool due to its re-entrancy.

---

<div class="post-metadata">

**Author:** ![tclementdev](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tclementdev/32/13398_2.png) [@tclementdev](https://forums.swift.org/u/tclementdev)\
**Post date:** [August 16, 2024, 11:08pm UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/35 "2024-08-16T23:08:05Z")

</div>

> [@CharlesS](#):
>
> In regards to all the comments recommending the use of a mutex, do we really want to use them in a Swift Concurrency context? A mutex or lock blocks the thread while it waits; blocking threads in the cooperative thread pool is something we're explicitly supposed to avoid.

Yes we really want to use them in a Swift Concurrency context. What you're saying is a very common misconception, blocking the thread is fine (for short period of times) if the code doesn't wait on future work (semaphores wait on future work and are not ok). There's plenty of locks in use today, including in Swift Concurrency itself, it's fine.

> [@CharlesS](#):
>
> An actor can do much more than a mutex, yes, but AFAIK there's no real reason _not_ to use it as a mutex if that's what you need, and it's a lot safer on the cooperative thread pool due to its re-entrancy.

Use the right tool for the job, that is what @ktoso is saying I believe. If a lock is the right tool, then absolutely use it.

---

<div class="post-metadata">

**Author:** ![nkbelov](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nkbelov/32/34617_2.png) [@nkbelov](https://forums.swift.org/u/nkbelov)\
**Post date:** [August 17, 2024, 6:24am UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/36 "2024-08-17T06:24:18Z")

</div>

> [@CharlesS](#):
>
> An actor can do much more than a mutex, yes, but AFAIK there's no real reason _not_ to use it as a mutex if that's what you need

Sometimes you just _really_ need that your locking is synchronous and doesn't break up your transaction with a suspension point.

---

<div class="post-metadata">

**Author:** ![Andropov](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/andropov/32/35955_2.png) [@Andropov](https://forums.swift.org/u/Andropov)\
**Post date:** [August 17, 2024, 9:56am UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/37 "2024-08-17T09:56:43Z")

</div>

> [@ktoso](#):
>
> here's an idea to suggest the method route when hitting the property setter restriction: [[Concurrency] Suggest adding a method, when mutating actor property cross isolation by ktoso · Pull Request #75922 · swiftlang/swift · GitHub ](https://github.com/swiftlang/swift/pull/75922)

This sounds great, thank you!

> [@ktoso](#):
>
> I'll also look into offering "long form" educational notes, which explain the "why" somewhat deeper.

This would be ✨awesome​✨. It's hard to condense the subtle difficulties of concurrency in one-line errors/warnings.

> [@ktoso](#):
>
> Whenever people bring up "just a counter" or "just a variable" examples, I quite honestly say don't use an actor for those (and that's coming from someone entire career with actors pretty much). It's _fine_ to use a lock or atomics, they're there for a reason. Not _everything_ has to be an actor, a lot of things are, but not everything - in Swift at least (and not in some hypothetical actors-only language). I see people getting frustrated because they assume everything _must_ be actors now, but that's not true. After all, we introduced [Mutex](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0433-mutex.md) and low-level atomics, just now, and there may be more things to follow.

I think locks have been demonized in Apple platforms for so long now that few developers choose them, even when they are the right tool for the job. There's been not one, but two concurrency frameworks (Grand Central Dispacth and now Swift Concurrency) launched in recent memory that put a heavy emphasis on how much better than locks they were. So, understandably, many people resist using locks now, which probably contributes to some of the frustration with Swift Concurrency.

> [@tclementdev](#):
>
> Yes we really want to use them in a Swift Concurrency context. What you're saying is a very common misconception, blocking the thread is fine (for short period of times) if the code doesn't wait on future work (semaphores wait on future work and are not ok). There's plenty of locks in use today, including in Swift Concurrency itself, it's fine.

I've read everywhere that semaphores are not okay, but isn't the problem generally that you must not **suspend** while holding a synchronization primitive? For example, isn't this code with semaphores correct?

```swift
actor LimitTo3ConcurrentJobs {
    private let semaphore = DispatchSemaphore(3)

    func performJob() {
        semaphore.wait()
        doLongSynchronousWork()
        semaphore.signal()
    }
}

```

Even though we use a semaphore here, its usage still creates a well-defined synchronous critical region between each `wait()` and `signal()`. Some threads could be blocked in `performJob()`, but there must be at least one thread alive and running in the concurrency pool that is doing forward progress. The only way this would deadlock is if it suspends before signaling the semaphore, as the cooperative thread pool may end up filled with threads that are all stuck blocked with no forward progress.

> [@nkbelov](#):
>
> Sometimes you just _really_ need that your locking is synchronous and doesn't break up your transaction with a suspension point.

True, and in those cases it's clear that an actor won't work and a lock must be used. But there are cases where you _could_ use an actor as a mutex, because you don't need the mutation to happen synchronously and a suspension point doesn't break the transactionality of the code.

---

<div class="post-metadata">

**Author:** ![tclementdev](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tclementdev/32/13398_2.png) [@tclementdev](https://forums.swift.org/u/tclementdev)\
**Post date:** [August 17, 2024, 1:21pm UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/38 "2024-08-17T13:21:59Z")

</div>

> [@Andropov](#):
>
> I've read everywhere that semaphores are not okay, but isn't the problem generally that you must not **suspend** while holding a synchronization primitive? For example, isn't this code with semaphores correct?

Possibly yes but this suffers from other problems such as priority inversion because a semaphore isn't limited to this pattern and the OS can't know what threads are going to signal the semaphore. I'm also unsure long synchronous work, especially constrained by a count semaphore, is a good idea to perform on the limited cooperated thread pool as this seems like an easy way to starve the pool. All in all, I'd probably look into another way to achieve something like this.

---

<div class="post-metadata">

**Author:** ![CharlesS](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/charless/32/6488_2.png) [@CharlesS](https://forums.swift.org/u/CharlesS)\
**Post date:** [August 17, 2024, 3:00pm UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/39 "2024-08-17T15:00:07Z")

</div>

> [@Andropov](#):
>
> I think locks have been demonized in Apple platforms for so long now that few developers choose them, even when they are the right tool for the job. There's been not one, but two concurrency frameworks (Grand Central Dispacth and now Swift Concurrency) launched in recent memory that put a heavy emphasis on how much better than locks they were. So, understandably, many people resist using locks now, which probably contributes to some of the frustration with Swift Concurrency.

Well, that and the fact that with GCD, blocking the thread on a dispatch queue often caused the system to say, "oh, one of my threads is blocked, let's spin up a new one", and then if you had a lot of stuff going on, you'd end up with threads in the four digits.

---

<div class="post-metadata">

**Author:** ![nkbelov](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nkbelov/32/34617_2.png) [@nkbelov](https://forums.swift.org/u/nkbelov)\
**Post date:** [August 17, 2024, 10:03pm UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/40 "2024-08-17T22:03:49Z")

</div>

> [@Andropov](#):
>
> There's been not one, but two concurrency frameworks (Grand Central Dispacth and now Swift Concurrency) launched in recent memory that put a heavy emphasis on how much better than locks they were.

Can't speak for GCD, but with Swift Concurrency, I'm quite definitely _not_ getting the impression that either locks are discouraged or that SC is claiming to offer a zero-cost replacement. I still heavily use locks when building SC-compatible primitives, and if actors iterate on anything, it's primarily GCD queues, as they most obviously resemble the actors' mailbox.

The issue is perhaps that the mental model for concurrency is already heavy and not everybody would willingly dedicate time to learn all of the intricacies (although I personally would argue that in the modern times it's almost as essential as if statemtents), so people tend to rely on the promise that _this one particular technique_ is going to solve all the problems — no, it won't, but this is how I would otherwise get the impression that actors are suggested to replace "older" primitives.

---

<div class="post-metadata">

**Author:** ![tclementdev](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tclementdev/32/13398_2.png) [@tclementdev](https://forums.swift.org/u/tclementdev)\
**Post date:** [August 18, 2024, 8:44am UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/41 "2024-08-18T08:44:02Z")

</div>

Unfortunately I'm afraid the actor type might be playing a detrimental role and we might have been better served if Swift Concurrency offered isolation domains instead, which you could use to assign many pieces of you program to them (more or less what global actors are). It could have help shift the discourse from "you now need to make things actors" to "assign your existing types to isolation domains" (i.e. developers won't be making new isolation domains for everything but instead be encouraged to reuse them for more things).

---

<div class="post-metadata">

**Author:** ![Andropov](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/andropov/32/35955_2.png) [@Andropov](https://forums.swift.org/u/Andropov)\
**Post date:** [August 19, 2024, 6:18am UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/42 "2024-08-19T06:18:55Z")

</div>

> [@nkbelov](#):
>
> Can't speak for GCD, but with Swift Concurrency, I'm quite definitely _not_ getting the impression that either locks are discouraged or that SC is claiming to offer a zero-cost replacement.

Oh, I may have worded that poorly. I'm not suggesting that the Swift project itself is pushing for Swift Concurrency as a one-size-fits-all alternative to locks (quite the contrary). However, in the wider community of developers for Apple platforms, it does feel like a lot of people are suggesting exactly that.

It may be my personal experience only, though I can't help but notice that many of the most prominent blogs about Swift all have articles following a template similar to _"Here's this code with locks. And now here's the same code using actors instead. Actors are much better!"_ with no mention to when or why locks would still be the best option.

---

<div class="post-metadata">

**Author:** ![tclementdev](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tclementdev/32/13398_2.png) [@tclementdev](https://forums.swift.org/u/tclementdev)\
**Post date:** [August 19, 2024, 8:36am UTC](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867/43 "2024-08-19T08:36:47Z")

</div>

Sadly this is also something that you will not find on the documentation pages for `Concurrency`, `Actor` or `Mutex` at [developer.apple.com](http://developer.apple.com). This would make a big difference if developers could read proper guidance from the documentation, right now all the bits are scattered on twitter, mastodon, wwdc videos and here, which makes it hard to find. And as Andropov said, it is lost in a sea of prominent blog posts sometimes telling you the opposite. The same thing happened with the libdispatch which pushed me to eventually [write a collection of all the dos and donts I could find](https://gist.github.com/tclementdev/6af616354912b0347cdf6db159c37057) (from various posts made by the Apple engineers working on the libdispatch) because Apple would not do so itself.

[Previous page](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867.md?page=1)

[Next page](https://forums.swift.org/t/why-do-not-actor-isolated-properties-support-await-setter/73867.md?page=3)
