Skip to content

runtime: unexpected failure and garbage data when c-shared Go DLL on Windows receives CtrlBreak signal concurrently with runtime initialization #46354

Open
@mknyszek

Description

@mknyszek

In c-shared mode, when a Go DLL is loaded, it spawns a new thread that starts runtime initialization. All calls into the DLL block on runtime initialization completing.

While investigating #45638, we discovered that the previous iteration of TestLibraryCtrlHandler would often send a CtrlBreak signal to the Go test process before runtime initialization was complete, and this would result in an exception landing in a Go exception handler on a Go thread that wasn't ready to receive it (no G or M).

Then, we'd end up in badsignal2. Previously, badsignal2 would not exit after printing the message, and garbage would spew into the stdout (or Windows equivalent) of the process before badsignal2 would be called again. Still, this garbage spewing was reproducible after this was fixed.

As an example, this is what the failure looked like:

=== RUN   TestLibraryCtrlHandler
    signal_windows_test.go:210: Program exited with error: exit status 1
        runtime: signal received on thread not created by Go.
        �o�<f�N9꙾�&����vl��3U0�{\K���X�س���64oMˡ1��!
                                                    R��n��)�Џ�jG'��T��-�0�i�
                                                                            �!Ρ6�)��,p??�G�j�V���k6˩���G
                                                                                                        �
                                                                                                         �6�runtime: signal received on thread not created by Go.
        �o�<f�N9꙾�&����vl��3U0�{\K���X�س���64oMˡ1��!
                                                    R��n��)�Џ�jG'��T��-�0�i�
                                                                            �!Ρ6�)��,p??�G�j�V���k6˩���G
                                                                                                        �
                                                                                                         �6�h�h�runtime: signal received on thread not created by Go.
        �o�<f�N9꙾�&����vl��3U0�{\K���X�س���64oMˡ1��!
                                                    R��n��)�Џ�jG'��T��-�0�i�
                                                                            �!Ρ6�)��,p??�G�j�V���k6˩���G
                                                                                                        �
                                                                                                         �6�h�h�d@�?@DDD �"""C:\Windows\system32\runtime: signal received on thread not created by Go.
        �o�<f�N9꙾�&����vl��3U0�{\K���X�س���64oMˡ1��!
                                                    R��n��)�Џ�jG'��T��-�0�i�
                                                                            �!Ρ6�)��,p??�G�j�V���k6˩���G
                                                                                                        �
                                                                                                         �6�h�h�d@�?@DDD �"""C:\Windows\system32\FAILURE: No signal received
--- FAIL: TestLibraryCtrlHandler (68.32s)
FAIL
FAIL	runtime	68.350s
FAIL
Error running run: exit status 1

Sometimes there would be a lot more garbage (spanning pages and pages of terminal buffer space). Sometimes less.

The failure is rare, maybe once in 30,000 runs. It's still unclear what the root cause is, but we've narrowed it down to the control handler getting installed in the Go runtime. If that is removed, it is not reproducible. It only started showing up in Go 1.17, during which time the way our console control handler worked changed. It's unclear how the exception handler gets invoked, also, because the control handler is generally completely separate from the exception handler on Windows.

Something may be going wrong on a Go-owned thread that hasn't been set up to have an M yet (i.e. between when the first M is properly initialized and when the control handler is installed), however it's still possible to reproduce the issue even if control handler installation is delayed all the way until just before runtime.main completes (signalling that runtime initialization is done).

However, it's possible this issue is reproducible under previous Go releases (just that it's more difficult to do so). Notably, it's still possible to reproduce the issue even if one installs a trivial handler written in assembly (much less complex than callbackasm1) without any of the compileCallback machinery that was added in Go 1.17.

The workaround for this issue is to block the application until the Go runtime has fully initialized by calling any Go function in the DLL before proceeding.

CC @alexbrainman @zx2c4

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.OS-Windowscompiler/runtimeIssues related to the Go compiler and/or runtime.help wanted

    Type

    No type

    Projects

    Status

    Triage Backlog

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions