use lua's lightuserdata to register timer callback

288 views Asked by At

I would like to wrap the C timer (not alarm) and use it within lua, in a way that I could specify a callback function to be triggered after one second have passed. In order to use multiple timer, a timer ID and callback will be stored to a table, but a Segmentation fault occured when 'lua_rawset' was called, so I use stack_dump check the lua stack, a nil was returned by 'lua_rawget' on line 66(lr_register_timer, marked by FIXME), what is wrong here? Sorry, my English is poor. Cheers.

lua code:

local lt = luatimer

lt.register_timer(1, function(id)
        io.stdout:write("id" .. id .. "timeout\n");
    end)

C code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

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

#include "timer.h"

static lua_State *L;

static void stack_dump(lua_State *L, FILE *fp)
{
    int i;
    int top = lua_gettop(L);

    for (i = 1; i <= top; i++) {
        int t = lua_type(L, i);
        switch (t) {
        case LUA_TSTRING: fprintf(fp, "'%s'", lua_tostring(L, i)); break;
        case LUA_TBOOLEAN: fprintf(fp, lua_toboolean(L, i) ? "true" : "false"); break;
        case LUA_TNUMBER: fprintf(fp, "%g", lua_tonumber(L, i)); break;
        default: fprintf(fp, "%s", lua_typename(L, t)); break;
        }
        fprintf(fp, " ");
    }
    fprintf(fp, "\n");
}

struct timer_env {
    int id;
    struct event *ev;
};

static void callback_timer_wrap(int id, void *arg)
{
    struct timer_env *env = arg;

    /* get timer id table */
    lua_pushlightuserdata(L, &L);
    lua_rawget(L, LUA_REGISTRYINDEX);
    lua_pushinteger(L, env->id);
    lua_gettable(L, -2);

    /* call lua handler with one result */
    lua_pushinteger(L, env->id);
    if (lua_pcall(L, 1, 1, 0) == 0) {
        lua_pop(L, 1); /* pop result */
    }
}

/* id, callback */
static int lr_register_timer(lua_State *L)
{
    struct timer_env *env;
    int id;
    int err;

    id = (int)luaL_checkinteger(L, 1);
    if (!lua_isfunction(L, 2) || lua_iscfunction(L, 2))
        return 0;
    /* set lua handler */
    lua_pushlightuserdata(L, &L);
    lua_rawget(L, LUA_REGISTRYINDEX); /* FIXME */
    lua_pushvalue(L, 1); /* key: id */
    lua_pushvalue(L, 2); /* value: callback */

    stack_dump(L, stderr);
    /* FIXME  crashed */
    lua_rawset(L, -3);
    lua_pop(L, 1);

    env = malloc(sizeof(*env));
    memset(env, 0, sizeof(*env));
    env->id = id;
    if ((err = register_timer(id, callback_timer_wrap, env)) < 0)
        free(env);
    lua_pushinteger(L, err);
    return 1;
}

static const luaL_Reg luatimer_lib[] = {
    { "register_timer", lr_register_timer },
    { NULL, NULL }
};

static int luaopen_luatimer(lua_State *L)
{
    luaL_register(L, "luatimer", luatimer_lib);

    /* timer id table */
    lua_pushlightuserdata(L, &L); /* key */
    lua_newtable(L); /* value */
    lua_rawset(L, LUA_REGISTRYINDEX);

    return 1;
}

int luaenv_init(void)
{
    L = luaL_newstate();
    luaL_openlibs(L);

    lua_pushcfunction(L, luaopen_luatimer);
    lua_pushstring(L, "luatimer");
    lua_call(L, 1, 0);

    return 0;
}

void luaenv_exit(void)
{
    if (L)
        lua_close(L);
}
1

There are 1 answers

0
uudiin On

Thank you very much, I make a stupid mistake that I use the same name L in local vars and global vars. I'm Sorry Thanks greatwold and immibis