Skip to content

Commit

Permalink
test: demonstrate issue with signal handling in golang with pact_ffi
Browse files Browse the repository at this point in the history
  • Loading branch information
YOU54F committed Jul 2, 2024
1 parent efc54d2 commit 69f0ec6
Show file tree
Hide file tree
Showing 21 changed files with 4,465 additions and 4 deletions.
42 changes: 42 additions & 0 deletions go/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
SKIP_SIGNAL_HANDLERS?=false
TEST_COUNT?=50
RACE=-race
CGO_ENABLED=1
ifeq ($(CGO_ENABLED),0)
SKIP_RACE=true
endif
ifeq ($(SKIP_RACE),true)
RACE=
endif
# export PACT_PROVIDER_DIR?=./pacts_plugin_protobuf

ifeq ($(PACT_PROVIDER),protobuf)
export PACT_PROVIDER_DIR=./pacts_plugin_protobuf
else ifeq ($(PACT_PROVIDER),csv)
export PACT_PROVIDER_DIR=./pacts_plugin_csv
else ifeq ($(PACT_PROVIDER),avro)
export PACT_PROVIDER_DIR=./pacts_plugin_avro
else ifeq ($(PACT_PROVIDER),message)
export PACT_PROVIDER_DIR=./pacts_plugin_message
else
export PACT_PROVIDER_DIR=./pacts_http
endif

run:
go run pact/go/native/native

test:
go test -v $(SKIP_RACE) -count $(TEST_COUNT) pact/go/native/native

test_protobuf:
make test PACT_PROVIDER=protobuf
test_csv:
make test PACT_PROVIDER=csv
test_avro:
make test PACT_PROVIDER=avro
test_go:
make test PACT_PROVIDER=go
test_message:
make test
test_http:
make test
8 changes: 8 additions & 0 deletions go/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module pact/go/native

go 1.22.2

require (
github.com/ebitengine/purego v0.7.1
golang.org/x/sys v0.21.0
)
4 changes: 4 additions & 0 deletions go/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA=
github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
97 changes: 97 additions & 0 deletions go/native/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Go Interop with Rust Issue

This is an example which shows a Rust project which has been compiled as a shared library, and is called from multiple languages including Go.

The rust project, may call out to external executables (called plugins) which can be written in any language.

This is currently causing issues with occasional segfaults in Linux / MacOS.

## Rust shared library

The rust shared library is currently built for the following platforms.

- x86_64-unknown-linux-gnu
- aarch64-unknown-linux-gnu
- x86_64-unknown-linux-musl
- aarch64-unknown-linux-musl
- x86_64-apple-darwin
- aarch64-apple-darwin
- x86_64-pc-windows-msvc
- aarch64-pc-windows-msvc

## Calling languages

It is called from various languages, including but not limited to:

- Golang (via cgo)
- JS (via node-napi)
- PHP (via FFI)
- Python (via cffi)

## Plugins, controlled by the rust shared library

The shared library, may load plugins which are written in any language, and may call out to external executables which are written in any language.

The following languages are currently being used as plugins:

- Golang
- Built with GoReleaser for the following platforms:
- linux/amd64
- linux/arm64
- darwin/amd64
- darwin/arm64
- windows/amd64
- windows/arm64
- Rust
- Built with cross / cargo
- For linux, musl static builds are used to work on glibc & musl systems
- x86_64-unknown-linux-musl
- aarch64-unknown-linux-musl
- x86_64-apple-darwin
- aarch64-apple-darwin
- x86_64-pc-windows-msvc
- aarch64-pc-windows-msvc
- Java
- Requires user to have openjdk 17 installed

## Issues

### Issue 1 - Segfaults on Linux / MacOS

```
signal 17 received but handler not on signal stack
mp.gsignal stack [0x400006e000 0x4000076000], mp.g0 stack [0xffff4320e810 0xffff43a0e410], sp=0x40000e3608
fatal error: non-Go code set up signal handler without SA_ONSTACK flag
```

