2014-12-22 31 views
1

我想包裝C計時器(而不是警報),並在lua中使用它,以一種方式,我可以指定一個回調函數在一秒鐘後觸發。 爲了使用多個定時器,一個定時器ID和回調將被存儲到一個表 ,但是當調用'lua_rawset'時發生了分段錯誤,所以我使用stack_dump 檢查lua堆棧,nil被'lua_rawget '在線66(lr_register_timer, ,由FIXME標記),這裏有什麼問題?對不起,我的英文很差。 乾杯。使用lua的lightuserdata來註冊定時器回調

Lua代碼:

local lt = luatimer 

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

C代碼:

#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

我的猜測是,多個'lua_State's正在創建,你的電話在某種程度上混合了狀態。在'lr_register_timer'函數中做一個斷言,例如。 'assert(:: L == L);' – greatwolf

+0

可能因爲您使用的是不同的lightuserdata值。 &L在調用不同函數(或者甚至是相同的函數)之間不保證是相同的。 – immibis

+0

非常感謝,我犯了一個愚蠢的錯誤,我在本地變量和全局變量中使用了相同的名稱'L'。對不起 – uudiin

回答

0

非常感謝你,我讓我在本地變量和全局變量使用相同的名稱L一個愚蠢的錯誤。我很抱歉 感謝greatwold和immibis