|
1 | 1 | #include <inline-python.h> |
2 | 2 | #include <stdlib.h> |
3 | 3 |
|
4 | | -PyObject *inline_py_function_wrapper(PyCFunction fun, int flags) { |
5 | | - PyMethodDef *meth = malloc(sizeof(PyMethodDef)); |
6 | | - meth->ml_name = "[inline_python]"; |
7 | | - meth->ml_meth = fun; |
8 | | - meth->ml_flags = flags; |
9 | | - meth->ml_doc = "Wrapper constructed by inline-python"; |
10 | | - // Python wrapper which carries PyMethodDef |
11 | | - PyObject* meth_obj = PyCapsule_New(meth, NULL, &inline_py_free_capsule); |
| 4 | +// ================================================================ |
| 5 | +// Callbacks |
| 6 | +// |
| 7 | +// General idea: we store function pointer (haskell's FunPtr) in |
| 8 | +// PyCapsule and use to call function. Most importantly we must |
| 9 | +// release GIL before calling into haskell. Haskell callback will |
| 10 | +// happen on different thread (on threaded RTS). So it'll have to |
| 11 | +// reacquire GIL there. |
| 12 | +// ================================================================ |
| 13 | + |
| 14 | +int inline_py_callback_depth = 0; |
| 15 | + |
| 16 | +static PyObject* callback_METH_O(PyObject* self, PyObject* arg) { |
| 17 | + PyObject *res; |
| 18 | + PyCFunction *fun = PyCapsule_GetPointer(self, NULL); |
| 19 | + //-- |
| 20 | + inline_py_callback_depth++; |
| 21 | +Py_BEGIN_ALLOW_THREADS |
| 22 | + res = (*fun)(self, arg); |
| 23 | +Py_END_ALLOW_THREADS |
| 24 | + inline_py_callback_depth--; |
| 25 | + return res; |
| 26 | +} |
| 27 | + |
| 28 | +static PyObject* callback_METH_FASTCALL(PyObject* self, PyObject** args, Py_ssize_t nargs) { |
| 29 | + PyObject *res; |
| 30 | + PyCFunctionFast *fun = PyCapsule_GetPointer(self, NULL); |
| 31 | + //-- |
| 32 | + inline_py_callback_depth++; |
| 33 | +Py_BEGIN_ALLOW_THREADS |
| 34 | + res = (*fun)(self, args, nargs); |
| 35 | +Py_END_ALLOW_THREADS |
| 36 | + inline_py_callback_depth--; |
| 37 | + return res; |
| 38 | +} |
| 39 | + |
| 40 | +static void capsule_free_FunPtr(PyObject* capsule) { |
| 41 | + PyCFunction *fun = PyCapsule_GetPointer(capsule, NULL); |
| 42 | + // We call directly to haskell RTS to free FunPtr. Only question |
| 43 | + // is how stable is this API. |
| 44 | + freeHaskellFunctionPtr(*fun); |
| 45 | + free(fun); |
| 46 | +} |
| 47 | + |
| 48 | +static PyMethodDef method_METH_O = { |
| 49 | + .ml_name = "[inline_python]", |
| 50 | + .ml_meth = callback_METH_O, |
| 51 | + .ml_flags = METH_O, |
| 52 | + .ml_doc = "Wrapper for haskell callback" |
| 53 | +}; |
| 54 | + |
| 55 | +static PyMethodDef method_METH_FASTCALL = { |
| 56 | + .ml_name = "[inline_python]", |
| 57 | + .ml_meth = (PyCFunction)callback_METH_FASTCALL, |
| 58 | + .ml_flags = METH_FASTCALL, |
| 59 | + .ml_doc = "Wrapper for haskell callback" |
| 60 | +}; |
| 61 | + |
| 62 | +PyObject *inline_py_callback_METH_O(PyCFunction fun) { |
| 63 | + PyCFunction *buf = malloc(sizeof(PyCFunction)); |
| 64 | + *buf = fun; |
| 65 | + PyObject* self = PyCapsule_New(buf, NULL, &capsule_free_FunPtr); |
12 | 66 | if( PyErr_Occurred() ) |
13 | 67 | return NULL; |
14 | 68 | // Python function |
15 | | - PyObject* f = PyCFunction_New(meth, meth_obj); |
16 | | - Py_DECREF(meth_obj); |
17 | | - return f; |
| 69 | + PyObject* f = PyCFunction_New(&method_METH_O, self); |
| 70 | + Py_DECREF(self); |
| 71 | + return f; |
18 | 72 | } |
19 | 73 |
|
| 74 | +PyObject *inline_py_callback_METH_FASTCALL(PyCFunctionFast fun) { |
| 75 | + PyCFunctionFast *buf = malloc(sizeof(PyCFunctionFast)); |
| 76 | + *buf = fun; |
| 77 | + PyObject* self = PyCapsule_New(buf, NULL, &capsule_free_FunPtr); |
| 78 | + if( PyErr_Occurred() ) |
| 79 | + return NULL; |
| 80 | + // Python function |
| 81 | + PyObject* f = PyCFunction_New(&method_METH_FASTCALL, self); |
| 82 | + Py_DECREF(self); |
| 83 | + return f; |
| 84 | +} |
| 85 | + |
| 86 | + |
| 87 | +// ================================================================ |
| 88 | +// Marshalling |
| 89 | +// ================================================================ |
| 90 | + |
20 | 91 | int inline_py_unpack_iterable(PyObject *iterable, int n, PyObject **out) { |
21 | 92 | // Initialize iterator. If object is not an iterable we treat this |
22 | 93 | // as not an exception but as a conversion failure |
@@ -57,11 +128,3 @@ int inline_py_unpack_iterable(PyObject *iterable, int n, PyObject **out) { |
57 | 128 | return -1; |
58 | 129 | } |
59 | 130 |
|
60 | | -void inline_py_free_capsule(PyObject* py) { |
61 | | - PyMethodDef *meth = PyCapsule_GetPointer(py, NULL); |
62 | | - // HACK: We want to release wrappers created by wrapper. It |
63 | | - // doesn't seems to be nice and stable C API |
64 | | - freeHaskellFunctionPtr(meth->ml_meth); |
65 | | - free(meth); |
66 | | -} |
67 | | - |
|
0 commit comments