Thread.isMainThread throwing compiler warnings

I don’t have answers to your actual questions, but I did want to share a titbit: You can avoid warnings like this by wrapping the call. For example, replace this:

func test() async {
    print(Thread.isMainThread)
              // ^ Class property 'isMainThread' is unavailable from
              // asynchronous contexts; Work intended for the main actor should
              // be marked with @MainActor
}

with this:

func inner() {
    print(Thread.isMainThread)
}

func testWithInner() async {
    inner()
}

This works because the noasync attribute doesn’t propagate. That’s explained in SE-0340 Unavailable From Async Attribute


Taking a step back, this whole class of APIs is problematic because folks use them for two different reasons:

  • To assert invariants, for example, to trap if you’re not running in the right context

  • To alter runtime behaviour, for example, to bounce to the main thread if you’re not on the main thread

The first usage is fine but the second is a path with many pitfalls. That’s why Dispatch’s variant is structured in terms of a precondition.

Finally, Konrad wrote:

The main actor is not necessarily the main thread (though most of the time it is).

Indeed. And that “most of the time” makes things hard because folks write code that assumes this correlation and then that code fails in odd circumstances.

I also want to stress that the main queue is not necessarily serviced by the main thread. I’ve included some fun examples below.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple


import Foundation
 
let queue = DispatchQueue(label: "not-main")
 
func main() {
    queue.async {
        DispatchQueue.main.sync {
            // This doesn’t trap:
            dispatchPrecondition(condition: .onQueue(.main))
            // But this prints false:
            print(Thread.isMainThread)
        }
    }
    dispatchMain()
}
 
main()

import Foundation
 
let queue = DispatchQueue(label: "not-main")
 
func main() {
    DispatchQueue.main.async {
        queue.sync {
            // Neither of these trap:
            dispatchPrecondition(condition: .onQueue(.main))
            dispatchPrecondition(condition: .onQueue(queue))
            // This prints true:
            print(Thread.isMainThread)
        }
    }
    dispatchMain()
}
 
main()
11 Likes