Commit 2cddba81 authored by Simon Feltman's avatar Simon Feltman
Browse files

cache refactoring: Move closure setup and marshaling into pygi-closure

Move closure argument caching and marshaling fragments into
pygi-closure.c.

https://bugzilla.gnome.org/show_bug.cgi?id=709700
parent 18d82747
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "pygi-basictype.h" #include "pygi-basictype.h"
#include "pygi-list.h" #include "pygi-list.h"
#include "pygi-array.h" #include "pygi-array.h"
#include "pygi-closure.h"
#include "pygi-error.h" #include "pygi-error.h"
...@@ -162,17 +163,6 @@ _sequence_cache_free_func (PyGISequenceCache *cache) ...@@ -162,17 +163,6 @@ _sequence_cache_free_func (PyGISequenceCache *cache)
} }
} }
static void
_callback_cache_free_func (PyGICallbackCache *cache)
{
if (cache != NULL) {
if (cache->interface_info != NULL)
g_base_info_unref ( (GIBaseInfo *)cache->interface_info);
g_slice_free (PyGICallbackCache, cache);
}
}
void void
_pygi_callable_cache_free (PyGICallableCache *cache) _pygi_callable_cache_free (PyGICallableCache *cache)
{ {
...@@ -248,28 +238,6 @@ pygi_arg_sequence_setup (PyGISequenceCache *sc, ...@@ -248,28 +238,6 @@ pygi_arg_sequence_setup (PyGISequenceCache *sc,
return TRUE; return TRUE;
} }
static PyGICallbackCache *
_callback_cache_new (GIArgInfo *arg_info,
GIInterfaceInfo *iface_info,
gssize child_offset)
{
PyGICallbackCache *cc;
cc = g_slice_new0 (PyGICallbackCache);
( (PyGIArgCache *)cc)->destroy_notify = (GDestroyNotify)_callback_cache_free_func;
cc->user_data_index = g_arg_info_get_closure (arg_info);
if (cc->user_data_index != -1)
cc->user_data_index += child_offset;
cc->destroy_notify_index = g_arg_info_get_destroy (arg_info);
if (cc->destroy_notify_index != -1)
cc->destroy_notify_index += child_offset;
cc->scope = g_arg_info_get_scope (arg_info);
g_base_info_ref( (GIBaseInfo *)iface_info);
cc->interface_info = iface_info;
return cc;
}
PyGIArgCache * PyGIArgCache *
_arg_cache_alloc (void) _arg_cache_alloc (void)
{ {
...@@ -334,38 +302,6 @@ _arg_cache_to_py_interface_object_setup (PyGIArgCache *arg_cache, ...@@ -334,38 +302,6 @@ _arg_cache_to_py_interface_object_setup (PyGIArgCache *arg_cache,
arg_cache->to_py_cleanup = _pygi_marshal_cleanup_to_py_interface_object; arg_cache->to_py_cleanup = _pygi_marshal_cleanup_to_py_interface_object;
} }
static void
_arg_cache_from_py_interface_callback_setup (PyGIArgCache *arg_cache,
PyGICallableCache *callable_cache)
{
PyGICallbackCache *callback_cache = (PyGICallbackCache *)arg_cache;
if (callback_cache->user_data_index >= 0) {
PyGIArgCache *user_data_arg_cache = _arg_cache_alloc ();
user_data_arg_cache->meta_type = PYGI_META_ARG_TYPE_CHILD_WITH_PYARG;
user_data_arg_cache->direction = PYGI_DIRECTION_FROM_PYTHON;
user_data_arg_cache->has_default = TRUE; /* always allow user data with a NULL default. */
_pygi_callable_cache_set_arg (callable_cache, callback_cache->user_data_index,
user_data_arg_cache);
}
if (callback_cache->destroy_notify_index >= 0) {
PyGIArgCache *destroy_arg_cache = _arg_cache_alloc ();
destroy_arg_cache->meta_type = PYGI_META_ARG_TYPE_CHILD;
destroy_arg_cache->direction = PYGI_DIRECTION_FROM_PYTHON;
_pygi_callable_cache_set_arg (callable_cache, callback_cache->destroy_notify_index,
destroy_arg_cache);
}
arg_cache->from_py_marshaller = _pygi_marshal_from_py_interface_callback;
arg_cache->from_py_cleanup = _pygi_marshal_cleanup_from_py_interface_callback;
}
static void
_arg_cache_to_py_interface_callback_setup (void)
{
PyErr_Format(PyExc_NotImplementedError,
"Callback returns are not supported");
}
static void static void
_arg_cache_from_py_interface_enum_setup (PyGIArgCache *arg_cache, _arg_cache_from_py_interface_enum_setup (PyGIArgCache *arg_cache,
GITransfer transfer) GITransfer transfer)
...@@ -404,45 +340,22 @@ _arg_cache_new_for_interface (GIInterfaceInfo *iface_info, ...@@ -404,45 +340,22 @@ _arg_cache_new_for_interface (GIInterfaceInfo *iface_info,
PyGICallableCache *callable_cache) PyGICallableCache *callable_cache)
{ {
PyGIArgCache *arg_cache = NULL; PyGIArgCache *arg_cache = NULL;
gssize child_offset = 0;
GIInfoType info_type; GIInfoType info_type;
if (callable_cache != NULL)
child_offset =
(callable_cache->function_type == PYGI_FUNCTION_TYPE_METHOD ||
callable_cache->function_type == PYGI_FUNCTION_TYPE_VFUNC) ? 1: 0;
info_type = g_base_info_get_type ( (GIBaseInfo *)iface_info); info_type = g_base_info_get_type ( (GIBaseInfo *)iface_info);
/* Callbacks are special cased */ switch (info_type) {
if (info_type == GI_INFO_TYPE_CALLBACK) { case GI_INFO_TYPE_CALLBACK:
PyGICallbackCache *callback_cache; {
return pygi_arg_callback_new_from_info (type_info,
if (direction & PYGI_DIRECTION_TO_PYTHON) { arg_info,
_arg_cache_to_py_interface_callback_setup (); transfer,
return NULL; direction,
iface_info,
callable_cache);
} }
default:
callback_cache = ; /* pass through to old model of setup */
_callback_cache_new (arg_info,
iface_info,
child_offset);
arg_cache = (PyGIArgCache *)callback_cache;
if (arg_cache == NULL)
return NULL;
pygi_arg_base_setup (arg_cache,
type_info,
arg_info,
transfer,
direction);
if (direction & PYGI_DIRECTION_FROM_PYTHON)
_arg_cache_from_py_interface_callback_setup (arg_cache, callable_cache);
return arg_cache;
} }
arg_cache = (PyGIArgCache *)_interface_cache_new (iface_info); arg_cache = (PyGIArgCache *)_interface_cache_new (iface_info);
......
...@@ -155,15 +155,6 @@ typedef struct _PyGIInterfaceCache ...@@ -155,15 +155,6 @@ typedef struct _PyGIInterfaceCache
gchar *type_name; gchar *type_name;
} PyGIInterfaceCache; } PyGIInterfaceCache;
typedef struct _PyGICallbackCache
{
PyGIArgCache arg_cache;
gssize user_data_index;
gssize destroy_notify_index;
GIScopeType scope;
GIInterfaceInfo *interface_info;
} PyGICallbackCache;
struct _PyGICallableCache struct _PyGICallableCache
{ {
const gchar *name; const gchar *name;
......
...@@ -14,12 +14,23 @@ ...@@ -14,12 +14,23 @@
* Lesser General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software * License along with this library; if not, see <http://www.gnu.org/licenses/>.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/ */
#include "pygi-private.h" #include "pygi-private.h"
#include "pygi-closure.h"
typedef struct _PyGICallbackCache
{
PyGIArgCache arg_cache;
gssize user_data_index;
gssize destroy_notify_index;
GIScopeType scope;
GIInterfaceInfo *interface_info;
} PyGICallbackCache;
static PyGICClosure *global_destroy_notify;
/* This maintains a list of closures which can be free'd whenever /* This maintains a list of closures which can be free'd whenever
as they have been called. We will free them on the next as they have been called. We will free them on the next
...@@ -669,3 +680,300 @@ _pygi_make_native_closure (GICallableInfo* info, ...@@ -669,3 +680,300 @@ _pygi_make_native_closure (GICallableInfo* info,
return closure; return closure;
} }
/* _pygi_destroy_notify_dummy:
*
* Dummy method used in the occasion when a method has a GDestroyNotify
* argument without user data.
*/
static void
_pygi_destroy_notify_dummy (gpointer data) {
}
static void
_pygi_destroy_notify_callback_closure (ffi_cif *cif,
void *result,
void **args,
void *data)
{
PyGICClosure *info = * (void**) (args[0]);
g_assert (info);
_pygi_invoke_closure_free (info);
}
/* _pygi_destroy_notify_create:
*
* Method used in the occasion when a method has a GDestroyNotify
* argument with user data.
*/
static PyGICClosure*
_pygi_destroy_notify_create (void)
{
if (!global_destroy_notify) {
PyGICClosure *destroy_notify = g_slice_new0 (PyGICClosure);
GIBaseInfo* glib_destroy_notify;
g_assert (destroy_notify);
glib_destroy_notify = g_irepository_find_by_name (NULL, "GLib", "DestroyNotify");
g_assert (glib_destroy_notify != NULL);
g_assert (g_base_info_get_type (glib_destroy_notify) == GI_INFO_TYPE_CALLBACK);
destroy_notify->closure = g_callable_info_prepare_closure ( (GICallableInfo*) glib_destroy_notify,
&destroy_notify->cif,
_pygi_destroy_notify_callback_closure,
NULL);
global_destroy_notify = destroy_notify;
}
return global_destroy_notify;
}
static gboolean
_pygi_marshal_from_py_interface_callback (PyGIInvokeState *state,
PyGICallableCache *callable_cache,
PyGIArgCache *arg_cache,
PyObject *py_arg,
GIArgument *arg,
gpointer *cleanup_data)
{
GICallableInfo *callable_info;
PyGICClosure *closure;
PyGIArgCache *user_data_cache = NULL;
PyGIArgCache *destroy_cache = NULL;
PyGICallbackCache *callback_cache;
PyObject *py_user_data = NULL;
callback_cache = (PyGICallbackCache *)arg_cache;
if (callback_cache->user_data_index > 0) {
user_data_cache = _pygi_callable_cache_get_arg (callable_cache, callback_cache->user_data_index);
if (user_data_cache->py_arg_index < state->n_py_in_args) {
/* py_user_data is a borrowed reference. */
py_user_data = PyTuple_GetItem (state->py_in_args, user_data_cache->py_arg_index);
if (!py_user_data)
return FALSE;
/* NULL out user_data if it was not supplied and the default arg placeholder
* was used instead.
*/
if (py_user_data == _PyGIDefaultArgPlaceholder) {
py_user_data = NULL;
} else if (callable_cache->user_data_varargs_index < 0) {
/* For non-variable length user data, place the user data in a
* single item tuple which is concatenated to the callbacks arguments.
* This allows callback input arg marshaling to always expect a
* tuple for user data. Note the
*/
py_user_data = Py_BuildValue("(O)", py_user_data, NULL);
} else {
/* increment the ref borrowed from PyTuple_GetItem above */
Py_INCREF (py_user_data);
}
}
}
if (py_arg == Py_None) {
return TRUE;
}
if (!PyCallable_Check (py_arg)) {
PyErr_Format (PyExc_TypeError,
"Callback needs to be a function or method not %s",
py_arg->ob_type->tp_name);
return FALSE;
}
callable_info = (GICallableInfo *)callback_cache->interface_info;
closure = _pygi_make_native_closure (callable_info, callback_cache->scope, py_arg, py_user_data);
arg->v_pointer = closure->closure;
/* always decref the user data as _pygi_make_native_closure adds its own ref */
Py_XDECREF (py_user_data);
/* The PyGICClosure instance is used as user data passed into the C function.
* The return trip to python will marshal this back and pull the python user data out.
*/
if (user_data_cache != NULL) {
state->in_args[user_data_cache->c_arg_index].v_pointer = closure;
}
/* Setup a GDestroyNotify callback if this method supports it along with
* a user data field. The user data field is a requirement in order
* free resources and ref counts associated with this arguments closure.
* In case a user data field is not available, show a warning giving
* explicit information and setup a dummy notification to avoid a crash
* later on in _pygi_destroy_notify_callback_closure.
*/
if (callback_cache->destroy_notify_index > 0) {
destroy_cache = _pygi_callable_cache_get_arg (callable_cache, callback_cache->destroy_notify_index);
}
if (destroy_cache) {
if (user_data_cache != NULL) {
PyGICClosure *destroy_notify = _pygi_destroy_notify_create ();
state->in_args[destroy_cache->c_arg_index].v_pointer = destroy_notify->closure;
} else {
gchar *msg = g_strdup_printf("Callables passed to %s will leak references because "
"the method does not support a user_data argument. "
"See: https://bugzilla.gnome.org/show_bug.cgi?id=685598",
callable_cache->name);
if (PyErr_WarnEx(PyExc_RuntimeWarning, msg, 2)) {
g_free(msg);
_pygi_invoke_closure_free(closure);
return FALSE;
}
g_free(msg);
state->in_args[destroy_cache->c_arg_index].v_pointer = _pygi_destroy_notify_dummy;
}
}
/* Use the PyGIClosure as data passed to cleanup for GI_SCOPE_TYPE_CALL. */
*cleanup_data = closure;
return TRUE;
}
static PyObject *
_pygi_marshal_to_py_interface_callback (PyGIInvokeState *state,
PyGICallableCache *callable_cache,
PyGIArgCache *arg_cache,
GIArgument *arg)
{
PyObject *py_obj = NULL;
PyErr_Format (PyExc_NotImplementedError,
"Marshalling a callback to PyObject is not supported");
return py_obj;
}
static void
_callback_cache_free_func (PyGICallbackCache *cache)
{
if (cache != NULL) {
if (cache->interface_info != NULL)
g_base_info_unref ( (GIBaseInfo *)cache->interface_info);
g_slice_free (PyGICallbackCache, cache);
}
}
static void
_pygi_marshal_cleanup_from_py_interface_callback (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
PyObject *py_arg,
gpointer data,
gboolean was_processed)
{
PyGICallbackCache *callback_cache = (PyGICallbackCache *)arg_cache;
if (was_processed && callback_cache->scope == GI_SCOPE_TYPE_CALL) {
_pygi_invoke_closure_free (data);
}
}
static void
_arg_cache_from_py_interface_callback_setup (PyGIArgCache *arg_cache,
PyGICallableCache *callable_cache)
{
PyGICallbackCache *callback_cache = (PyGICallbackCache *)arg_cache;
if (callback_cache->user_data_index >= 0) {
PyGIArgCache *user_data_arg_cache = _arg_cache_alloc ();
user_data_arg_cache->meta_type = PYGI_META_ARG_TYPE_CHILD_WITH_PYARG;
user_data_arg_cache->direction = PYGI_DIRECTION_FROM_PYTHON;
user_data_arg_cache->has_default = TRUE; /* always allow user data with a NULL default. */
_pygi_callable_cache_set_arg (callable_cache, callback_cache->user_data_index,
user_data_arg_cache);
}
if (callback_cache->destroy_notify_index >= 0) {
PyGIArgCache *destroy_arg_cache = _arg_cache_alloc ();
destroy_arg_cache->meta_type = PYGI_META_ARG_TYPE_CHILD;
destroy_arg_cache->direction = PYGI_DIRECTION_FROM_PYTHON;
_pygi_callable_cache_set_arg (callable_cache, callback_cache->destroy_notify_index,
destroy_arg_cache);
}
arg_cache->from_py_marshaller = _pygi_marshal_from_py_interface_callback;
arg_cache->from_py_cleanup = _pygi_marshal_cleanup_from_py_interface_callback;
}
static gboolean
pygi_arg_callback_setup_from_info (PyGICallbackCache *arg_cache,
GITypeInfo *type_info,
GIArgInfo *arg_info, /* may be null */
GITransfer transfer,
PyGIDirection direction,
GIInterfaceInfo *iface_info,
PyGICallableCache *callable_cache)
{
gssize child_offset = 0;
if (!pygi_arg_base_setup ((PyGIArgCache *)arg_cache,
type_info,
arg_info,
transfer,
direction)) {
return FALSE;
}
if (direction & PYGI_DIRECTION_TO_PYTHON) {
((PyGIArgCache *)arg_cache)->to_py_marshaller = _pygi_marshal_to_py_interface_callback;
}
if (callable_cache != NULL)
child_offset =
(callable_cache->function_type == PYGI_FUNCTION_TYPE_METHOD ||
callable_cache->function_type == PYGI_FUNCTION_TYPE_VFUNC) ? 1: 0;
( (PyGIArgCache *)arg_cache)->destroy_notify = (GDestroyNotify)_callback_cache_free_func;
arg_cache->user_data_index = g_arg_info_get_closure (arg_info);
if (arg_cache->user_data_index != -1)
arg_cache->user_data_index += child_offset;
arg_cache->destroy_notify_index = g_arg_info_get_destroy (arg_info);
if (arg_cache->destroy_notify_index != -1)
arg_cache->destroy_notify_index += child_offset;
arg_cache->scope = g_arg_info_get_scope (arg_info);
g_base_info_ref( (GIBaseInfo *)iface_info);
arg_cache->interface_info = iface_info;
if (direction & PYGI_DIRECTION_FROM_PYTHON)
_arg_cache_from_py_interface_callback_setup ((PyGIArgCache *)arg_cache, callable_cache);
return TRUE;
}
PyGIArgCache *
pygi_arg_callback_new_from_info (GITypeInfo *type_info,
GIArgInfo *arg_info, /* may be null */
GITransfer transfer,
PyGIDirection direction,
GIInterfaceInfo *iface_info,
PyGICallableCache *callable_cache)
{
gboolean res = FALSE;
PyGICallbackCache *callback_cache;
callback_cache = g_slice_new0 (PyGICallbackCache);
if (callback_cache == NULL)
return NULL;
res = pygi_arg_callback_setup_from_info (callback_cache,
type_info,
arg_info,
transfer,
direction,
iface_info,
callable_cache);
if (res) {
return (PyGIArgCache *)callback_cache;
} else {
_pygi_arg_cache_free ((PyGIArgCache *)callback_cache);
return NULL;
}
}
...@@ -12,9 +12,7 @@ ...@@ -12,9 +12,7 @@
* Lesser General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software * License along with this library; if not, see <http://www.gnu.org/licenses/>.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/ */
#ifndef __PYGI_CLOSURE_H__ #ifndef __PYGI_CLOSURE_H__
...@@ -24,6 +22,8 @@ ...@@ -24,6 +22,8 @@
#include <girffi.h> #include <girffi.h>
#include <ffi.h> #include <ffi.h>
#include "pygi-cache.h"
G_BEGIN_DECLS G_BEGIN_DECLS
...@@ -52,6 +52,13 @@ PyGICClosure* _pygi_make_native_closure (GICallableInfo* info, ...@@ -52,6 +52,13 @@ PyGICClosure* _pygi_make_native_closure (GICallableInfo* info,
PyObject *function, PyObject *function,
gpointer user_data); gpointer user_data);
PyGIArgCache *pygi_arg_callback_new_from_info (GITypeInfo *type_info,
GIArgInfo *arg_info, /* may be null */
GITransfer transfer,
PyGIDirection direction,
GIInterfaceInfo *iface_info,
PyGICallableCache *callable_cache);
G_END_DECLS G_END_DECLS
#endif /* __PYGI_CLOSURE_H__ */ #endif /* __PYGI_CLOSURE_H__ */
...@@ -233,20 +233,6 @@ _pygi_marshal_cleanup_to_py_interface_object (PyGIInvokeState *state, ...@@ -233,20 +233,6 @@ _pygi_marshal_cleanup_to_py_interface_object (PyGIInvokeState *state,
g_object_unref (G_OBJECT(data)); g_object_unref (G_OBJECT(data));
} }
void
_pygi_marshal_cleanup_from_py_interface_callback (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
PyObject *py_arg,
gpointer data,
gboolean was_processed)
{
PyGICallbackCache *callback_cache = (PyGICallbackCache *)arg_cache;
if (was_processed && callback_cache->scope == GI_SCOPE_TYPE_CALL) {
_pygi_invoke_closure_free (data);
}
}
void void
_pygi_marshal_cleanup_from_py_interface_struct_gvalue (PyGIInvokeState *state, _pygi_marshal_cleanup_from_py_interface_struct_gvalue (PyGIInvokeState *state,
PyGIArgCache *arg_cache, PyGIArgCache *arg_cache,
......
...@@ -65,11 +65,6 @@ void _pygi_marshal_cleanup_to_py_interface_object (PyGIInvokeState *stat ...@@ -65,11 +65,6 @@ void _pygi_marshal_cleanup_to_py_interface_object (PyGIInvokeState *stat
PyObject *dummy, PyObject *dummy,
gpointer data, gpointer data,
gboolean was_processed); gboolean was_processed);
void _pygi_marshal_cleanup_from_py_interface_callback (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
PyObject *py_arg,
gpointer data,
gboolean was_processed);
G_END_DECLS G_END_DECLS
#endif /* __PYGI_MARSHAL_CLEANUP_H__ */ #endif /* __PYGI_MARSHAL_CLEANUP_H__ */
...@@ -140,166 +140,6 @@ _is_union_member (GIInterfaceInfo *interface_info, PyObject *py_arg) { ...@@ -140,166 +140,6 @@ _is_union_member (GIInterfaceInfo *interface_info, PyObject *py_arg) {
return is_member; return is_member;
} }
/* _pygi_destroy_notify_dummy:
*
* Dummy method used in the occasion when a method has a GDestroyNotify