-
Notifications
You must be signed in to change notification settings - Fork 62
/
Copy pathasync.lua
131 lines (114 loc) · 4.3 KB
/
async.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
---
-- Asynchronous callback shim.
--
-- @module radio.core.async
local ffi = require('ffi')
-- Lua C API
ffi.cdef[[
typedef double lua_Number;
typedef void lua_State;
typedef int (*lua_CFunction)(lua_State *L);
lua_State *luaL_newstate(void);
void lua_close(lua_State *L);
void luaL_openlibs(lua_State *L);
int luaL_loadbuffer(lua_State *L, const char *buff, size_t sz, const char *name);
const char *lua_tolstring(lua_State *L, int index, size_t *len);
enum { LUA_GLOBALSINDEX = -10002 };
void lua_getfield (lua_State *L, int index, const char *k);
void lua_setfield (lua_State *L, int index, const char *k);
void lua_pushnumber(lua_State *L, lua_Number n);
void lua_pushlstring(lua_State *L, const char *s, size_t len);
void lua_pushboolean(lua_State *L, int b);
void lua_pushnil(lua_State *L);
int lua_pcall(lua_State *L, int nargs, int nresults, int errfunc);
const void *lua_topointer(lua_State *L, int index);
void lua_settop(lua_State *L, int index);
]]
---
-- Wrap a callback for asynchronous calling. This function creates a callback
-- in a new Lua state and returns its function pointer, which can be safely
-- called asynchronously, e.g. from another thread or as a signal handler.
--
-- @function callback
-- @tparam function callback_factory Factory function that creates a callback
-- and return its function pointer
-- @param ... Arguments to factory function, can only be primitive types
-- number, string, boolean, and nil
-- @treturn pointer Function pointer of callback
-- @treturn pointer Lua state that owns callback
--
-- @usage
-- local function callback_factory(foo)
-- local ffi = require('ffi')
--
-- local function callback(sig)
-- print(foo, sig)
-- end
--
-- return ffi.cast('void (*)(int)', callback)
-- end
--
-- ffi.cdef[[
-- sighandler_t signal(int signum, void (*handler)(int));
-- unsigned int alarm(unsigned int seconds);
-- unsigned int sleep(unsigned int seconds);
-- ]]
--
-- local async = require('radio.core.async')
-- local callback, callback_state = radio.async.callback(callback_factory, 123)
-- ffi.C.signal(ffi.C.SIGALRM, callback)
--
-- ffi.C.alarm(5)
-- while true do
-- print('bar')
-- ffi.C.sleep(1)
-- end
local function callback(callback_factory, ...)
-- Create a new Lua state
local L = ffi.gc(ffi.C.luaL_newstate(), ffi.C.lua_close)
if L == nil then
error("luaL_newstate(): Allocating Lua state.")
end
-- Open standard libraries in the Lua state
ffi.C.luaL_openlibs(L)
-- Inherit parent environment's package.path
ffi.C.lua_getfield(L, ffi.C.LUA_GLOBALSINDEX, "package")
ffi.C.lua_pushlstring(L, package.path, #package.path)
ffi.C.lua_setfield(L, -2, "path")
ffi.C.lua_settop(L, -2)
-- Load the callback factory bytecode into the Lua state
local bytecode = string.dump(callback_factory)
if ffi.C.luaL_loadbuffer(L, bytecode, #bytecode, nil) ~= 0 then
error("luaL_loadbuffer(): " .. ffi.string(ffi.C.lua_tolstring(L, -1, nil)))
end
-- Push arguments into the Lua state
local args, num_args = {...}, select("#", ...)
for i = 1, num_args do
if type(args[i]) == "number" then
ffi.C.lua_pushnumber(L, args[i])
elseif type(args[i]) == "string" then
ffi.C.lua_pushlstring(L, args[i], #args[i])
elseif type(args[i]) == "boolean" then
ffi.C.lua_pushboolean(L, args[i])
elseif type(args[i]) == "nil" then
ffi.C.lua_pushnil(L)
else
error(string.format("Unsupported argument type \"%s\" (index %d).", type(args[i]), i))
end
end
-- Call the callback factory inside the Lua state
if ffi.C.lua_pcall(L, num_args, 1, 0) ~= 0 then
error("lua_pcall(): " .. ffi.string(ffi.C.lua_tolstring(L, -1, nil)))
end
-- Pop the returned callback function pointer
local callback_ptrptr = ffi.C.lua_topointer(L, -1)
if callback_ptrptr == nil then
error("Callback factory did not return a function pointer.")
end
local callback_ptr = ffi.cast('void **', callback_ptrptr)[0]
-- Clear the Lua state stack
ffi.C.lua_settop(L, -1)
-- Return the callback function pointer and Lua state
return callback_ptr, L
end
return {callback = callback}