Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runtime: immediate lock-up when using go library in Unity 6 #70734

Open
arjenveenhuizen opened this issue Dec 9, 2024 · 7 comments
Open

runtime: immediate lock-up when using go library in Unity 6 #70734

arjenveenhuizen opened this issue Dec 9, 2024 · 7 comments
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. help wanted NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@arjenveenhuizen
Copy link

arjenveenhuizen commented Dec 9, 2024

Go version

go1.21.1 darwin/arm64 (we have observed identical behaviour when using go 1.23.1 and 1.23.4)

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='arm64'
GOBIN=''
GOCACHE='Library/Caches/go-build'
GOENV='Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='temp/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/$HOME/temp/go/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.21.1'
GCCGO='gccgo'
AR='ar'
CC='clang'
CXX='clang++'
CGO_ENABLED='1'
GOMOD='/dev/null'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/f8/0nlswm8x445_jl3720jx6kmw0000gn/T/go-build2270444360=/tmp/go-build -gno-record-gcc-switches -fno-common'

What did you do?

When running on Apple Silicon hardware:

Prerequisites:

  1. Extract plugin code: TestPlugin.zip
  2. Extract Unity 2022.3.43f1 repro project: Unity2022ReproProject.zip. Do not open the project yet.
  3. Extract Unity 6000.0.28f1 repro project: Unity6ReproProject.zip. Do not open the project yet.
  4. Install dylibbundler required for dylib signing (brew install dylibbundler)
  5. Build attached TestPlugin go lib as .dylib on MacOS (using cgo). You can use the included magefile.
  6. Copy build/TestPlugin.dylib to the extracted Unity 2022 project in Assets/Plugins/TestPlugin.dylib.
  7. Copy the same build/TestPlugin.dylib to the extracted Unity 6 project in Assets/Plugins/TestPlugin.dylib.

What did you see happen?

In Unity 2022

Unity 2022 (and also other versions like Unity 2021 and Unity 2023) is working as expected.

Open the Unity 2022 project, open the Scenes/SampleScene and click the Play button. Observe that the Unity Console shows:

Calling HelloWorld on plugin...
Successfully called HelloWorld on plugin...

If you open ~/Library/Logging/Unity/Editor.log it will show:

Calling HelloWorld on plugin...
UnityEngine.StackTraceUtility:ExtractStackTrace () (at /Users/bokken/build/output/unity/unity/Runtime/Export/Scripting/StackTrace.cs:37)
UnityEngine.DebugLogHandler:LogFormat (UnityEngine.LogType,UnityEngine.Object,string,object[])
UnityEngine.Logger:Log (UnityEngine.LogType,object)
UnityEngine.Debug:Log (object)
MacOSPluginLoader:Start () (at Assets/Scripts/MacOSPluginLoader.cs:17)

(Filename: Assets/Scripts/MacOSPluginLoader.cs Line: 17)

Hello from Go!
Hello from Go 2!
Hello from Go 3!
Successfully called HelloWorld on plugin...
UnityEngine.StackTraceUtility:ExtractStackTrace () (at /Users/bokken/build/output/unity/unity/Runtime/Export/Scripting/StackTrace.cs:37)
UnityEngine.DebugLogHandler:LogFormat (UnityEngine.LogType,UnityEngine.Object,string,object[])
UnityEngine.Logger:Log (UnityEngine.LogType,object)
UnityEngine.Debug:Log (object)
MacOSPluginLoader:Start () (at Assets/Scripts/MacOSPluginLoader.cs:19)

In Unity 6

Unity 6 is immediately locking up (showing a beachball spinner) as soon as any go API is called.

Open the Unity 6 project, open the Scenes/SampleScene and click the Play button. Observe that the Unity Console shows:

Calling HelloWorld on plugin...

If you open ~/Library/Logging/Unity/Editor.log it will show:

Calling HelloWorld on plugin...
UnityEngine.StackTraceUtility:ExtractStackTrace () (at /Users/bokken/build/output/unity/unity/Runtime/Export/Scripting/StackTrace.cs:37)
UnityEngine.DebugLogHandler:LogFormat (UnityEngine.LogType,UnityEngine.Object,string,object[])
UnityEngine.Logger:Log (UnityEngine.LogType,object)
UnityEngine.Debug:Log (object)
MacOSPluginLoader:Start () (at Assets/Scripts/MacOSPluginLoader.cs:17)

