func initAndLock(initsigs bool) *Lock { // Lock the current goroutine to the current OS thread, until we have // released the GIL (as CPython uses per-thread state) runtime.LockOSThread() // Initialize the default Python interpreter if initsigs { C.Py_InitializeEx(1) } else { C.Py_InitializeEx(0) } // Enable Python thread support, and then immediately release the GIL (and // thus "deativate" and per-thread state associated with the current thread C.PyEval_InitThreads() C.PyEval_SaveThread() // We can now unlock the current goroutine from the current OS thread, as // there is no active per-thread state runtime.UnlockOSThread() // Now that Python is setup, we can return a locked Lock, ready for the // calling code to use return NewLock() }
// UnblockThreads() releases the GIL so that other Python threads may run. It // does not free the per-thread state created by Lock, nor does it call // runtime.UnlockOSThread(). This function is intended to allow other Python // threads to run whilst the calling code is either performing a slow/long // running operation or is going to block. // // Nothing happens if this function is called more than once, all calls but the // first will be ignored. func (lock *Lock) UnblockThreads() { if lock.gilState == nil { panic("UnblockThreads() called on Unlocked Lock") } if lock.thState == nil { lock.thState = C.PyEval_SaveThread() } }
//export gil func gil(self, args *C.PyObject) *C.PyObject { var res *C.PyObject tState := C.PyEval_SaveThread() var mu sync.Mutex mu.Lock() go func() { C.PyEval_RestoreThread(tState) res = C.PyLong_FromLong(1) mu.Unlock() }() mu.Lock() return res }
func threadInit() (defaultThreadState *C.PyThreadState) { initLock.Lock() defer initLock.Unlock() if !initialized { C.Py_InitializeEx(0) C.PyEval_InitThreads() C.PySys_SetArgvEx(0, nil, 0) pyEmptyTuple = C.PyTuple_New(0) falseObject = &object{C.False_INCREF()} trueObject = &object{C.True_INCREF()} defaultThreadState = C.PyEval_SaveThread() initialized = true } return }
func (t *Thread) loop() { runtime.LockOSThread() threadState := threadInit() if threadState == nil { gilState := C.PyGILState_Ensure() oldThreadState := C.PyGILState_GetThisThreadState() threadState = C.PyThreadState_New(oldThreadState.interp) C.PyGILState_Release(gilState) } for f := range t.queue { C.PyEval_RestoreThread(threadState) f() threadState = C.PyEval_SaveThread() } gilState := C.PyGILState_Ensure() C.PyThreadState_Clear(threadState) C.PyThreadState_Delete(threadState) C.PyGILState_Release(gilState) }
// PyThreadState* PyEval_SaveThread() // Release the global interpreter lock (if it has been created and thread // support is enabled) and reset the thread state to NULL, returning the // previous thread state (which is not NULL). If the lock has been created, // the current thread must have acquired it. (This function is available even // when thread support is disabled at compile time.) func PyEval_SaveThread() *PyThreadState { state := C.PyEval_SaveThread() return &PyThreadState{ptr: state} }