Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions TSPL.docc/LanguageGuide/Concurrency.md
Original file line number Diff line number Diff line change
Expand Up @@ -1392,6 +1392,118 @@ You can define your own singleton global actors
using the `@globalActor` attribute,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The paragraph under "Global Actors" needs some work:

Global Actors

"The main actor is a global singleton instance of the [MainActor][] type.
Normally, an actor can have multiple instances,
each of which provide independent isolation.
The possibility of multiple instances is why you declare all of an actor's isolated data
as instance properties of that actor.
However, because MainActor is singleton ---
there is only ever a single instance of this type ---
the type alone is sufficient to identify the actor,
allowing you to mark main-actor isolation using just an attribute.
This approach gives you more flexibility to organize your code
in the way that works best for you."

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed. Did you want to revisit this paragraph more together, or does the edit above fix the issues?

as described in <doc:Attributes#globalActor>.

### Isolation inference

Global actor isolation can be inferred in the code you write. Isolation
is inferred from class inheritance, protocol conformances, and context
where code is written.

#### Classes

If a superclass is isolated to a global actor, the global actor is
inferred on subclasses. For example, the code below has a main-actor
isolated class `Vehicle`, and a subclass `Train` that inherits
main-actor isolation:

```swift
@MainActor
class Vehicle {
var currentSpeed = 0.0
func makeNoise() {
// do nothing - an arbitrary vehicle doesn't necessarily make a noise

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// do nothing - an arbitrary vehicle doesn't necessarily make a noise
// Do nothing: an arbitrary vehicle doesn't necessarily make a noise.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this comment mean? "An arbitrary vehicle doesn't necessary make a noise"? What makes it "arbitrary"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed

The Vehicle class is a sort of abstract or placeholder superclass — it doesn't implement much behavior, but its subclasses like Train do. In real Swift code, abstract base classes like this are somewhat rare (unlike some other languages) because protocols or generics are often better tools. However, it does make a pretty approachable teaching example.

For additional context, this code listing follows the running code listing example from the Inheritance chapter earlier in the book. So we might also want to cross-apply edits there.

Also, if there's an editorial preference for colons rather than dashes, I can spin up a tracking bug for that. (Thinking of this because I have a related fix to normalize "OK" vs "Ok" in comments, per Apple Style, on a different branch that includes similar punctuation.)

}
}

class Train: Vehicle {
override func makeNoise() {
print("Choo Choo")
}
}
```

In the above example, `Vehicle` and all of its methods and properties
are isolated to the main actor. The `Train` subclass infers its isolation
from the superclass, so the overridden `makeNoise` method is also isolated
to the main actor.

Global actor isolation is also inferred from individual overridden
methods. For example, the following code isolates one method of the
`Vehicle` class to the main actor instead of the entire class:

```swift
class Vehicle {
var currentSpeed = 0.0

@MainActor
func makeNoise() {
// do nothing - an arbitrary vehicle doesn't necessarily make a noise

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// do nothing - an arbitrary vehicle doesn't necessarily make a noise
// Do nothing: an arbitrary vehicle doesn't necessarily make a noise.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I'm not clear what this comment means.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed

}
}

class Train: Vehicle {
override func makeNoise() {
print("Choo Choo")
}
}
```

The override of `makeNoise` in `Train` infers main-actor isolation from
the overridden `makeNoise` method in `Vehicle`.

#### Protocols

If a protocol is isolated to a global actor, the global actor is
inferred on conforming types. For example, the following code has
a main-actor isolated protocol `Togglable`, and a conforming struct
`Switch`:

```swift
@MainActor
protocol Togglable {
mutating func toggle()
}

struct Switch: Togglable {
var isOn: Bool = false

mutating func toggle() {
isOn.toggle()
}
}
```

In the above example, `Togglable` and all of its requirements

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first example says “Vehicle and all of its methods and properties are isolated to the main actor”. This one says “all of its requirements”. After reading the next example, I think (?) that all of Switch’s methods and properties are also isolated to the main actor? Is that right? If so I find the first wording very helpful and clearer and I think it would help to use it here as well. If not, perhaps that fact could be made clearer?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used the language "methods and properties" when talking about concrete types and "requirements" when talking about protocols. But you're right that I should state that when @MainActor is inferred on Switch, all of its methods and properties become isolated to the main actor too.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, yeah I think that’s really helpful because it’s changing more than just the requirements.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in bee33b2:

In the example above, the Togglable protocol is marked @MainActor to indicate that all of its requirements are isolated to the main actor. Swift infers main-actor isolation on types that conform to Togglable, so all methods and properties of Switch are isolated to the main actor, including the isOn property and the toggle method.

are isolated to the main actor. The `Switch` type infers its isolation
from the protocol conformance, and the implementation of `toggle` is
isolated to the main actor.

Isolation is only inferred from protocols when the conformance is
written at the primary declaration. If the conformance is written in
an extension, then isolation inference only applies to requirements that
are implemented in the extension. For example, the following code
implements a conformance of `Switch` to `Togglable` in an extension:

```swift
@MainActor
protocol Togglable {
mutating func toggle()
}

struct Switch: Togglable {
var isOn: Bool = false
}

extension Switch: Togglable {
mutating func toggle() {
isOn.toggle()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this example the function toggle is isolated to the main actor but isOn is not isolated to the main actor, is it? And if that’s the case, isn’t it not allowed to use it here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this example, isOn is nonisolated, which is fine to access from a main actor isolated method.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, is that because you can only access the toggle function if your Switch is currently living in the main actor and thus any access to isOn would necessarily be on the main actor as well? I always forget about that. Very cool.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jasongregori Do you think it's worth calling out in the explanation of this code listing that isOn is nonisolated? Or ok to make this conversation thread as resolved?

}
}
```

Now, the `Switch` type itself does not have inferred isolation. The
`toggle` method inside `Switch` is isolated to the main actor, because
the protocol requirement that it implements is isolated to the main actor.


<!--
OUTLINE
Expand Down