/*
* This file is part of LEM, a Lua Event Machine.
* Copyright 2011 Emil Renner Berthing
*
* LEM is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* LEM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LEM. If not, see .
*/
#include
#include
static int
sleeper_wakeup(lua_State *T)
{
struct ev_timer *w;
lua_State *S;
int nargs;
luaL_checktype(T, 1, LUA_TUSERDATA);
w = lua_touserdata(T, 1);
S = w->data;
if (S == NULL) {
lua_pushnil(T);
lua_pushliteral(T, "not sleeping");
return 2;
}
ev_timer_stop(EV_G_ w);
nargs = lua_gettop(T) - 1;
lua_settop(S, 0);
lua_xmove(T, S, nargs);
lem_queue(S, nargs);
w->data = NULL;
/* return true */
lua_pushboolean(T, 1);
return 1;
}
static void
sleep_handler(EV_P_ struct ev_timer *w, int revents)
{
lua_State *T = w->data;
(void)revents;
/* return nil, "timeout" */
lem_queue(T, 2);
w->data = NULL;
}
static int
sleeper_sleep(lua_State *T)
{
struct ev_timer *w;
luaL_checktype(T, 1, LUA_TUSERDATA);
w = lua_touserdata(T, 1);
if (w->data != NULL) {
lua_pushnil(T);
lua_pushliteral(T, "busy");
return 2;
}
if (lua_gettop(T) > 1) {
ev_tstamp delay = (ev_tstamp)luaL_checknumber(T, 2);
if (delay <= 0) {
/* return nil, "timeout"
* ..but yield the thread */
lua_pushnil(T);
lua_pushvalue(T, lua_upvalueindex(1));
lem_queue(T, 2);
return lua_yield(T, 2);
}
ev_timer_set(w, delay, 0);
ev_timer_start(EV_G_ w);
}
w->data = T;
/* yield sleeper, nil, "timeout" */
lua_settop(T, 1);
lua_pushnil(T);
lua_pushvalue(T, lua_upvalueindex(1));
return lua_yield(T, 3);
}
static int
sleeper_new(lua_State *T)
{
struct ev_timer *w;
/* create new sleeper object and set metatable */
w = lua_newuserdata(T, sizeof(struct ev_timer));
lua_pushvalue(T, lua_upvalueindex(1));
lua_setmetatable(T, -2);
ev_init(w, sleep_handler);
w->data = NULL;
return 1;
}
static int
timer_cancel(lua_State *T)
{
struct ev_timer *w;
lua_State *S;
luaL_checktype(T, 1, LUA_TUSERDATA);
w = lua_touserdata(T, 1);
S = w->data;
if (S == NULL) {
lua_pushnil(T);
lua_pushliteral(T, "expired");
return 2;
}
ev_timer_stop(EV_G_ w);
lem_forgetthread(S);
/* return true */
lua_pushboolean(T, 1);
return 1;
}
static void
timer_handler(EV_P_ struct ev_timer *w, int revents)
{
lua_State *T = w->data;
(void)revents;
lua_settop(T, 1);
lem_queue(T, 0);
/* mark this timer as expired */
w->data = NULL;
}
static int
timer_new(lua_State *T)
{
ev_tstamp delay = (ev_tstamp)luaL_checknumber(T, 1);
struct ev_timer *w;
lua_State *S;
luaL_checktype(T, 2, LUA_TFUNCTION);
S = lem_newthread();
lua_settop(T, 2);
lua_xmove(T, S, 1);
/* create new timer object and set metatable */
w = lua_newuserdata(T, sizeof(struct ev_timer));
lua_pushvalue(T, lua_upvalueindex(1));
lua_setmetatable(T, -2);
if (delay > 0) {
w->data = S;
/* push a reference of w to S */
lua_pushvalue(T, -1);
lua_xmove(T, S, 1);
ev_timer_init(w, timer_handler, delay, 0);
ev_timer_start(EV_G_ w);
} else {
w->data = NULL;
lem_queue(S, 0);
}
return 1;
}
static int
spawn(lua_State *T)
{
lua_State *S;
int nargs;
luaL_checktype(T, 1, LUA_TFUNCTION);
S = lem_newthread();
nargs = lua_gettop(T);
lua_xmove(T, S, nargs);
lem_queue(S, nargs - 1);
lua_pushboolean(T, 1);
return 1;
}
static int
yield_lua(lua_State *T)
{
lem_queue(T, 0);
return lua_yield(T, 0);
}
static int
exit_lua(lua_State *T)
{
int status = (int)luaL_checknumber(T, 1);
lem_exit(status);
return 0;
}
int luaopen_lem_utils(lua_State *L)
{
/* create module table */
lua_newtable(L);
/* create new sleeper metatable */
lua_newtable(L);
/* mt.__index = mt */
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
/* mt.wakeup = */
lua_pushcfunction(L, sleeper_wakeup);
lua_setfield(L, -2, "wakeup");
/* mt.sleep = */
lua_pushliteral(L, "timeout"); /* upvalue 1: "timeout" */
lua_pushcclosure(L, sleeper_sleep, 1);
lua_setfield(L, -2, "sleep");
/* set sleeper function */
lua_pushcclosure(L, sleeper_new, 1);
lua_setfield(L, 2, "sleeper");
/* create new timer metatable */
lua_newtable(L);
/* mt.__index = mt */
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
/* mt.cancel = */
lua_pushcfunction(L, timer_cancel);
lua_setfield(L, -2, "cancel");
/* set timer function */
lua_pushcclosure(L, timer_new, 1);
lua_setfield(L, 2, "timer");
/* set spawn function */
lua_pushcfunction(L, spawn);
lua_setfield(L, 2, "spawn");
/* set yield function */
lua_pushcfunction(L, yield_lua);
lua_setfield(L, 2, "yield");
/* set exit function */
lua_pushcfunction(L, exit_lua);
lua_setfield(L, 2, "exit");
return 1;
}