lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Creating a solution with setjmp/longjmp was not too hard (code: see below).
But I wonder, what could it really break?

It seems it does not leave a memory leak after lua_close (tested with clang address sanitizer), I suppose all user defined garbage collecting functions are called as well.

For a C library function using lua_pcall or lua_yield, I do not violate any valid expectation. A Lua library function calling lua_pcall cannot be sure the lua_pcall function will return, since the Lua code could also switch to another coroutine. For lua_yield, it is even stated in the manual “only use as a tail call”. Unless I have some unknown error in this reasoning, I do not introduce any additional problem.

For a C library using lua_pcallk or lua_yieldk, I was not sure at first – but I found there is no guarantee that a continuation function will ever be called. So, this should not cause any new problem as well.

Did I miss something?


(In case someone is interested, this is how I realized the "my_exit" function, plus some tests, but without complete error handling.)

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include <setjmp.h>
#include <stdio.h>
#include <assert.h>

jmp_buf JUMP_TO_EXIT;

int my_exit(lua_State *L)
{
  int num = lua_tointeger(L, 1);
  lua_close(L);
  longjmp(JUMP_TO_EXIT, num);
  return 0; /* will not return */
}

int fail_continuation_function(lua_State *L, int status, lua_KContext ctx) {
  assert(0); /* will never be called */
}

int my_pcall(lua_State *L)
{
  lua_pcallk(L, lua_gettop(L)-1, 0, 0, 0, fail_continuation_function);
  return 0;
}

int num_from_lua(const char *code)
{
  lua_State *L = luaL_newstate();  /* create state */
  luaL_openlibs(L);
  lua_pushcfunction(L, my_exit);
  lua_setglobal(L, "my_exit");
  lua_pushcfunction(L, my_pcall);
  lua_setglobal(L, "my_pcall");

  luaL_loadstring(L, code);
  int result = setjmp(JUMP_TO_EXIT);
  if (result) {
      return result;
  }
  int status = lua_pcall(L, 0, 1, 0);
  if (status) {
    printf("%s\n", lua_tostring(L, 1));
  } else {
    result = lua_tonumber(L, -1);
  }
  lua_close(L);
  return result;
}

int main()
{
  int a = num_from_lua("pcall(function () pcall(my_exit, 123) end); return 45;");
  assert(a == 123);

  a = num_from_lua("co = coroutine.create(function () pcall(my_exit, 67) end); coroutine.resume(co); return 89;");
  assert(a == 67);

  a = num_from_lua("co = coroutine.create(function () my_pcall(my_exit, 98) end); coroutine.resume(co); return 76;");
  assert(a == 98);

  a = num_from_lua("co = coroutine.create(function () my_pcall(coroutine.yield) end); coroutine.resume(co); return 54;");
  assert(a == 54);

  return 0;
}




On Fri, Feb 26, 2021 at 10:43 AM Viacheslav Usov <via.usov@gmail.com> wrote:
On Thu, Feb 25, 2021 at 9:58 PM bel <bel2125@gmail.com> wrote:

> Is there some recommended method to do this?

Unless you control tightly all the "C" [1] code, including all the
libraries, that interacts with a given state, that is generally
impossible. Because "C" code will generally expect that no transfer of
control can happen across lua_pcall (and the like) boundaries.
Whatever you do, you will either have to violate the expectation, with
generally unpredictable consequences, or have to be limited by it.

[1] "C" meaning whatever uses the Lua "C" API, which is not
necessarily in the C language.

Cheers,
V.