Post

Replies

Boosts

Views

Activity

Reply to Task on MainActor does not run on the main thread, why?
Thank you, exhausting enough, everything is clear. Just a final summary for all others reading this thread, the core of my interest (not needed to be further commented, if correct :-) ) Unstructured Task created through Task.init inherits actor async context. (Detached Task does NOT - detached tasks are not subject of the summary below) The inherited actor async context has nothing to do with threads, after each await the thread may change, however the actor async context is kept throughout all the unstructured task (yes, may be on various threads, but this is important just for the executor). If the inherited actor async context is MainActor, then the task runs on the main thread, from beginning till its end, because the actor context is MainActor. This has no impact on any subtasks. Unstructured subtasks may inherit the actor async context, where applicable. async let = tasks, and group tasks (and detached unstructured tasks) do NOT inherit actor context! The bullet above is important if you plan to run some really parallel computation - make sure all the unstructured tasks do not run on the same thread. Uff. I think that this thread can be closed. Thanks a lot!
Jul ’22
Reply to Task on MainActor does not run on the main thread, why?
The XCode documentation for Task says: "...the task created by Task.init(priority:operation:) inherits the priority and actor context of the caller, so the operation is treated more like an asynchronous extension to the synchronous operation." You wrote "task don’t inherit their actor." If task does NOT inherit caller actor context, then everything is clear to me. Please, confirm.... (and please update the documentation accordingly)
Jul ’22
Reply to Task on MainActor does not run on the main thread, why?
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)
Jul ’22
Reply to reloadItems behavior on NSDiffableDataSourceSnapshot
My understanding was, that identifiers must be Equatable, and Hashable. Hashable - identifies the item: if two items have the same hashValue, then it is the same item. Equatable - used for diffing purposes, whether the item is added, deleted or updated: if two items are equal, then no update (reload) is needed. Unfortunately, my understanding is in contradiction to an Apple Engineer saying "It only cares about identifiers and it considers identifiers to be equal as long as their isEqual: methods return YES."
Aug ’20