Thursday, May 19, 2016

Brief note on LuaWrapper and unexpected crashes in destructor & destructor ordering

PowerDNS products rely heavily on Lua, and we mostly use the most excellent LuaWrapper to seamlessly share data and code between Lua and C++. LuaWrapper is such a fundamental part of our products that we took over maintenance of LuaWrapper when the genius original author Pierre Krieger moved on to other things.

Yesterday we traced a weird crash when we fixed a memory leak in the PowerDNS Recursor, and I think I got lucky in finding the cause quite quickly. This could have taken days. My usual technique of searching the web for other people with similar crashes failed.

So this post is here to create something for your search engine to find. If you destroy the LuaWrapper object and afterwards see crashes in lua_pushlightuserdata(), what is going on is that any objects you copied from Lua are being destroyed *after* the LuaWrapper instance itself got destroyed.

This means that those objects are trying to deregister themselves with a Lua instance.. that is no longer there.

If you have a struct like this:

struct Somestuff
{
   typedef std::function < bool(std::shared_ptr < DNSQuestion > ) > luacall_t; 
   lfunc_t d_preresolve;
   LuaContext d_lw;
};
And within your code you did:

d_preresolve = d_lw.readVariable("preresolve");

You will get a crash because when your Somestuff instance gets detroyed, destructors run in reverse order from bottom to top. So d_lw is gone, and only THEN does preresolve get destroyed, at which point it tries to deregister with a Lua that is no longer there.

This is all trivially resolved by putting LuaContext at the very top of your object, and everything that depends on it below.

The C++ standard is explicit about the order in which destructors get called, so this is safe.

I hope this was helpful!