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/cgo: call C exit to allow global destructors/atexit to run #20713

Open
h1467792822 opened this issue Jun 17, 2017 · 8 comments
Open

runtime/cgo: call C exit to allow global destructors/atexit to run #20713

h1467792822 opened this issue Jun 17, 2017 · 8 comments
Labels
help wanted NeedsFix The path to resolution is known, but the work has not been done. Unfortunate
Milestone

Comments

@h1467792822
Copy link

Please answer these questions before submitting your issue. Thanks!

What version of Go are you using (go version)?

go version go1.8 linux/amd64

What operating system and processor architecture are you using (go env)?

GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/go"
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build855140157=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"
PKG_CONFIG="pkg-config"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"

What did you do?

I want to do something in the destructor of a global variable defined in c++ library which is linked into a go program via cgo. but the destructor can't be invoked when the go program exited.

//c++ library
class AutoInit
{
public:
AutoInit() { printf("+++++ auto init\n"); }
~AutoInit() { printf("---- auto fini\n"); }
};

What did you expect to see?

two lines should be printed:
+++++ auto init
---- auto fini

What did you see instead?

only one line was be printed:
+++++ auto init

@Konstantin8105
Copy link
Contributor

Could you clarify your CPP and Go code.

@h1467792822
Copy link
Author

//library files:
//tmp_log.h
#ifndef TMP_LOG_H
#define TMP_LOG_H

#ifdef __cplusplus
extern "C"
{
#endif

void tmp_log(const char* msg);

#ifdef __cplusplus
}
#endif

#endif

//tmp_log.cpp
#include "tmp_log.h"
#include <stdio.h>

extern "C"
{

void tmp_log(const char* msg)
{
printf("%s",msg);
}

}

struct Auto_Init
{
Auto_Init() {
tmp_log("++++ auto_init\n");
}
~Auto_Init() {
tmp_log("--- auto_fini\n");
}
};
static Auto_Init auto_init;

//compile
g++ -shared -fPIC -g -o libtmp_log.so tmp_log.cpp

//go files: go/src/tmp/log.go
package log

import(
"unsafe"
)

/*
#cgo CPPFLAGS: -I./tool -I.
#cgo LDFLAGS: -L./tool -L. -ltmp_log
#include "tmp_log.h"
#include <malloc.h>
*/
import "C"

func Log(msg string) {
cmsg := C.CString(msg)
C.tmp_log(cmsg)
C.free(unsafe.Pointer(cmsg))
}

//tool.go
package main

import (
"tmp/log"
)

func main() {
log.Log("go is running\n")
}

//run tool:
$ LD_LIBRARY_PATH=. ./tool
++++ auto_init
go is running

//c test, tool.c
#include "tmp_log.h"

int main(int argc,char* argv[])
{
tmp_log("*** tmp_log running\n");
return 0;
}
//compile
gcc -g -o c_tool tool.c -L. -ltmp_log
//run c tool
$ LD_LIBRARY_PATH=. ./c_tool
++++ auto_init
*** tmp_log running
--- auto_fini

@bradfitz
Copy link
Contributor

@ianlancetaylor?

@bradfitz bradfitz changed the title cgo don't invoke the destructor of global variant defined in c++ library runtime/cgo: destructor of global variant defined in c++ library are not invoked Jun 19, 2017
@bradfitz bradfitz added this to the Go1.10 milestone Jun 19, 2017
@bradfitz bradfitz added the NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. label Jun 19, 2017
@ianlancetaylor
Copy link
Contributor

It's true that Go programs don't currently run the C++ static destructors of any code linked in via cgo.

I guess the way to fix this would be for cgo programs to call the C library exit function rather than the exit system call.

@rsc rsc changed the title runtime/cgo: destructor of global variant defined in c++ library are not invoked runtime/cgo: call C exit to allow global destructors/atexit to run Jun 26, 2017
@rsc rsc added NeedsFix The path to resolution is known, but the work has not been done. Unfortunate labels Jun 26, 2017
@ianlancetaylor ianlancetaylor removed the NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. label Jun 26, 2017
@ianlancetaylor ianlancetaylor self-assigned this Jun 26, 2017
@rsc rsc modified the milestones: Go1.10, Go1.11 Nov 29, 2017
@rsc
Copy link
Contributor

rsc commented Nov 29, 2017

I agree we need to do this, although I am very sad about it. It didn't happen for Go 1.10, though, so at least there's that to be happy about.

@bcmills
Copy link
Contributor

bcmills commented Jan 8, 2018

It occurs to me that this will have an unfortunate interaction with log.Fatal: log.Fatal is defined to call os.Exit(1), which (after this is addressed) will invoke atexit hooks.

However, atexit hooks are not normally invoked if a program terminates abnormally, and I would argue that most uses of log.Fatal indicate abnormal termination. (I would argue that ideally log.Fatal should instead exit by raising SIGABRT.)

I don't think that interaction is fixable for Go 1, since the os.Exit behavior is clearly documented in the log API, but we should at least consider the interaction and perhaps revisit it for Go 2.

@ryanp76
Copy link

ryanp76 commented Jun 6, 2018

Just wanted to comment on other issues we have encountered due to this issue. When attempting to extract GCOV or mem track data for the C code, these tools require a C exit. Currently we have to explicitly call the exit() function in our test framework which prevents the go test code from finishing up. Please consider this when deciding the priority for the fix. We are looking forward to removing these workarounds once you have a fix. Thanks!

@ianlancetaylor
Copy link
Contributor

Just a note that currently when building with -race os.Exit will call the race detector __tsan_fini function which will call the C exit function and therefore will run destructors and atexit functions. In other words the current behavior varies depending on whether -race is used or not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted NeedsFix The path to resolution is known, but the work has not been done. Unfortunate
Projects
None yet
Development

No branches or pull requests

7 participants