Fixed by workaround in following PR

- https://github.com/wailsapp/wails/pull/2152/files#diff-d4a0fa73df7b0ab971e550f95249e358b634836e925ace96f7400480916ac09e

> Sets up a new signal handler in C that overrides the current one (in C) so that SA_ONSTACK is used.
See golang docs for os/signal

https://pkg.go.dev/os/signal#hdr-Go_programs_that_use_cgo_or_SWIG

> If the non-Go code installs any signal handlers, it must use the SA_ONSTACK flag with sigaction. Failing to do so is likely to cause the program to crash if the signal is received. Go programs routinely run with a limited stack, and therefore set up an alternate signal stack.
#### Additional Problems

1. Does not fix Alpine linux
2. Fix does not with CGO_ENABLED=0 using https://github.com/ebitengine/purego, as it requires C code to be injected.
1. Probably fix needs to be applied to fakego in purego

## Testing

- Use of CGO or ebitengine/purego is controlled by `CGO_ENABLED=1` for `cgo` and `CGO_ENABLED=0` for `purego`
- Fix can be removed by settings `SKIP_SIGNAL_HANDLERS=true`

### Issue 2 - Windows plugin executables are not shutdown properly

```
*** Test I/O incomplete 1m0s after exiting.
exec: WaitDelay expired before I/O complete
```

The windows executable is shutdown externally via taskkill or the task manager, will exit the plugin correctly and the test will pass.
38 changes: 38 additions & 0 deletions go/native/lib.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//go:build cgo
// +build cgo

// Package native contains the c bindings into the Pact Reference types.
package main

/*
#cgo darwin,arm64 LDFLAGS: -L/tmp -L/usr/local/lib -Wl,-rpath -Wl,/tmp -Wl,-rpath -Wl,/usr/local/lib -lpact_ffi
#cgo darwin,amd64 LDFLAGS: -L/tmp -L/usr/local/lib -Wl,-rpath -Wl,/tmp -Wl,-rpath -Wl,/usr/local/lib -lpact_ffi
#cgo windows,amd64 LDFLAGS: -lpact_ffi
#cgo linux,amd64 LDFLAGS: -L/tmp -L/opt/pact/lib -L/usr/local/lib -Wl,-rpath -Wl,/opt/pact/lib -Wl,-rpath -Wl,/tmp -Wl,-rpath -Wl,/usr/local/lib -lpact_ffi
#cgo linux,arm64 LDFLAGS: -L/tmp -L/opt/pact/lib -L/usr/local/lib -Wl,-rpath -Wl,/opt/pact/lib -Wl,-rpath -Wl,/tmp -Wl,-rpath -Wl,/usr/local/lib -lpact_ffi
#include "pact.h"
*/
import "C"
import (
"fmt"
"os"
)

func plugin_provider() int {
verifier := C.pactffi_verifier_new()
// C.pactffi_log_to_stdout(0)
C.pactffi_verifier_set_provider_info(verifier, C.CString("p1"), C.CString("http"), C.CString("localhost"), 8000, C.CString("/"))
// C.pactffi_verifier_add_provider_transport(verifier, C.CString("http"), 8000, C.CString("/"), C.CString("http"))
// C.pactffi_verifier_add_provider_transport(verifier, C.CString("protobuf"), 37757, C.CString("/"), C.CString("tcp"))

C.pactffi_verifier_add_directory_source(verifier, C.CString(os.Getenv("PACT_PROVIDER_DIR")))
InstallSignalHandlers()
defer C.pactffi_verifier_shutdown(verifier)
result := C.pactffi_verifier_execute(verifier)
if result != 0 {
fmt.Printf("Result is not 0: %d", result)
} else {
fmt.Print("Result success")
}
return int(result)
}
Loading

0 comments on commit 69f0ec6

Please sign in to comment.