summaryrefslogtreecommitdiffstats
path: root/gi/pygi-closure.c
diff options
context:
space:
mode:
Diffstat (limited to 'gi/pygi-closure.c')
-rw-r--r--gi/pygi-closure.c205
1 files changed, 205 insertions, 0 deletions
diff --git a/gi/pygi-closure.c b/gi/pygi-closure.c
new file mode 100644
index 0000000..0ad8fef
--- /dev/null
+++ b/gi/pygi-closure.c
@@ -0,0 +1,205 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * vim: tabstop=4 shiftwidth=4 expandtab
+ *
+ * pygi-closure.c: PyGI C Closure functions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#include "pygi-private.h"
+
+/* This maintains a list of closures which can be free'd whenever
+ as they have been called. We will free them on the next
+ library function call.
+ */
+static GSList* async_free_list;
+
+void
+_pygi_closure_handle (ffi_cif *cif,
+ void *result,
+ void **args,
+ void *data)
+{
+ PyGILState_STATE state;
+ PyGICClosure *closure = data;
+ gint n_args, i;
+ GIArgInfo *arg_info;
+ GIDirection arg_direction;
+ GITypeInfo *arg_type;
+ GITransfer arg_transfer;
+ GITypeTag arg_tag;
+ GITypeTag return_tag;
+ GITransfer return_transfer;
+ GITypeInfo *return_type;
+ PyObject *retval;
+ PyObject *py_args;
+ PyObject *pyarg;
+ gint n_in_args, n_out_args;
+
+
+ /* Lock the GIL as we are coming into this code without the lock and we
+ may be executing python code */
+ state = PyGILState_Ensure();
+
+ return_type = g_callable_info_get_return_type(closure->info);
+ return_tag = g_type_info_get_tag(return_type);
+ return_transfer = g_callable_info_get_caller_owns(closure->info);
+
+ n_args = g_callable_info_get_n_args (closure->info);
+
+ py_args = PyTuple_New(n_args);
+ if (py_args == NULL) {
+ PyErr_Clear();
+ goto end;
+ }
+
+ n_in_args = 0;
+
+ for (i = 0; i < n_args; i++) {
+ arg_info = g_callable_info_get_arg (closure->info, i);
+ arg_type = g_arg_info_get_type (arg_info);
+ arg_transfer = g_arg_info_get_ownership_transfer(arg_info);
+ arg_tag = g_type_info_get_tag(arg_type);
+ arg_direction = g_arg_info_get_direction(arg_info);
+ switch (arg_tag){
+ case GI_TYPE_TAG_VOID:
+ {
+ if (g_type_info_is_pointer(arg_type)) {
+ if (PyTuple_SetItem(py_args, n_in_args, closure->user_data) != 0) {
+ PyErr_Clear();
+ goto end;
+ }
+ n_in_args++;
+ continue;
+ }
+ }
+ case GI_TYPE_TAG_ERROR:
+ {
+ continue;
+ }
+ default:
+ {
+ pyarg = _pygi_argument_to_object (args[i],
+ arg_type,
+ arg_transfer);
+
+ if(PyTuple_SetItem(py_args, n_in_args, pyarg) != 0) {
+ PyErr_Clear();
+ goto end;
+ }
+ n_in_args++;
+ g_base_info_unref((GIBaseInfo*)arg_info);
+ g_base_info_unref((GIBaseInfo*)arg_type);
+ }
+ }
+
+ }
+
+ if(_PyTuple_Resize (&py_args, n_in_args) != 0) {
+ PyErr_Clear();
+ goto end;
+ }
+
+ retval = PyObject_CallObject((PyObject *)closure->function, py_args);
+
+ Py_DECREF(py_args);
+
+ if (retval == NULL) {
+ goto end;
+ }
+
+ *(GArgument*)result = _pygi_argument_from_object(retval, return_type, return_transfer);
+
+end:
+ g_base_info_unref((GIBaseInfo*)return_type);
+
+ PyGILState_Release(state);
+
+ if (closure->user_data)
+ Py_XDECREF(closure->user_data);
+
+ /* Now that the closure has finished we can make a decision about how
+ to free it. Scope call gets free'd now, scope notified will be freed
+ when the notify is called and we can free async anytime we want
+ once we return from this function */
+ switch (closure->scope) {
+ case GI_SCOPE_TYPE_CALL:
+ _pygi_invoke_closure_free(closure);
+ break;
+ case GI_SCOPE_TYPE_NOTIFIED:
+ break;
+ case GI_SCOPE_TYPE_ASYNC:
+ /* Append this PyGICClosure to a list of closure that we will free
+ after we're done with this function invokation */
+ async_free_list = g_slist_prepend(async_free_list, closure);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+void _pygi_invoke_closure_free(gpointer data)
+{
+ PyGICClosure* invoke_closure = (PyGICClosure *)data;
+
+
+ Py_DECREF(invoke_closure->function);
+
+ g_callable_info_free_closure(invoke_closure->info,
+ invoke_closure->closure);
+
+ if (invoke_closure->info)
+ g_base_info_unref((GIBaseInfo*)invoke_closure->info);
+
+ g_slice_free(PyGICClosure, invoke_closure);
+}
+
+
+PyGICClosure*
+_pygi_make_native_closure (GICallableInfo* info,
+ GIArgInfo* arg_info,
+ PyObject *py_function,
+ gpointer py_user_data)
+{
+ PyGICClosure *closure;
+ ffi_closure *fficlosure;
+
+ /* Begin by cleaning up old async functions */
+ g_slist_foreach(async_free_list, (GFunc)_pygi_invoke_closure_free, NULL);
+ g_slist_free(async_free_list);
+ async_free_list = NULL;
+
+ /* Build the closure itself */
+ closure = g_slice_new0(PyGICClosure);
+ closure->info = (GICallableInfo *) g_base_info_ref ((GIBaseInfo *) info);
+ closure->function = py_function;
+ closure->user_data = py_user_data;
+
+ Py_INCREF(py_function);
+ if (closure->user_data)
+ Py_INCREF(closure->user_data);
+
+ fficlosure =
+ g_callable_info_prepare_closure (info, &closure->cif, _pygi_closure_handle,
+ closure);
+ closure->closure = fficlosure;
+
+ /* Give the closure the information it needs to determine when
+ to free itself later */
+ closure->scope = g_arg_info_get_scope(arg_info);
+
+ return closure;
+}