1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
|
/* WirePlumber
*
* Copyright © 2020 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include "wplua.h"
#include "private.h"
#include <wp/wp.h>
/* This structure is added to a lua global and it's only referenced from there;
When the lua_State closes, it is unrefed and its finalize function below
invalidates all the closures so that nothing attempts to call into a lua
function after the state is closed */
typedef struct _WpLuaClosureStore WpLuaClosureStore;
struct _WpLuaClosureStore
{
GPtrArray *closures;
};
static WpLuaClosureStore *
_wplua_closure_store_new (void)
{
WpLuaClosureStore *self = g_rc_box_new (WpLuaClosureStore);
self->closures = g_ptr_array_new ();
return self;
}
static void
_wplua_closure_store_finalize (WpLuaClosureStore * self)
{
for (guint i = self->closures->len; i > 0; i--) {
GClosure *c = g_ptr_array_index (self->closures, i-1);
g_closure_ref (c);
g_closure_invalidate (c);
g_ptr_array_remove_index_fast (self->closures, i-1);
g_closure_unref (c);
}
g_ptr_array_unref (self->closures);
}
static WpLuaClosureStore *
_wplua_closure_store_ref (WpLuaClosureStore * self)
{
return g_rc_box_acquire (self);
}
static void
_wplua_closure_store_unref (WpLuaClosureStore * self)
{
g_rc_box_release_full (self, (GDestroyNotify) _wplua_closure_store_finalize);
}
G_DEFINE_BOXED_TYPE(WpLuaClosureStore, _wplua_closure_store,
_wplua_closure_store_ref, _wplua_closure_store_unref)
typedef struct _WpLuaClosure WpLuaClosure;
struct _WpLuaClosure
{
GClosure closure;
int func_ref;
GPtrArray *closures;
};
static void
_wplua_closure_marshal (GClosure *closure, GValue *return_value,
guint n_param_values, const GValue *param_values,
gpointer invocation_hint, gpointer marshal_data)
{
static int reentrant = 0;
lua_State *L = closure->data;
int func_ref = ((WpLuaClosure *) closure)->func_ref;
/* invalid closure, skip it */
if (func_ref == LUA_NOREF || func_ref == LUA_REFNIL)
return;
/* stop the garbage collector */
if (reentrant == 0)
lua_gc (L, LUA_GCSTOP, 0);
/* push the function */
lua_rawgeti (L, LUA_REGISTRYINDEX, func_ref);
/* push arguments */
for (guint i = 0; i < n_param_values; i++)
wplua_gvalue_to_lua (L, ¶m_values[i]);
/* call in protected mode */
reentrant++;
int res = _wplua_pcall (L, n_param_values, return_value ? 1 : 0);
reentrant--;
/* handle the result */
if (res == LUA_OK && return_value) {
wplua_lua_to_gvalue (L, -1, return_value);
lua_pop (L, 1);
}
/* clean up */
lua_gc (L, LUA_GCCOLLECT, 0);
if (reentrant == 0)
lua_gc (L, LUA_GCRESTART, 0);
}
static void
_wplua_closure_invalidate (lua_State *L, WpLuaClosure *c)
{
wp_trace_boxed (G_TYPE_CLOSURE, c, "invalidated");
luaL_unref (L, LUA_REGISTRYINDEX, c->func_ref);
c->func_ref = LUA_NOREF;
}
static void
_wplua_closure_finalize (lua_State *L, WpLuaClosure *c)
{
g_ptr_array_remove_fast (c->closures, c);
g_ptr_array_unref (c->closures);
}
GClosure *
wplua_checkclosure (lua_State *L, int idx)
{
luaL_checktype (L, idx, LUA_TFUNCTION);
return wplua_function_to_closure (L, idx);
}
/**
* wplua_function_to_closure:
*
* Make a GClosure out of a Lua function at index @em idx
*
* Returns: (transfer floating): the new closure
*/
GClosure *
wplua_function_to_closure (lua_State *L, int idx)
{
g_return_val_if_fail (lua_isfunction(L, idx), NULL);
GClosure *c = g_closure_new_simple (sizeof (WpLuaClosure), L);
WpLuaClosure *wlc = (WpLuaClosure *) c;
WpLuaClosureStore *store;
lua_pushvalue (L, idx);
wlc->func_ref = luaL_ref (L, LUA_REGISTRYINDEX);
wp_trace_boxed (G_TYPE_CLOSURE, c, "created, func_ref = %d", wlc->func_ref);
g_closure_set_marshal (c, _wplua_closure_marshal);
g_closure_add_invalidate_notifier (c, L,
(GClosureNotify) _wplua_closure_invalidate);
g_closure_add_finalize_notifier (c, L,
(GClosureNotify) _wplua_closure_finalize);
/* keep a weak ref of the closure in the store's array,
so that we can invalidate the closure when lua_State closes;
keep a strong ref of the array in the closure so that
_wplua_closure_finalize() works even after the state is closed */
lua_pushliteral (L, "wplua_closures");
lua_gettable (L, LUA_REGISTRYINDEX);
store = wplua_toboxed (L, -1);
lua_pop (L, 1);
g_ptr_array_add (store->closures, c);
wlc->closures = g_ptr_array_ref (store->closures);
return c;
}
void
_wplua_init_closure (lua_State *L)
{
lua_pushliteral (L, "wplua_closures");
wplua_pushboxed (L,
_wplua_closure_store_get_type (),
_wplua_closure_store_new ());
lua_settable (L, LUA_REGISTRYINDEX);
}
|