diff options
Diffstat (limited to 'gi/pygi-closure.c')
-rw-r--r-- | gi/pygi-closure.c | 205 |
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; +} |