mirror of
https://gitlab.freedesktop.org/pipewire/wireplumber.git
synced 2026-05-22 23:18:13 +02:00
* First of all, do not clear the stack... it's not necessary to do so and it causes trouble if this function is called from within itself, as it clears the stack of the previous context * Then, do not stop/restart the garbage collector unless this is the base call Fixes #73
181 lines
4.6 KiB
C
181 lines
4.6 KiB
C
/* 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);
|
|
}
|