On Fri, Aug 5, 2022 at 12:48 PM 'Aaron Klotz' via golang-dev
<
golan...@googlegroups.com> wrote:
>
> I'm working on adding support for Windows' asynchronous DNS query APIs to the net package in the standard library.
>
> When these APIs complete asynchronously, they post their completion events to the Windows thread pool. Unfortunately, when these events call into Go, they trigger a deadlock if the initial invocation of the API originated from the main package's init() function. A simplified test case for this scenario is available at [0].
>
> Since the callback is arriving on an OS thread that has not previously entered Go, its stub is waiting for main's init to complete before proceeding [1]. However, the main goroutine is blocked because the API is waiting on the callback [2][3].
>
> Your first question is probably, "Why would you ever even try to call something like that from within main's init()?" Actually I wouldn't, but there is a net.Dial that is effectively triggered from within main's init() as part of the Go test suite! [4]
>
> Now for my actual question: Is this deadlock considered to be expected behaviour on the part of runtime, or is this something that needs to be fixed?
Everything you wrote above sounds like expected behavior. When a C
program is linked against Go code, the Go initialization is run in a
separate thread, so that Go initialization doesn't delay the C program
startup. This means that when C code calls into Go code, we have a
check to make sure that the Go code is fully initialized. The way
that we detect C code calling Go code is that the thread was created
by C.
This is breaking in your case because you have Go code that is run at
init time that causes a thread created by C to call Go code. There
are other ways to trigger this deadlock, like having a Go init
function call a C function that creates a thread that calls a Go
function and then waits for the thread to complete. We decided that
that was bizarre enough not to worry about.
I don't know what the Windows thread pool is. I don't know where the
callback lives. Would it be possible to do something like start a
goroutine that waits for Windows, and have WIndows code that passes
information to that goroutine using atomic memory stores rather than
via a function call?
We could also of course add a special purpose hook here: define a
function that can be called by C code that can call into Go code
without waiting for initialization to be complete.
Ian