Multithreaded C Lua module leading to segfault in Lua script Multithreaded C Lua module leading to segfault in Lua script multithreading multithreading

Multithreaded C Lua module leading to segfault in Lua script


Why does the Segfault Happen in the Lua Module?

Your Lua script exits before the thread has finished which causes the segfault. The Lua module is unloaded using dlclose() during the normal interpreter shutdown, and so the thread's instructions are removed from memory, and it segfaults on reading its next instruction.

What are the options?

Any solution which stops the threads before the module is unloaded will work. Using pthread_join() in the main thread will wait for the threads to finish (you may want to kill long-running threads using pthread_cancel()). Calling pthread_exit() in the main thread before the module is unloaded will also prevent the crash (because it will prevent the dlclose()), but it also aborts the normal cleanup/shutdown procedure of the Lua interpreter.

Here are some examples that work:

int pexit(lua_State* L) {   pthread_exit(NULL);   return 0; } int join(lua_State* L){  pthread_join(handle, NULL);  return 0;}static const luaL_Reg testlib[] = {    {"start_mythread", start_mythread_lua},    {"join", join},    {"exit", pexit},    {NULL, NULL}};void* mythread(void* args) {  int i, j, k;    printf("In the thread !\n");    for (i = 0; i < 10000; ++i) {      for (j = 0; j < 10000; ++j) {        for (k = 0; k < 10; ++k) {          pow(1, i);        }      }    }    pthread_exit(NULL);}

Now the script will exit nicely:

require('test')test.start_mythread()print("launched thread")test.join() -- or test.exit()print("thread joined")

To automate this, you can tie into the garbage collector since all the objects in module are freed before the unloading of the shared object. (as greatwolf suggested)

Discussion on calling pthread_exit() from main(): There is a definite problem if main() finishes before the threads it spawned if you don't call pthread_exit() explicitly. All of the threads it created will terminate because main() is done and no longer exists to support the threads. By having main() explicitly call pthread_exit() as the last thing it does, main() will block and be kept alive to support the threads it created until they are done.

(This quote is a bit misleading: Returning from main() is roughly equivalent to calling exit(), which will quit the process including all running threads. This may or may not be exactly the behavior you want. Calling pthread_exit() in the main thread on the other hand will quit the main thread but keep all other threads running until they stop on their own or somebody else kills them. Again, this may or may not be the behavior you want. There is no problem unless you choose the wrong option for your use case.)


So, it seems I do have to make sure all my threads have finished by the time Lua unloads my lib.

A Solution

I can set a cleanup function to be called when the library is unloaded.
Within this function, I can make sure that all threads that my lib started have terminated. Calling pthread_exit from it could be easy if I have detached threads that are still running, but I'm not sure about how safe/clean it is, since it will abruptly interrupt Lua...
Anyway, I can achieve this by creating a metatable with a __gc field set to my cleanup function, and then affect this metatable to my lib's table in Lua 5.2.

int cleanup(lua_State* L){    /*Do the cleaning*/    return 0;}int luaopen_test(lua_State* L){    //for lua 5.2    //metatable with cleanup method for the lib    luaL_newmetatable(L, "test.cleanup");    //set our cleanup method as the __gc callback    lua_pushstring(L, "__gc");    lua_pushcfunction(L, cleanup);    lua_settable(L, -3);    //open our test lib    luaL_newlib(L, testlib);    //associate it with our metatable    luaL_setmetatable(L, "test.cleanup");    return 1;}

In Lua 5.1, the __gc option works only for userdata. There are several solutions to get it to work in my case :
- Lua shutdown/End of the program execution callback
- http://lua-users.org/wiki/LuaFaq (see 'Why doesn't the __gc and __len metamethods work on tables?')
- Greatwolf's solution of having a global object with said metatable attached.