5
votes

I have a main program (in C) which needs to branch out into lua_thread(the main continues to run).This lua_thread calls a lua_script.lua. this lua_script contains a while loop. a lua variable controls this while loop.Currently this loop runs forever.

lua_script.lua

  --this loop runs forever, as the exit value is not set yet
  a=0
    while(a<=0)
    do
       print("value of a:", a)
    end

My goal is to change this lua variable(a) from main program such that it exits this infinite loop. Once this loop ends, it exits the thread and returns to the main program.

main.c

#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
void *lua_thread()
 {
    int status, result;
    double sum;
    lua_State *L;

    L = luaL_newstate();
    luaL_openlibs(L); 

    status = luaL_loadfile(L, "lua_script.lua");
    if (status)
    {
        fprintf(stderr, "Couldn't load file: %s\n", lua_tostring(L, -1));
        exit(1);
    }

result = lua_pcall(L, 0, 0, 0);
    if (result) {
        fprintf(stderr, "Failed to run script: %s\n", lua_tostring(L, -1));
        exit(1);
    }

    lua_close(L);   
    return 0;
}

int main(void)
{    
    pthread_t p1;
    pthread_create(&p1,NULL,lua_thread,NULL);
    pthread_join(p1,NULL);    
    return 0;
}

If you run the above code

cc -o xcute main.c  -I/usr/include/lua5.2 -llua -lm -ldl -pthread

it will go into an infinite loop. I want to somehow control the lua variable and change it to a=1,from the main program so that it comes out of the infinite loop. the reason for doing such a test is that it will make sure that before the main program exits, this thread exits first by controlling the lua variable. Please suggest how to change this lua variable so that it exits the while loop.

4
AFAIK Lua is not thread-safe - that is, each lua_State can only be used from one thread at a time.user253751

4 Answers

5
votes

Interacting with a running lua state from a different thread is not necessarily safe so modifying the script's global variable may or may not be a useful idea depending on where you are planning to be making that change from the C side.

If you wanted to do this you would simply need to use the lua C api to set the global variable of the appropriate name in the appropriate lua state.

An alternate idea would be to create a should_exit global function which is called at the start or end of every loop and when it returns true causes the lua code to break or return. This function can then check anything it wants to on the C side in whatever thread-appropriate manner is desired.

1
votes

Why to have this loop in Lua? You may loop in c-thread instead, lua_pcalling some entry-point function (e.g. onEvent()) on each iteration.

If loop has to be in Lua script, for example in case of setup-loop-cleanup scheme, you may run script in coroutine and use coroutine.yield() as loop condition. Thread should lua_resume() with true value or exit depending on your c-side condition. (Or resume with false if Lua-side cleanup after the loop is preferred.)

Anyway, Lua is not thread-safe and cannot be called simultaneously from more than one thread.

1
votes

Oh, you went hard way in your answer (sure that was a great exercise though). Things could be much simpler:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

static volatile int shouldRun = 1; // 1.

static int
fn_sleep(lua_State *L)
{
    lua_Integer n = luaL_checkinteger(L, 1);
    sleep(n < 0 ? 0 : n);
    return 0;
}

static int
fn_shouldRun(lua_State *L)
{
    lua_pushboolean(L, shouldRun);
    return 1;
}

static void *
thread_main(void *dummy)
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    lua_register(L, "sleep", fn_sleep); // 2.
    lua_register(L, "shouldRun", fn_shouldRun);

    if (luaL_dofile(L, "script.lua")) {
        fprintf(stderr, "%s\n", lua_tostring(L, -1));
        lua_pop(L, 1);
    }

    lua_close(L); // 3.
    return 0;
}

int
main(int argc, char *argv[])
{
    pthread_t thread;
    pthread_create(&thread, NULL, thread_main, NULL);

    sleep(5);
    shouldRun = 0; // 1.

    pthread_join(thread, NULL);
    return 0;
}

script.lua:

print("Hi!")

while shouldRun() do
    print("Running!")
    sleep(1)
end

print("Bye!")

output:

Hi!
Running!
Running!
Running!
Running!
Running!
Bye!

Few things to note:

  1. Polled exit conditions usually do not need any protection. If shouldRun() was called simultaneously with variable update and missed it, then nothing to worry about – it will return false next time. Anyway, you can't even guess where the script is executing, just [re]set the value and wait for completion.
  2. For a pair of specific functions there is no need to write a require/preload-compatible module. Export them into global namespace instead.
  3. For graceful close on error do not just exit(). lua_close() will garbage-collect all userdata used by script, and their __gc metamethod may do something useful like flushing buffers to disk, etc. You lose all chances with hard exit.
0
votes

It took me two days to come up with this. Hopefully,it will help someone to save sometime.

main.c

#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <assert.h>
#include <fcntl.h>
#include <semaphore.h>

void *lua_thread(void *arg)
 {   
    int status, result, i;
    double sum;
    lua_State *L=(lua_State *)arg;

    int a=0;
    status = luaL_dofile(L, "lua_script.lua");
    if (status) {
        fprintf(stderr, "Couldn't load file: %s\n", lua_tostring(L, -1));
        exit(1);
    }

    printf("Lua thread exiting\n");   
    return 0;
}

int main(void)
{
    lua_State *L;
    L = luaL_newstate();
    luaL_openlibs(L);

    //Open semaphore which will signal Lua loop to exit
    sem_t *lex=sem_open("luaexitsem", O_CREAT, 0600, 0);

    //Make sure value of the semaphore is 0 before we start running
    //so that sem_post sets it to 1 later
      int retval;
      for(retval=0;retval==0;) {
          retval=sem_trywait(lex);
          printf("Dec sem val: %d\n", retval);
      }

    //Start Lua thread
    pthread_t p1;
    pthread_create(&p1,NULL,lua_thread,L);

    sleep(5);

    //Signal Lua script to exit
    sem_post(lex);

    //Wait for Lua thread to exit
    pthread_join(p1,NULL);

    //Cleanup
    printf("Main exiting\n");
    lua_close(L);
    sem_close(lex);
    return 0;
}

To compile

gcc -o main main.c -I/usr/include/lua5.1 -llua5.1 -lm -ldl -pthread

lua_script.lua

    require "lualinuxthread"
    a=1
    while(not linuxthread.signaltoexit())
    do
      print("value of a:", a)
      a=a+1
    end

lualinuxthread.c

#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include <semaphore.h>
#include <errno.h>
#include <fcntl.h>

static int signaltoexit(lua_State *L) {

  sem_t *lex=sem_open("luaexitsem", O_CREAT, 0600, 0);

  int exitvalue=0, retval;

  if (lex!=SEM_FAILED)
    retval=sem_trywait(lex);

  if (retval==-1) exitvalue=0; else exitvalue=1;

  printf("signaltoexit - exitvalue: %d, retval: %d,  %x\n", exitvalue, retval, lex);

  lua_pushboolean(L, exitvalue);

  sem_close(lex);
  return 1;
}

static const luaL_Reg libfuncs[] = {
{"signaltoexit",   signaltoexit},
{NULL, NULL}
};

LUALIB_API int luaopen_lualinuxthread (lua_State *L) {

  luaL_register(L, "linuxthread", libfuncs);
  return 1;
}

to compile:

gcc -O -O2 -fpic -shared lualinuxthread.c -o lualinuxthread.so -lpthread -llua5.1

Thank you