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
|
-- Exercise the Queue, WaitQueue, ErrorQueue family
Queue = require('Queue')
WaitQueue = require('WaitQueue')
ErrorQueue = require('ErrorQueue')
util = require('util')
inspect = require('inspect')
-- resume() wrapper to propagate errors
function resume(co, ...)
-- if there's an idiom other than table.pack() to assign an arbitrary
-- number of return values, I don't yet know it
local ok_result = table.pack(coroutine.resume(co, ...))
if not ok_result[1] then
-- if [1] is false, then [2] is the error message
error(ok_result[2])
end
-- ok is true, whew, just return the rest of the values
return table.unpack(ok_result, 2)
end
-- ------------------ Queue variables are instance-specific ------------------
q1 = Queue:new()
q2 = Queue:new()
q1:Enqueue(17)
assert(not q1:IsEmpty())
assert(q2:IsEmpty())
assert(q1:Dequeue() == 17)
assert(q1:Dequeue() == nil)
assert(q2:Dequeue() == nil)
-- ----------------------------- test WaitQueue ------------------------------
q1 = WaitQueue:new()
q2 = WaitQueue:new()
result = {}
values = { 1, 1, 2, 3, 5, 8, 13, 21 }
for i, value in pairs(values) do
q1:Enqueue(value)
end
-- close() while not empty tests that queue drains before reporting done
q1:close()
-- ensure that WaitQueue instance variables are in fact independent
assert(q2:IsEmpty())
-- consumer() coroutine to pull from the passed q until closed
function consumer(desc, q)
print(string.format('consumer(%s) start', desc))
local value = q:Dequeue()
while value ~= nil do
print(string.format('consumer(%s) got %q', desc, value))
table.insert(result, value)
value = q:Dequeue()
end
print(string.format('consumer(%s) done', desc))
end
-- run two consumers
coa = coroutine.create(consumer)
cob = coroutine.create(consumer)
-- Since consumer() doesn't yield while it can still retrieve values,
-- consumer(a) will dequeue all values from q1 and return when done.
resume(coa, 'a', q1)
-- consumer(b) will wake up to find the queue empty and closed.
resume(cob, 'b', q1)
coroutine.close(coa)
coroutine.close(cob)
print('values:', inspect(values))
print('result:', inspect(result))
assert(util.equal(values, result))
-- try incrementally enqueueing values
q3 = WaitQueue:new()
result = {}
values = { 'This', 'is', 'a', 'test', 'script' }
coros = {}
for _, name in {'a', 'b'} do
local coro = coroutine.create(consumer)
table.insert(coros, coro)
-- Resuming both coroutines should leave them both waiting for a queue item.
resume(coro, name, q3)
end
for _, s in pairs(values) do
print(string.format('Enqueue(%q)', s))
q3:Enqueue(s)
end
q3:close()
function joinall(coros)
local running
local errors = 0
repeat
running = false
for i, coro in pairs(coros) do
if coroutine.status(coro) == 'suspended' then
running = true
-- directly call coroutine.resume() instead of our resume()
-- wrapper because we explicitly check for errors here
local ok, message = coroutine.resume(coro)
if not ok then
print('*** ' .. message)
errors += 1
end
if coroutine.status(coro) == 'dead' then
coros[i] = nil
end
end
end
until not running
return errors
end
joinall(coros)
print(string.format('%q', table.concat(result, ' ')))
assert(util.equal(values, result))
-- ----------------------------- test ErrorQueue -----------------------------
q4 = ErrorQueue:new()
result = {}
values = { 'This', 'is', 'a', 'test', 'script' }
coros = {}
for _, name in {'a', 'b'} do
local coro = coroutine.create(consumer)
table.insert(coros, coro)
-- Resuming both coroutines should leave them both waiting for a queue item.
resume(coro, name, q4)
end
for i = 1, 4 do
print(string.format('Enqueue(%q)', values[i]))
q4:Enqueue(values[i])
end
q4:Error('something went wrong')
assert(joinall(coros) == 2)
|