summaryrefslogtreecommitdiffstats
path: root/player/lua.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2014-10-19 00:09:27 +0200
committerwm4 <wm4@nowhere>2014-10-19 05:51:25 +0200
commitdaab65693c4994ea350f6dcfcf43653c57175ea5 (patch)
tree8cff6b2fd83ab6a57dd1aa400ba6651750ce52b2 /player/lua.c
parent76af31b0eb51d50f7dc17db3fdb3cc3e40fac187 (diff)
downloadmpv-daab65693c4994ea350f6dcfcf43653c57175ea5.tar.bz2
mpv-daab65693c4994ea350f6dcfcf43653c57175ea5.tar.xz
lua: add a helper to auto-free temporary C memory
Using the Lua API is a big PITA because it uses longjmp() error handling. That is, a Lua API function could any time raise an error and longjmp() to a lower part of the stack. This kind of "exception handling" is completely foreign to C, and there are no proper ways to clean up the "skipped" stack frames. Other than avoiding such situations entirely, the only way to deal with this is using Lua "userdata", which is basically a malloc'ed data block managed by the Lua GC, and which can have a destructor function associated (__gc metamethod). This requires an awful lot of code (because the Lua API is just so terrible), so I avoided this utnil now. But it looks like this will make some of the following commits much easier, so here we go.
Diffstat (limited to 'player/lua.c')
-rw-r--r--player/lua.c38
1 files changed, 32 insertions, 6 deletions
diff --git a/player/lua.c b/player/lua.c
index 73b5a2991c..30e00a625d 100644
--- a/player/lua.c
+++ b/player/lua.c
@@ -90,6 +90,34 @@ static int mp_cpcall (lua_State *L, lua_CFunction func, void *ud)
}
#endif
+static int destroy_crap(lua_State *L)
+{
+ void **data = luaL_checkudata(L, 1, "ohthispain");
+ talloc_free(data[0]);
+ data[0] = NULL;
+ return 0;
+}
+
+// Creates a small userdata object and pushes it to the Lua stack. The function
+// will (on the C level) return a talloc object that will be released by the
+// userdata gc routine.
+// This can be used to free temporary C data structures correctly if Lua errors
+// happen.
+// You can't free the talloc context directly; the Lua __gc handler does this.
+static void *mp_lua_PITA(lua_State *L)
+{
+ void **data = lua_newuserdata(L, sizeof(void *)); // u
+ if (luaL_newmetatable(L, "ohthispain")) { // u metatable
+ lua_pushvalue(L, -1); // u metatable metatable
+ lua_setfield(L, -2, "__index"); // u metatable
+ lua_pushcfunction(L, destroy_crap); // u metatable gc
+ lua_setfield(L, -2, "__gc"); // u metatable
+ }
+ lua_setmetatable(L, -2); // u
+ *data = talloc_new(NULL);
+ return *data;
+}
+
static struct script_ctx *get_ctx(lua_State *L)
{
lua_getfield(L, LUA_REGISTRYINDEX, "ctx");
@@ -693,7 +721,6 @@ static void makenode(void *tmp, mpv_node *dst, lua_State *L, int t)
MP_TARRAY_GROW(tmp, list->keys, list->num);
makenode(tmp, &list->values[list->num], L, -1);
if (lua_type(L, -2) != LUA_TSTRING) {
- talloc_free(tmp);
luaL_error(L, "key must be a string, but got %s",
lua_typename(L, -2));
}
@@ -706,7 +733,6 @@ static void makenode(void *tmp, mpv_node *dst, lua_State *L, int t)
}
default:
// unknown type
- talloc_free(tmp);
luaL_error(L, "disallowed Lua type found: %s\n", lua_typename(L, t));
}
}
@@ -716,10 +742,10 @@ static int script_set_property_native(lua_State *L)
struct script_ctx *ctx = get_ctx(L);
const char *p = luaL_checkstring(L, 1);
struct mpv_node node;
- void *tmp = talloc_new(NULL);
+ void *tmp = mp_lua_PITA(L);
makenode(tmp, &node, L, 2);
int res = mpv_set_property(ctx->client, p, MPV_FORMAT_NODE, &node);
- talloc_free(tmp);
+ talloc_free_children(tmp);
return check_error(L, res);
}
@@ -896,10 +922,10 @@ static int script_command_native(lua_State *L)
struct script_ctx *ctx = get_ctx(L);
struct mpv_node node;
struct mpv_node result;
- void *tmp = talloc_new(NULL);
+ void *tmp = mp_lua_PITA(L);
makenode(tmp, &node, L, 1);
int err = mpv_command_node(ctx->client, &node, &result);
- talloc_free(tmp);
+ talloc_free_children(tmp);
const char *errstr = mpv_error_string(err);
if (err >= 0) {
bool ok = pushnode(L, &result, 50);