sol2: stack is not properly unwound on lua errors

Take this example code:

#include <sol.hpp>
#include <iostream>

struct Noisy
{
    Noisy() { std::cerr << "ctor" << std::endl; }
    ~Noisy() { std::cerr << "dtor" << std::endl; }
};

int foo(sol::function fun)
{
    Noisy n;
    return fun();
}

int main(int argc, char** argv)
{
    sol::state lua;
    lua.open_libraries(sol::lib::base, sol::lib::package);
    lua.set_function("foo", &foo);
    try { lua.script("foo()"); }
    catch (const sol::error& e) { std::cerr << "Caught: " << e.what() << std::endl; }
    lua.script("print(foo(function() return 3 end))");
    return 0;
}

If I compile it with lua5.1 or 5.2, I get the following output:

ctor
Caught: lua: error: attempt to call a nil value
ctor
dtor
3

In the first case, it doesn’t run the destructor of the noisy class. Looks like lua longjmps out of the function, and only calls atpanic after this, thus throw won’t properly unwind the stack. Not sure if we can do anything about it, other than wrapping anything that can fail inside a ©pcall (but that would probably have disastrous consequences on performance…), or requiring a lua built with c++ and c++ exceptions instead of this setjmp/longjmp madness (but that would probably kill abi compatibility with existing lua libs).

By the way, this is what I get if I compile with luajit on x64 linux:

ctor
dtor
Caught: lua: error: caught (...) exception
ctor
dtor
3

Here at least the destructor runs correctly, but the error message disappears. (Calling fun will throw an exception that can be caught as (…) on the C++ side, if we run on a platform with full exception interoperability. On windows/x86, who knows what the hell will happen.)

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 15 (9 by maintainers)

Most upvoted comments

Changing the sol::function to sol::protected_function seems to fix the problem. I opened issue #65 yesterday having similar issues with sol::function.

Part of ThePhD’s response:

If you have a file or function you expect to error and crash, you should be using sol::protected_function to have error handling. As I write this comment, I changed the sol::function type to sol::protected_function in your example, and no horrible stack overflows or explosions are happening.

Right now, sol::function is presented as THE way to handle Lua functions in C++, but it’s very dangerous to use. I think the difference between sol::function and sol::protected_function isn’t immediately clear.

Personally, I would prefer sol::function being named sol::unprotected_function and sol::protected_function being named sol::function. The usage seems more clear to me here.

Of course, that isn’t necessarily the best route. Mentioning the difference between the function types in the tutorial might be just as good of a solution.