Skip to content

Latest commit

 

History

History
52 lines (38 loc) · 2.07 KB

sending-risks-data-race.md

File metadata and controls

52 lines (38 loc) · 2.07 KB

Sending value risks causing data races

If a type does not conform to Sendable the compiler will enforce that each instance of that type is only accessed by one concurrency domain at a time. The sending 'x' risks causing data races error indicates that your code can access a non-Sendable value from multiple concurrency domains at once.

For example, if a value can be accessed from the main actor, it's invalid to send the same instance to another concurrency domain while the main actor can still access it. This mistake is common when calling an async function on a class from the main actor:

class Person {
  var name: String = ""
    
  func printNameConcurrently() async {
    print(name)
  }
}

@MainActor
func onMainActor(person: Person) async {
  await person.printNameConcurrently()
}

The above code produces:

await person.printNameConcurrently()
            |- error: sending 'person' risks causing data races
            `- note: sending main actor-isolated 'person' to nonisolated instance method 'printNameConcurrently()' risks causing data races between nonisolated and main actor-isolated uses

This happens because the printNameConcurrently function runs off of the main actor, and the onMainActor function suspends while waiting for printNameConcurrently to complete. While suspended, the main actor can run other tasks that still have access to person, which can lead to a data race.

The most common fix is to change the async method to run on the caller's actor using the nonisolated(nonsending) specifier:

class Person {
  var name: String = ""
    
  nonisolated(nonsending)
  func printNameConcurrently() async {
    print(name)
  }
}

@MainActor
func onMainActor(person: Person) async {
  await person.printNameConcurrently()
}

This eliminates the risk of data-races because printNameConcurrently continues to run on the main actor, so all access to person is serialized.

You can also enable the AsyncCallerExecution upcoming feature to make nonisolated(nonsending) the default for async functions on non-Sendable types.