I am trying to use the PETSc (Portable, Extensible Toolkit for Scientific Computation) library in Go over cgo. After the call of PetscInitialize, the program will crash at a random point with the error:
[0]PETSC ERROR: ------------------------------------------------------------------------
[0]PETSC ERROR: Caught signal
[0]PETSC ERROR: Try option -start_in_debugger or -on_error_attach_debugger
[0]PETSC ERROR: or see https://petsc.org/release/faq/#valgrind and https://petsc.org/release/faq/
[0]PETSC ERROR: --------------------- Stack Frames ------------------------------------
SIGABRT: abort
PC=0x732cd809eb2c m=0 sigcode=18446744073709551610
Every command that I use works and I also get the correct result, if it does not crash before... The simplified code looks something like this:
func main() {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if err := PetscInitialize(os.Args, "", ""); err != nil {
panic("could not initialize petsc")
}
doSomeNonPetscRelatedStuff()
}
where PetscInitialize is an interface to C.PetscInitialize.
I tried compiling Petsc in the debug mode without OpenMPI, but it still crashes. When I do everything in pure C, it works.
Edit: More Code Details:
import ...
func doSomeNonPetscRelatedStuff() {
// some things, that take time.
// The crash happens more consistently, if one goes slowly line by line in a debugger.
}
func PetscInitialize(args []string, file string, help string) error {
cArgc, cArgv := argsToC(args)
//defer c_freeArray_string(cArgv, c_size_t(cArgc))
var cFile *c_char
if file != "" {
cFile = c_CString(file)
defer c_free(unsafe.Pointer(cFile))
}
var cHelp *c_char
if help != "" {
cHelp = c_CString(help)
defer c_free(unsafe.Pointer(cHelp))
}
if cIerr := c_PetscInitialize(&cArgc, &cArgv, cFile, cHelp); cIerr != 0 {
return errors.New(fmt.Sprintf("PetscInitialize failed with errorcode: %v", int(cIerr)))
}
return nil
}
func argsToC(args []string) (argc c_int, argv **c_char) {
argc = c_int(len(args))
argv = c_makeArray_string(c_size_t(argc))
for i, args_i := range args {
cArgs_i := c_CString(args_i)
c_writeArray_string(argv, cArgs_i, c_size_t(i))
}
return
}
This is a separate file, to have all C-stuff in one file and no import "C" in other files:
/*
#cgo CFLAGS: ...
#cgo LDFLAGS: ...
#include <slepceps.h>
#include <slepcnep.h>
#include <stdlib.h>
static void writeArray_string(char **arr, char *s, size_t n) {
arr[n] = s;
}
static char**makeArray_string(size_t size) {
return calloc(sizeof(char*), size);
}
*/
import "C"
type c_int = C.int
type c_char = C.char
type c_void = C.void
type c_size_t = C.size_t
func c_CString(str string) *c_char {
return C.CString(str)
}
func c_writeArray_string(arr **c_char, s *c_char, n c_size_t) {
C.writeArray_string(arr, s, n)
}
func c_makeArray_string(size c_size_t) **c_char {
return C.makeArray_string(size)
}
func c_free(ptr unsafe.Pointer) {
C.free(ptr)
}
func c_PetscInitialize(argc *c_int, args ***c_char, file *c_char, help *c_char) c_PetscErrorCode {
return C.PetscInitialize(argc, args, file, help)
}
Notes:
- My actual plan was to use SLEPc, but this uses PETSc which crashes.
- The complete code can't be published, because it is part of a big non-published project, but this minimal subpart should be sufficient to reproduce the crash.
PETSC ERROR: Caught signalmeans PETSC installs its own signal handler. I don't know the how the Go runtime works, but if the Go runtime depends on its own signal handlers, the fact that PETSC also installs its own signal handler could be a huge problem. Nevermind "Caught signal" is a bad error message. What signal? From where? The signal and the callstack should also be part of that error message.-on_error_attach_debugger, I'd love to see how PETSC does that in an async-signal-safe way.PetscCallA(PetscOptionsSetValue(PETSC_NULL_OPTIONS,"-no_signal_handler","true",ierr))before the call toPetscInitialize(). This prevents PETSc from defaulting to using a signal handler.». And also: «There is a separate signal handler for floating-point exceptions. The option -fp_trap turns on the floating-point trap at runtime, and the routinePetscFPTrapPush(PetscFPTrap flag);can be used within a program.» …