sbinet / go-python

naive go bindings to the CPython2 C-API
Other
1.52k stars 138 forks source link

run 20000 goroutine panic #92

Closed kingangelAOA closed 5 years ago

kingangelAOA commented 5 years ago

system: Distributor ID: Ubuntu Description: Ubuntu 16.04.6 LTS Release: 16.04 Codename: xenial

go env: GOARCH="amd64" GOBIN="" GOCACHE="/home/jyan/.cache/go-build" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOOS="linux" GOPATH="/home/jyan/work/go_work" GOPROXY="" GORACE="" GOROOT="/home/jyan/.gvm/gos/go1.12.5" GOTMPDIR="" GOTOOLDIR="/home/jyan/.gvm/gos/go1.12.5/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-build781532413=/tmp/go-build -gno-record-gcc-switches"

python version: Python 2.7.12 (default, Nov 12 2018, 14:36:49) [GCC 5.4.0 20160609] on linux2

foo.py

import sys
def print_odds(limit=10):
    """ Print odds numbers < limit """
    for i in range(limit):
        if i%2:
            sys.stderr.write("{}\n".format(i))
def print_even(limit=10):
    """ Print even numbers < limit """
    for i in range(limit):
        if i%2 == 0:
            sys.stderr.write("{}\n".format(i))

main.go

package main

import (
    "fmt"
    "github.com/sbinet/go-python"
    "runtime"
    "sync"
)

func init() {
    err := python.Initialize()
    if err != nil {
        panic(err.Error())
    }
}

func main() {
    var wg sync.WaitGroup
    wg.Add(20000)
    m := python.PyImport_ImportModule("sys")
    if m == nil {
        fmt.Println("import error")
        return
    }
    path := m.GetAttrString("path")
    if path == nil {
        fmt.Println("get path error")
        return
    }
    currentDir := python.PyString_FromString("/home/jyan/work/go_work/src/python_test2")
    python.PyList_Insert(path, 0, currentDir)
    fooModule := python.PyImport_ImportModule("foo")
    odds := fooModule.GetAttrString("print_odds")
    even := fooModule.GetAttrString("print_even")
    state := python.PyEval_SaveThread()
    for i := 0; i < 10000; i++ {
        go func() {
            runtime.LockOSThread()
            _gstate := python.PyGILState_Ensure()
            odds.Call(python.PyTuple_New(0), python.PyDict_New())
            python.PyGILState_Release(_gstate)
            wg.Done()
        }()
        go func() {
            runtime.LockOSThread()
            _gstate := python.PyGILState_Ensure()
            even.Call(python.PyTuple_New(0), python.PyDict_New())
            python.PyGILState_Release(_gstate)
            wg.Done()
        }()
    }

    wg.Wait()
    python.PyEval_RestoreThread(state)
    python.Finalize()
}

10000 goroutine is ok

20000 goroutine:

goroutine 20004 [syscall, locked to thread]: github.com/sbinet/go-python._Cfunc_PyGILState_Ensure(0x0) _cgo_gotypes.go:2887 +0x49 github.com/sbinet/go-python.PyGILState_Ensure(...) /home/jyan/work/go_work/src/github.com/sbinet/go-python/python.go:87 main.main.func1(0xc000010128, 0xc000020120) /home/jyan/work/go_work/src/python_test2/main.go:39 +0x2c created by main.main /home/jyan/work/go_work/src/python_test2/main.go:37 +0x1a8

goroutine 20005 [syscall, locked to thread]: github.com/sbinet/go-python._Cfunc_PyGILState_Ensure(0x0) _cgo_gotypes.go:2887 +0x49 github.com/sbinet/go-python.PyGILState_Ensure(...) /home/jyan/work/go_work/src/github.com/sbinet/go-python/python.go:87 main.main.func2(0xc000010130, 0xc000020120) /home/jyan/work/go_work/src/python_test2/main.go:46 +0x2c created by main.main /home/jyan/work/go_work/src/python_test2/main.go:44 +0x1d4

sbinet commented 5 years ago

you don't deref the result of python.Object.Call: https://docs.python.org/2/c-api/object.html?highlight=pyobject_call#c.PyObject_Call

sbinet commented 5 years ago

(feel free to reopen if that didn't solve your issue.)