What did you expect to see?

Unity 2022 and Unity 6 to behave the same. Instead, Unity 2022 is behaving as expected wheras Unity 6 immediately locks-up the Unity Editor.

@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label Dec 9, 2024
@ianlancetaylor
Copy link
Member

I'm sorry, I don't know what Unity is. But from your description, this seems like a problem with Unity, not a problem with Go. Have you raised this with the developers of Unity?

@ianlancetaylor ianlancetaylor added the WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided. label Dec 9, 2024
@arjenveenhuizen
Copy link
Author

I'm sorry, I don't know what Unity is.

Unity is a 3D game engine that is used abundantly.

But from your description, this seems like a problem with Unity, not a problem with Go. Have you raised this with the developers of Unity?

Plugins written in plain C/C++ work just fine in the Unity Editor. When you add (c-)go in the mix Unity locks-up, hence our believe that this (c-)go related.

In the meantime we have a couple more findings:

  1. The exact same go code works fine in Unity 6000.0.28f1 when compiled as a Windows DLL.
  2. If you build the Unity 6 project into a stand-alone app on MacOS it also works OK.

@ianlancetaylor
Copy link
Member

Just to set expectations, while the problem may be Go related, since we don't know anything about Unity it's unlikely that we'll be able to figure this out.

@ianlancetaylor ianlancetaylor added help wanted and removed WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided. labels Dec 11, 2024
@ianlancetaylor ianlancetaylor added this to the Unplanned milestone Dec 11, 2024
@arjenveenhuizen
Copy link
Author

Just to set expectations, while the problem may be Go related, since we don't know anything about Unity it's unlikely that we'll be able to figure this out.

Appreciated that. We are willing to contribute. Any pointer on where to start debugging this issue would be appreciated.

To set the scene, the following minimal example (from the ZIP files attached to the first post) is causing the lock-up:

package main

// #include <stdio.h>
// #include <stdlib.h>
import "C"
import (
	"fmt"
)

//export HelloWorld
func HelloWorld() {
	fmt.Println("Hello from Go!")
}

func main() {}

Build:

go build -o ./TestPlugin.dylib -buildmode=c-shared -ldflags="-s -w" TestPlugin.go

C#:

    private const string PluginRelativePath =  "TestPlugin";
    [DllImport(PluginRelativePath, EntryPoint = "HelloWorld", CallingConvention = CallingConvention.Cdecl)]
    private static extern void HelloWorld();

    void Start() {
            Debug.Log("Calling HelloWorld on plugin...");
            HelloWorld();
            Debug.Log("Successfully called HelloWorld on plugin...");
    }

Unity locks up when executing HelloWorld();

@cagedmantis cagedmantis added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Dec 11, 2024
@ianlancetaylor
Copy link
Member

I'm sorry, I have no idea. From your description, it works with one version of Unity and fails with a different one. So something must have changed in Unity, but I have no idea what.

@arjenveenhuizen
Copy link
Author

We have reported this issue to Unity (issue 91704, I do not have a public link (yet?)).

Our own investigation (by sprinkling log lines in the go source code) has shown that the go runtime is at least reaching x_cgo_init:

x_cgo_init(G *g, void (*setg)(void*))

and we see a number of threads spinning up in _cgo_sys_thread_start:

_cgo_sys_thread_start(ThreadStart *ts)

when the plugin is loaded by the unity editor.

But when we call the exported function, it immediately hangs. At this point, I have no clue where to look next. Are there other spots where I can add some log lines to see what is going on? Any pointer is greatly appreciated.

@ianlancetaylor
Copy link
Member

When a Go library is built with -buildmode=c-shared, then when the library is loaded the Go initializers are run automatically in a separate thread. Because Go initializers must be complete before any ordinary Go code can run, if the non-Go code calls a Go function, we wait for all Go initializers to be complete before permitting the Go function call to proceed. This means that we can have a deadlock in the case where code in a Go initializer calls a non-Go function that then calls a Go function. The call to the Go function will wait for initializers to complete, but that will never happen because the initializer is waiting for the non-Go function to complete. Perhaps that is what is happening here, somehow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. help wanted NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

4 participants