-
Notifications
You must be signed in to change notification settings - Fork 52
Added example for go routines #21
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import sys | ||
|
||
def print_odds(limit=10): | ||
""" | ||
Print odds numbers < limit | ||
""" | ||
for i in range(limit): | ||
if i%2: | ||
sys.stderr.write("odds: {}\n".format(i)) | ||
|
||
def print_even(limit=10): | ||
""" | ||
Print even numbers < limit | ||
""" | ||
for i in range(limit): | ||
if i%2 == 0: | ||
sys.stderr.write("even: {}\n".format(i)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"log" | ||
"runtime" | ||
"sync" | ||
|
||
python3 "github.com/go-python/cpy3" | ||
) | ||
|
||
var wg sync.WaitGroup | ||
|
||
func isPyErr(val *python3.PyObject) bool { | ||
|
||
if val == nil && python3.PyErr_Occurred() != nil { | ||
python3.PyErr_Print() | ||
return true | ||
} | ||
|
||
return false | ||
} | ||
|
||
func callPyFunc(pyFunc *python3.PyObject, args *python3.PyObject, kwargs *python3.PyObject) { | ||
runtime.LockOSThread() | ||
|
||
_gstate := python3.PyGILState_Ensure() // acquire the GIL, the GIL is locked by the 1st goroutine | ||
defer python3.PyGILState_Release(_gstate) // release the GIL, the GIL is unlocked for using by others | ||
|
||
ret := pyFunc.Call(args, kwargs) | ||
if isPyErr(ret) { | ||
return | ||
} | ||
defer ret.DecRef() | ||
|
||
wg.Done() | ||
} | ||
|
||
func main() { | ||
// Undo all initializations made by Py_Initialize() and subsequent | ||
defer python3.Py_Finalize() | ||
|
||
// Prints any python error if it was here | ||
// no needs to call it after each single check of PyErr_Occurred() | ||
// defer python3.PyErr_Print() | ||
|
||
// Initialize the Python interpreter and | ||
// since version 3.7 it also create the GIL explicitly by calling PyEval_InitThreads() | ||
// so you don’t have to call PyEval_InitThreads() yourself anymore | ||
python3.Py_Initialize() // create the GIL, the GIL is locked by the main thread | ||
|
||
if !python3.Py_IsInitialized() { | ||
log.Printf("%+v", errors.New("error initializing the python interpreter")) | ||
return | ||
} | ||
|
||
fooModule := python3.PyImport_ImportModule("foo") // new reference, a call DecRef() is needed | ||
if isPyErr(fooModule) { | ||
return | ||
} | ||
defer fooModule.DecRef() | ||
|
||
odds := fooModule.GetAttrString("print_odds") // new reference, a call DecRef() is needed | ||
if isPyErr(odds) { | ||
return | ||
} | ||
defer odds.DecRef() | ||
|
||
even := fooModule.GetAttrString("print_even") // new reference, a call DecRef() is needed | ||
if isPyErr(even) { | ||
return | ||
} | ||
defer even.DecRef() | ||
|
||
limit := python3.PyLong_FromGoInt(50) // new reference, will stolen later, a call DecRef() is NOT needed | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. again the docs aren't clear if this causes a PyErr. Only check for |
||
if limit == nil { | ||
log.Printf("%+v", errors.New("error creating python long object")) | ||
return | ||
} | ||
|
||
args := python3.PyTuple_New(1) // new reference, a call DecRef() is needed | ||
if args == nil { | ||
log.Printf("%+v", errors.New("error creating python tuple object")) | ||
return | ||
} | ||
defer args.DecRef() | ||
|
||
ret := python3.PyTuple_SetItem(args, 0, limit) // steals reference to limit | ||
if ret != 0 { | ||
log.Printf("%+v", errors.New("error setting a tuple item")) | ||
limit.DecRef() | ||
limit = nil | ||
return | ||
} | ||
// Cleans the Go variable, because now a new owner is caring about related PyObject | ||
// no action, such as a call DecRef(), is needed here | ||
limit = nil | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this cleanup is too early? In the next block you're still using |
||
|
||
kwargs := python3.PyDict_New() | ||
if kwargs == nil { | ||
log.Printf("%+v", errors.New("error initializing kwargs in callPyFunc ")) | ||
return | ||
} | ||
defer kwargs.DecRef() | ||
|
||
// Save the current state and release the GIL | ||
// so that goroutines can acquire it | ||
state := python3.PyEval_SaveThread() // release the GIL, the GIL is unlocked for using by goroutines | ||
|
||
wg.Add(2) | ||
|
||
go callPyFunc(odds, args, kwargs) | ||
|
||
go callPyFunc(even, args, kwargs) | ||
|
||
wg.Wait() | ||
|
||
// Restore the state and lock the GIL | ||
python3.PyEval_RestoreThread(state) // acquire the GIL, the GIL is locked by the main thread | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that's safe. The docs say: