-
Notifications
You must be signed in to change notification settings - Fork 55
/
ev-async.lua
77 lines (68 loc) · 2.93 KB
/
ev-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
-- WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! WARNING!!
--
-- Use this code at your own risk. If one Lua VM has a loop or async watcher
-- that goes out of scope and is used by a different Lua VM, your process
-- will crash and/or behave in unexpected ways.
--
-- WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! WARNING!! WARNING!!
--
-- Want something safer? Feel free to contribute!
--
-- One idea is to allocate all ev_loop and ev_async objects into a pool shared
-- shared across all uses of this library (aka a statically allocated array). This
-- pool would also contain reference counts for each object.
--
-- The userdata objects for loop and async watchers could then be changed to
-- reference the index into that pool.
--
-- We could then "serialize" an ev_loop or ev_async object by returning the index.
--
-- The "deserialize" would allocate an userdata that points to this index and
-- increment the reference count (assuming that an object of the correct type
-- exists at this index, otherwise throw an error).
--
-- Note that all allocations (and deallocations) of loop and async watchers would
-- require a mutex to ensure only one thread modifies this data at one time.
-- Contribution from https://github.com/ImagicTheCat
--
-- Extension for lua-ev and LuaJIT to trigger async watchers from other threads.
local ev = require("ev")
local ffi = require("ffi")
ffi.cdef[[
struct ev_loop;
struct ev_async;
void ev_async_send(struct ev_loop*, struct ev_async*);
]]
local libev = ffi.load("ev")
local loop_mt = getmetatable(ev.Loop.default)
local async_mt = getmetatable(ev.Async.new(function() end))
local async_sz_t = ffi.typeof("struct{struct ev_loop *loop; struct ev_async *async;}")
local function error_arg(index, expected)
error("bad argument #"..index.." ("..expected.." expected)")
end
local M = {}
-- Export serialized send for multi-threading signalization.
-- return string
function M.export(loop, async)
if getmetatable(loop) ~= loop_mt then error_arg(1, "loop") end
if getmetatable(async) ~= async_mt then error_arg(2, "async watcher") end
local loop_p = ffi.cast("struct ev_loop**", loop)[0]
local async_p = ffi.cast("struct ev_async*", async)
local async_sz = async_sz_t({loop = loop_p, async = async_p})
return ffi.string(async_sz, ffi.sizeof(async_sz_t))
end
-- Import serialized send for multi-threading signalization.
-- Return a function which will act as `ev_async_send` when called.
--
-- Calling the function beyond the lifespan of either the exported loop or async
-- object will result in undefined behavior (e.g. crash/corruption). The same applies
-- with an ill-formed string.
function M.import(data)
if type(data) ~= "string" or #data ~= ffi.sizeof(async_sz_t) then
error("invalid payload")
end
local async_sz = async_sz_t()
ffi.copy(async_sz, data, ffi.sizeof(async_sz_t))
return function() libev.ev_async_send(async_sz.loop, async_sz.async) end
end
return M