It definitely should come back to the main thread. I think I did see a bug fix go by about nonisolated(nonsending) and the error path; can you check if this is fixed in 6.4 beta?
Isn't this some artifact of linux behavior where assert(Thread.isMainThread) and MainActor.assertIsolated() can differ after a suspension point? I'm not sure it's the same thing as the bad executor hop bug.
As @jamieQ has pointed out, this is the current behavior on non-Apple platforms (at least Linux and Windows). I investigated this issue in the past, and the problem seems to be that Dispatch is initialized on the "wrong" thread. From my past investigation, I couldn't determine whether the fault lies with Dispatch or Swift, but there is an open bug report for this in libdispatch.
Currently, a program starts executing on its initial thread (the main thread), but after the first real suspension point, it switches to the thread associated with the MainActor (Dispatch.main thread). Regardless, when performing an executor check, both threads are considered to be the MainActor.
You'll see the effective value of the "is main thread" calculation changes after the suspension point, but the actor isolation does not. In my experience, this behavior appears to be primarily a peculiarity of non-Darwin platforms (e.g. here's the libdispatch issue alluded to above, and its motivating forum discussion), though I've read mixed-messaging about whether or not Darwin is entirely immune from it (see here vs here vs here).
I looked into this a bit more, and I think the actual culprit is that, on non-Darwin platforms, dispatchMain() is called during the initialization of the main executor. Interestingly, the first part of the program, before hitting any real suspension point, executes inline on the main thread. That’s why the first Thread.isMainThread returns true.
P.S. Let’s fix it :D
P.P.S. Can someone move the issue from libdispatch to swift?
I really think it should be fixed asap. Such a fundamental language feature. The issue has existed for a long time. It deserves a hotfix. It affects the infrastructures that were written by swift for non-Darwin OS.
I haven't looked into this much, so I don't fully understand the implications here. I get that the result of Thread.isMainThread is confusing. But it looks like the runtime isolation of the main actor is functioning correctly.
What are the visible negative effects of this phenomenon? Is it mainly thread-local state?
I don't know, as I don't use Swift Concurrency. But I'm working on a library for Windows that includes GUI objects. The library itself does not use Swift Concurrency, but I'm very concerned if a user of my library will use concurrency, and at this moment will be exposed to race conditions or to GUI rendering on a wrong thread.
why don’t you think it is an issue? It doesn’t run on the expected thread. It should have a predictable behaviour. Also it is not on the same expected thread. It can cause crashes.
That's my question. I get that the thread changes and that this is weird and concerning. But then, we see above that the runtime thinks that the main actor isolation still applies. It certainly seems like the compiler will still enforce serialization for the main actor. And regular actors change threads all the time.
The implication across this topic has been that this is a real data race hole. It could be! I'm just having trouble figuring out if that is actually the case.
I don’t think it’s a data race issue, but if the main thread has special significance on the target platform like it does on Darwin, exiting it by calling dispatchMain() could certainly cause other problems.
Apologies all for the confusion. That playground clearly demonstrates another manifestation of this problem in this topic. I really do get that there is difference between the Thread API and the notion of MainActor.
I was trying to understand if the runtime behavior here would have an impact even if it does not specifically exercise this weirdness. And it sounds like the answer is yes. I just thought it was worth asking, given that it seems like the concurrency runtime does a very incorrect thing here and has done so for years now.