aboutsummaryrefslogtreecommitdiff
path: root/src/lanes-keeper.lua
diff options
context:
space:
mode:
Diffstat (limited to 'src/lanes-keeper.lua')
-rw-r--r--src/lanes-keeper.lua302
1 files changed, 302 insertions, 0 deletions
diff --git a/src/lanes-keeper.lua b/src/lanes-keeper.lua
new file mode 100644
index 0000000..1f17599
--- /dev/null
+++ b/src/lanes-keeper.lua
@@ -0,0 +1,302 @@
1--
2-- KEEPER.LUA
3--
4-- Keeper state logic
5--
6-- This code is read in for each "keeper state", which are the hidden, inter-
7-- mediate data stores used by Lanes inter-state communication objects.
8--
9-- Author: Asko Kauppi <akauppi@gmail.com>
10--
11--[[
12===============================================================================
13
14Copyright (C) 2008-10 Asko Kauppi <akauppi@gmail.com>
15
16Permission is hereby granted, free of charge, to any person obtaining a copy
17of this software and associated documentation files (the "Software"), to deal
18in the Software without restriction, including without limitation the rights
19to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20copies of the Software, and to permit persons to whom the Software is
21furnished to do so, subject to the following conditions:
22
23The above copyright notice and this permission notice shall be included in
24all copies or substantial portions of the Software.
25
26THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32THE SOFTWARE.
33
34===============================================================================
35]]--
36
37-- We only need to have base and table libraries (and io for debugging)
38--
39local table_concat = assert( table.concat)
40local table_insert = assert( table.insert)
41local table_remove = assert( table.remove)
42local select, unpack = assert( select), assert( unpack)
43
44--[[
45local function WR(...)
46 if io then
47 io.stderr:write( table_concat({...},'\t').."\n" )
48 end
49end
50
51local function DEBUG(title,ud,key)
52 assert( title and ud and key )
53
54 local data,_= tables(ud)
55
56 local s= tostring(data[key])
57 for _,v in ipairs( data[key] or {} ) do
58 s= s..", "..tostring(v)
59 end
60 WR( "*** "..title.." ("..tostring(key).."): ", s )
61end
62--]]
63
64-----
65-- FIFO for a key
66--
67
68local fifo_new = function()
69 return { first = 1, count = 0}
70end
71
72local fifo_push = function( fifo, ...)
73 local first, count, added = fifo.first, fifo.count, select( '#', ...)
74 local start = first + count - 1
75 for i = 1, added do
76 fifo[start + i] = select( i, ...)
77 end
78 fifo.count = count + added
79end
80
81local fifo_peek = function( fifo, count)
82 if count <= fifo.count then
83 local first = fifo.first
84 local last = first + count - 1
85 return unpack( fifo, first, last)
86 end
87end
88
89local fifo_pop = function( fifo, count)
90 local first = fifo.first
91 local last = first + count - 1
92 local out = { unpack( fifo, first, last)}
93 for i = first, last do
94 fifo[i] = nil
95 end
96 fifo.first = first + count
97 fifo.count = fifo.count - count
98 return unpack( out)
99end
100
101
102-----
103-- Actual data store
104--
105-- { [linda_deep_ud]= { key= { val [, ... ] } [, ...] }
106-- ...
107-- }
108--
109local _data= {}
110
111-----
112-- Length limits (if any) for queues
113--
114-- 0: don't queue values at all; ':send()' waits if the slot is not vacant
115-- N: allow N values to be queued (slot itself + N-1); wait if full
116-- nil: no limits, '_data' may grow endlessly
117--
118local _limits= {}
119
120-----
121-- data_tbl, limits_tbl = tables( linda_deep_ud )
122--
123-- Gives appropriate tables for a certain Linda (creates them if needed)
124--
125local function tables( ud )
126 -- tables are created either all or nothing
127 --
128 if not _data[ud] then
129 _data[ud]= {}
130 _limits[ud]= {}
131 end
132 return _data[ud], _limits[ud]
133end
134
135-----
136-- bool= send( linda_deep_ud, key, ...)
137--
138-- Send new data (1..N) to 'key' slot. This send is atomic; all the values
139-- end up one after each other (this is why having possibility for sending
140-- multiple values in one call is deemed important).
141--
142-- If the queue has a limit, values are sent only if all of them fit in.
143--
144-- Returns: 'true' if all the values were placed
145-- 'false' if sending would exceed the queue limit (wait & retry)
146--
147function send( ud, key, ...)
148
149 local data, limits = tables( ud)
150
151 local n = select( '#', ...)
152
153 -- Initialize queue for all keys that have been used with ':send()'
154 --
155 if data[key] == nil then
156 data[key] = fifo_new()
157 end
158 local fifo = data[key]
159
160 local len = fifo.count
161 local m = limits[key]
162
163 if m and len+n > m then
164 return false -- would exceed the limit; try again later
165 end
166
167 fifo_push( fifo, ...)
168 return true
169end
170
171
172-----
173-- [val, key]= receive( linda_deep_ud, key [, ...] )
174--
175-- Read any of the given keys, consuming the data found. Keys are read in
176-- order.
177--
178function receive( ud, ...)
179
180 local data = tables( ud)
181
182 for i = 1, select( '#', ...) do
183 local key = select( i, ...)
184 local fifo = data[key]
185 if fifo and fifo.count > 0 then
186 local val = fifo_pop( fifo, 1)
187 if val ~= nil then
188 return val, key
189 end
190 end
191 end
192end
193
194
195-----
196-- [val1, ... valCOUNT]= receive_batched( linda_deep_ud, key , min_COUNT, max_COUNT)
197--
198-- Read a single key, consuming the data found.
199--
200receive_batched = function( ud, key, min_count, max_count)
201 if min_count > 0 then
202 local fifo = tables( ud)[key]
203 if fifo then
204 local fifo_count = fifo.count
205 if fifo_count >= min_count then
206 max_count = max_count or min_count
207 max_count = (max_count > fifo_count) and fifo_count or max_count
208 return fifo_pop( fifo, max_count)
209 end
210 end
211 end
212end
213
214
215-----
216-- = limit( linda_deep_ud, key, uint )
217--
218function limit( ud, key, n)
219
220 local _, limits = tables( ud)
221
222 limits[key] = n
223end
224
225
226-----
227-- void= set( linda_deep_ud, key, [val] )
228--
229function set( ud, key, val)
230
231 local data, _ = tables( ud)
232
233 -- Setting a key to 'nil' really clears it; only queing uses sentinels.
234 --
235 if val ~= nil then
236 local fifo = fifo_new()
237 fifo_push( fifo, val)
238 data[key] = fifo
239 else
240 data[key] = nil
241 end
242end
243
244
245-----
246-- [val]= get( linda_deep_ud, key )
247--
248function get( ud, key)
249 local data, _ = tables( ud)
250 local fifo = data[key]
251 return fifo and fifo_peek( fifo, 1)
252end
253
254
255-----
256-- [val]= count( linda_deep_ud, ...)
257--
258-- 3 modes of operation
259-- linda:count() -> returns a table of key/count pairs
260-- linda:count(key) returns the number of items waiting in the key
261-- linda:count(key,...) -> returns a table telling, for each key, the number of items
262function count( ud, ...)
263 local data, _ = tables( ud)
264 local n = select( '#', ...)
265 if n == 0 then
266 local out
267 for key, _ in pairs( data) do
268 local fifo = data[key]
269 local count = fifo and fifo.count or 0
270 out = out or {}
271 out[key] = count
272 found = true
273 end
274 return out
275 elseif n == 1 then
276 local key = ...
277 local fifo = data[key]
278 return fifo and fifo.count or nil
279 else -- more than 1 key
280 local out
281 for i = 1, n do
282 local key = select( i, ...)
283 local fifo = data[key]
284 local count = fifo and fifo.count or nil
285 out = out or {}
286 out[key] = count
287 end
288 return out
289 end
290end
291
292
293-----
294-- void= clear( linda_deep_ud)
295--
296-- Clear the data structures used for a Linda (at its destructor)
297--
298function clear( ud)
299
300 _data[ud]= nil
301 _limits[ud]= nil
302end