Eliminate data races using Swift Concurrency

RSS for tag

Discuss the WWDC22 Session Eliminate data races using Swift Concurrency

Posts under wwdc2022-110351 tag

1 result found
Sort by:
Post marked as solved
10 Replies
8k Views
This code can be compiled as command line tool for macOS. import Foundation @main struct App {     static var counter = 0     static func main() async throws {         print("Thread: \(Thread.current)")         let task1 = Task { @MainActor () -> Void in             print("Task1 before await Task.yield(): \(Thread.current)")             await Task.yield()             print("Task1 before await increaseCounter(): \(Thread.current)")             await increaseCounter()             print("Task1 after await increaseCounter(): \(Thread.current)")         }         let task2 = Task { @MainActor () -> Void in             print("Task2 before await Task.yield(): \(Thread.current)")             await Task.yield()             print("Task2 before await decreaseCounter(): \(Thread.current)")             await decreaseCounter()             print("Task2 after await decreaseCounter(): \(Thread.current)")         }         _ = await (task1.value, task2.value)         print("Final counter value: \(counter)")     }     static func increaseCounter() async {         for i in 0..<999 {             counter += 1             print("up step \(i), counter: \(counter), thread: \(Thread.current)")             await Task.yield()         }     }     static func decreaseCounter() async {         for i in 0..<999 {             counter -= 1             print("down step \(i), counter: \(counter), thread: \(Thread.current)")             await Task.yield()         }     } } My understanding is: static func main() async throws inherits MainActor async context, and should always run on the main thread (and it really seems that it does so) Task is initialized by the initializer, so it inherits the actor async context, so I would expect that will run on the main thread. Correct? Moreover, the closure for Task is annotated by @MainActor, so I would even more expect it will run on the main thread. I would expect that static func main() async throws inherits MainActor async context and will prevent data races, so the final counter value will always be zero. But it is not. Both task1 and task2 really start running on the main thread, however the async functions increaseCounter() and decreaseCounter() run on other threads than the main thread, so the Task does not prevent data races, while I would expect it. When I annotate increaseCounter() and decreaseCounter() by @MainActor then it works correctly, but this is what I do not want to do, I would expect that Task will do that. Can anyone explain, why this works as it does, please?
Posted
by hibernat.
Last updated
.