golang/go

runtime/cgo: deadlock during daemons in _cgo_wait_runtime_init_done

Closed this issue · 3 comments

eancc commented

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

$ go version
go version go1.12.4 linux/amd64

Does this issue reproduce with the latest release?

yes

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

go env Output
$ go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/data/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build504982591=/tmp/go-build -gno-record-gcc-switches"

What did you do?

I am developing a static library with GO for use in C/C++
Here is an example:

shell>cat demo.c 

/**
 *  
 *  bulid:gcc -g ./demo.c -I./ -L./ -lcdemo -lpthread -o demo
 * 
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "libcdemo.h"

GoString toGoString(const char *str)
{
    int len = strlen(str);
    GoString gostr = {str, len};
    return gostr;
}
int init_daemon(void) 
{ 
        int pid; 
        int i; 

        signal(SIGTTOU,SIG_IGN);
        signal(SIGTTIN,SIG_IGN);
        signal(SIGTSTP,SIG_IGN);
        signal(SIGHUP,SIG_IGN);

        pid = fork();
        if(pid > 0) {
                exit(0); //exit parent
        }
        else if(pid < 0) { 
                return -1;
        }

        setsid();

        pid=fork();
        if( pid > 0) {
                exit(0);
        }
        else if( pid< 0) {
                return -1;
        }
 
        for(i=0;i< NOFILE;close(i++));
 
        chdir("/data/demo/");

        umask(0);

        signal(SIGCHLD,SIG_IGN); 

        return 0;
}
int main()
{
    printf("demo start... \r\n");

    //TODO todo something
    // ...
    //sleep(3);
    //call daemon
    init_daemon();
    sleep(3);
    //-----------------------call go function--------------------
    openlog("testlogs", LOG_CONS | LOG_PID, 0);  
    syslog(LOG_USER | LOG_INFO ,"This is a syslog test message generated by program \n");  
    //TODO todo something
    //Bar();
    syslog(LOG_USER | LOG_INFO ,"bar2:%s \n",Bar2());
    syslog(LOG_USER | LOG_INFO ,"bar2:%s \n","Hello C");

    // ...
    sleep(3);
    closelog();  
    return 0;
}

The go code:

shell>cat cdemo.go 

package main

import "C"
import (
        "fmt"
)

func main() {
        fmt.Println(Bar2())
        return
}

//export Bar2
func Bar2() *C.char {
        return C.CString("Hello World From Go!")
}

Compile the static library and use it

shell>go build -x -v -gcflags "-N -l" -buildmode=c-archive -o libcdemo.a .
shell>ls
cdemo.go  libcdemo.a  libcdemo.h
shell>mv libcdemo* cdemo_test
shell>cd cdemo_test
shell>ls
demo.c  libcdemo.a  libcdemo.h
shell>gcc -g ./demo.c -I./ -L./ -lcdemo -lpthread -o demo
shell>ls
demo  demo.c  libcdemo.a  libcdemo.h
shell>./demo
demo start...
shell>pstack 16968
#0  0x0000003dfe80b68c in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#1  0x0000000000401583 in _cgo_wait_runtime_init_done ()
#2  0x000000000040149c in Bar2 ()
#3  0x00000000004013d4 in main ()

What did you expect to see?

Hello World From Go!

What did you see instead?

The cgo blocked!

What do I need to do?

I don't think there is any safe way to daemonize a program once the Go runtime has started, since the daemonization process copies only one thread, not all existing ones. When there are other threads running during daemonization, the daemonized process is starting with inconsistent memory. That is most likely the cause of the problem you are seeing. See the discussion in #227.

The only reasonable fix I know of is to make sure the daemonization happens before the Go runtime starts, either by doing it in a separate program or by doing it as a high priority initializer.

eancc commented

Thanks for your reply.
I am looking for a way to advance the daemon. Can you provide an example? Thanks

The simplest way is to use a separate program. It should do what your init_daemon function does, except that after the second fork it should execve the program you want to run as a daemon.

You may get a better answer on a forum: see https://golang.org/wiki/Questions .

I'm going to close this issue because we aren't to make any changes to the Go project to fix this.