I respect statement "Swift makes no guarantee that the thread which executed the code before the await is the same thread which will pick up the continuation as well.".
I would like to fully understand, what exactly means "Task inherits async context". Does it keep the async (actor!) context only till the first await? Or longer?
I have modified the code above:
@main
struct App {
static var counter = 0
static func main() async throws {
print("Thread: \(Thread.current)")
let task1 = Task { () -> Void in
print("Task1 before await increaseCounter(): \(Thread.current)")
await increaseCounter()
print("Task1 AFTER await increaseCounter(): \(Thread.current)")
await increaseCounter()
print("Task1 AFTER2 await increaseCounter(): \(Thread.current)")
}
let task2 = Task.detached { () -> Void in
print("Task2 before await decreaseCounter(): \(Thread.current)")
await decreaseCounter()
print("Task2 AFTER await decreaseCounter(): \(Thread.current)")
await decreaseCounter()
print("Task2 AFTER2 await decreaseCounter(): \(Thread.current)")
}
_ = await (task1.value, task2.value)
print("Final counter value: \(counter)")
}
static func increaseCounter() async {
print("increase before loop, thread: \(Thread.current)")
for _ in 0..<999 {
counter += 1
await Task.yield()
}
print("increase after loop, thread: \(Thread.current)")
}
static func decreaseCounter() async {
print("decrease before loop, thread: \(Thread.current)")
for _ in 0..<999 {
counter -= 1
await Task.yield()
}
print("decrease after loop, thread: \(Thread.current)")
}
}
The key question is related to the thread used for continuation in the tasks. The tasks now do not have explicit async context, however should inherit async context from the main() function, and I would expect that also continuation (after await) will run on the same MainActor async context, eg. on the main thread.
However, as seen below, the continuation in task2 run on other than main thread. What is wrong in my understanding, please?
Thread: <_NSMainThread: 0x10700aad0>{number = 1, name = main}
Task1 before await increaseCounter(): <_NSMainThread: 0x10700aad0>{number = 1, name = main}
Task2 before await decreaseCounter(): <NSThread: 0x107411040>{number = 2, name = (null)}
decrease before loop, thread: <NSThread: 0x107411040>{number = 2, name = (null)}
increase before loop, thread: <_NSMainThread: 0x10700aad0>{number = 1, name = main}
decrease after loop, thread: <NSThread: 0x1071451d0>{number = 3, name = (null)}
Task2 AFTER await decreaseCounter(): <NSThread: 0x1071451d0>{number = 3, name = (null)}
increase after loop, thread: <NSThread: 0x107045480>{number = 4, name = (null)}
decrease before loop, thread: <NSThread: 0x1071451d0>{number = 3, name = (null)}
Task1 AFTER await increaseCounter(): <_NSMainThread: 0x10700aad0>{number = 1, name = main}
increase before loop, thread: <_NSMainThread: 0x10700aad0>{number = 1, name = main}
decrease after loop, thread: <NSThread: 0x107005d70>{number = 5, name = (null)}
Task2 AFTER2 await decreaseCounter(): <NSThread: 0x107005d70>{number = 5, name = (null)}
increase after loop, thread: <NSThread: 0x107045480>{number = 4, name = (null)}
Task1 AFTER2 await increaseCounter(): <_NSMainThread: 0x10700aad0>{number = 1, name = main}
Final counter value: -3
Program ended with exit code: 0
The surprise is the line Task2 AFTER2 await decreaseCounter(): <NSThread: 0x107005d70>{number = 5, name = (null)}
Tested on XCode 13.4.1 (13F100)