-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathfiber.lua
More file actions
169 lines (151 loc) · 3.39 KB
/
fiber.lua
File metadata and controls
169 lines (151 loc) · 3.39 KB
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
--- a wrapper around coroutines
-- @classmod fiber
local class = require "class"
local unpack = table.unpack or unpack
local fiber = class "Fiber"
fiber.current = nil
fiber.yield = coroutine.yield
--- create a fiber
-- @function fiber:new
-- @tparam function func
function fiber:init(func)
self.co = coroutine.create(func)
self.time = 0
self.defers = {}
end
--- resume a fiber,
-- @param ... arguments passed to the coroutine
-- @treturn ?nil values returned by the coroutine or nil on timeout
function fiber:resume(...)
if self.timeout then
if self.time > self.timeout then
self:close()
return
end
end
fiber.current = self
local start = os.clock()
local res = { coroutine.resume(self.co, ...) }
local finish = os.clock()
self.time = self.time + finish - start
fiber.current = nil
return unpack(res)
end
--- get the status of a fiber's coroutine
-- @return string
function fiber:status()
return coroutine.status(self.co)
end
--- close a fiber
function fiber:close()
for _, d in ipairs(self.defers) do
local ok, err = pcall(d.func, unpack(d.args))
if not ok then
io.stderr:write(err .. "\n")
end
end
if coroutine.close then
coroutine.close(self.co)
end
end
--- pause the current fiber for a given time
-- @number sec the time to sleep in seconds
function fiber.sleep(sec)
local res
local start = os.clock()
while os.clock() - start < sec do
res = { fiber.yield() }
end
return unpack(res)
end
--- keep yielding until func returns a positive value
-- @tparam function func
-- @param ... arguments passed to func
-- @return the result of func
function fiber.wait(func, ...)
while true do
local res = { func(...) }
if res[1] then
return unpack(res)
end
fiber.yield()
end
end
--- keep yielding until func returns a positive value or the timeout is reached
-- @number time number of seconds to wait for--
-- @tparam function func
-- @return the result of func
function fiber.wait_for(time, func, ...)
local start = os.clock()
while true do
if os.clock() - start > time then
return
end
local res = { func(...) }
if res[1] then
return unpack(res)
end
fiber.yield()
end
end
local fibers = {}
--- spawn a new fiber
-- @tparam function func
-- @param ...
function fiber.spawn(func, ...)
local t = fiber:new(func)
local ok, err = t:resume(...)
if not ok then
io.stderr:write(err, "\n")
end
if t:status() == "dead" then
t:close()
else
table.insert(fibers, t)
end
return t
end
local stop = false
--- start the async loop
-- @tparam table t
function fiber.loop(t)
for _, v in pairs(t) do
table.insert(fibers, v)
end
while not stop and #fibers > 0 do
for i=#fibers,1,-1 do
local t = fibers[i]
local ok, err = t:resume()
if not ok then
io.stderr:write(err, "\n")
end
if t:status() == "dead" then
t:close()
table.remove(fibers, i)
end
if stop then break end
end
end
end
--- exit the async loop
function fiber.exit()
stop = true
fiber.yield()
end
--- return current fiber count
function fiber.count()
return #fibers
end
--- defer a function to be executed when the fiber quits
-- (possibly due to an error)
-- @tparam function func
-- @param ...
function fiber.defer(func, ...)
table.insert(
fiber.current.defers, 1, {
func = func,
args = { ... }
}
)
end
return fiber