diff options
Diffstat (limited to 'src/keeper.lua')
-rw-r--r-- | src/keeper.lua | 225 |
1 files changed, 134 insertions, 91 deletions
diff --git a/src/keeper.lua b/src/keeper.lua index 4bb181a..828932e 100644 --- a/src/keeper.lua +++ b/src/keeper.lua | |||
@@ -34,30 +34,27 @@ THE SOFTWARE. | |||
34 | =============================================================================== | 34 | =============================================================================== |
35 | ]]-- | 35 | ]]-- |
36 | 36 | ||
37 | -- unique key instead of 'nil' in queues | ||
38 | -- | ||
39 | assert( nil_sentinel ) | ||
40 | |||
41 | -- We only need to have base and table libraries (and io for debugging) | 37 | -- We only need to have base and table libraries (and io for debugging) |
42 | -- | 38 | -- |
43 | local table_concat = assert( table.concat) | 39 | local table_concat = assert( table.concat) |
44 | local table_insert = assert( table.insert) | 40 | local table_insert = assert( table.insert) |
45 | local table_remove = assert( table.remove) | 41 | local table_remove = assert( table.remove) |
42 | local select, unpack = assert( select), assert( unpack) | ||
46 | 43 | ||
47 | --[[ | 44 | --[[ |
48 | local function WR(...) | 45 | local function WR(...) |
49 | if io then | 46 | if io then |
50 | io.stderr:write( table_concat({...},'\t').."\n" ) | 47 | io.stderr:write( table_concat({...},'\t').."\n" ) |
51 | end | 48 | end |
52 | end | 49 | end |
53 | 50 | ||
54 | local function DEBUG(title,ud,key) | 51 | local function DEBUG(title,ud,key) |
55 | assert( title and ud and key ) | 52 | assert( title and ud and key ) |
56 | 53 | ||
57 | local data,incoming,_= tables(ud) | 54 | local data,_= tables(ud) |
58 | 55 | ||
59 | local s= tostring(data[key]) | 56 | local s= tostring(data[key]) |
60 | for _,v in ipairs( incoming[key] or {} ) do | 57 | for _,v in ipairs( data[key] or {} ) do |
61 | s= s..", "..tostring(v) | 58 | s= s..", "..tostring(v) |
62 | end | 59 | end |
63 | WR( "*** "..title.." ("..tostring(key).."): ", s ) | 60 | WR( "*** "..title.." ("..tostring(key).."): ", s ) |
@@ -65,34 +62,64 @@ end | |||
65 | --]] | 62 | --]] |
66 | 63 | ||
67 | ----- | 64 | ----- |
68 | -- Actual data store | 65 | -- FIFO for a key |
69 | -- | ||
70 | -- { [linda_deep_ud]= { key= val [, ...] } | ||
71 | -- ... | ||
72 | -- } | ||
73 | -- | 66 | -- |
74 | local _data= {} | 67 | |
68 | local fifo_new = function() | ||
69 | return { first = 1, count = 0} | ||
70 | end | ||
71 | |||
72 | local 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 | ||
79 | end | ||
80 | |||
81 | local 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 | ||
87 | end | ||
88 | |||
89 | local fifo_pop = function( fifo, count) | ||
90 | if count > fifo.count then error("list is too short") end | ||
91 | local first = fifo.first | ||
92 | local last = first + count - 1 | ||
93 | local out = { unpack( fifo, first, last)} | ||
94 | for i = first, last do | ||
95 | fifo[i] = nil | ||
96 | end | ||
97 | fifo.first = first + count | ||
98 | fifo.count = fifo.count - count | ||
99 | return unpack( out) | ||
100 | end | ||
101 | |||
75 | 102 | ||
76 | ----- | 103 | ----- |
77 | -- Entries queued for use when the existing 'data[ud][key]' entry is consumed. | 104 | -- Actual data store |
78 | -- | 105 | -- |
79 | -- { [linda_deep_ud]= { key= { val [, ... } [, ...] } | 106 | -- { [linda_deep_ud]= { key= { val [, ... ] } [, ...] } |
80 | -- ... | 107 | -- ... |
81 | -- } | 108 | -- } |
82 | -- | 109 | -- |
83 | local _incoming= {} | 110 | local _data= {} |
84 | 111 | ||
85 | ----- | 112 | ----- |
86 | -- Length limits (if any) for queues | 113 | -- Length limits (if any) for queues |
87 | -- | 114 | -- |
88 | -- 0: don't queue values at all; ':send()' waits if the slot is not vacant | 115 | -- 0: don't queue values at all; ':send()' waits if the slot is not vacant |
89 | -- N: allow N values to be queued (slot itself + N-1); wait if full | 116 | -- N: allow N values to be queued (slot itself + N-1); wait if full |
90 | -- nil: no limits, '_incoming' may grow endlessly | 117 | -- nil: no limits, '_data' may grow endlessly |
91 | -- | 118 | -- |
92 | local _limits= {} | 119 | local _limits= {} |
93 | 120 | ||
94 | ----- | 121 | ----- |
95 | -- data_tbl, incoming_tbl, limits_tbl = tables( linda_deep_ud ) | 122 | -- data_tbl, limits_tbl = tables( linda_deep_ud ) |
96 | -- | 123 | -- |
97 | -- Gives appropriate tables for a certain Linda (creates them if needed) | 124 | -- Gives appropriate tables for a certain Linda (creates them if needed) |
98 | -- | 125 | -- |
@@ -101,14 +128,13 @@ local function tables( ud ) | |||
101 | -- | 128 | -- |
102 | if not _data[ud] then | 129 | if not _data[ud] then |
103 | _data[ud]= {} | 130 | _data[ud]= {} |
104 | _incoming[ud]= {} | ||
105 | _limits[ud]= {} | 131 | _limits[ud]= {} |
106 | end | 132 | end |
107 | return _data[ud], _incoming[ud], _limits[ud] | 133 | return _data[ud], _limits[ud] |
108 | end | 134 | end |
109 | 135 | ||
110 | ----- | 136 | ----- |
111 | -- bool= send( linda_deep_ud, key, ... ) | 137 | -- bool= send( linda_deep_ud, key, ...) |
112 | -- | 138 | -- |
113 | -- Send new data (1..N) to 'key' slot. This send is atomic; all the values | 139 | -- Send new data (1..N) to 'key' slot. This send is atomic; all the values |
114 | -- end up one after each other (this is why having possibility for sending | 140 | -- end up one after each other (this is why having possibility for sending |
@@ -119,42 +145,28 @@ end | |||
119 | -- Returns: 'true' if all the values were placed | 145 | -- Returns: 'true' if all the values were placed |
120 | -- 'false' if sending would exceed the queue limit (wait & retry) | 146 | -- 'false' if sending would exceed the queue limit (wait & retry) |
121 | -- | 147 | -- |
122 | function send( ud, key, ... ) | 148 | function send( ud, key, ...) |
123 | 149 | ||
124 | local data,incoming,limits= tables(ud) | 150 | local data, limits = tables( ud) |
125 | 151 | ||
126 | local n= select('#',...) | 152 | local n = select( '#', ...) |
127 | if n==0 then return true end -- nothing to send | 153 | if n == 0 then return true end -- nothing to send |
128 | 154 | ||
129 | -- Initialize queue for all keys that have been used with ':send()' | 155 | -- Initialize queue for all keys that have been used with ':send()' |
130 | -- | 156 | -- |
131 | if incoming[key]==nil then | 157 | if data[key] == nil then |
132 | incoming[key]= {} | 158 | data[key] = fifo_new() |
133 | end | 159 | end |
160 | local fifo = data[key] | ||
134 | 161 | ||
135 | local len= data[key] and 1+#incoming[key] or 0 | 162 | local len = fifo.count |
136 | local m= limits[key] | 163 | local m = limits[key] |
137 | 164 | ||
138 | if m and len+n > m then | 165 | if m and len+n > m then |
139 | return false -- would exceed the limit; try again later | 166 | return false -- would exceed the limit; try again later |
140 | end | 167 | end |
141 | 168 | ||
142 | for i=1,n do | 169 | fifo_push( fifo, ...) |
143 | local val= select(i,...) | ||
144 | |||
145 | -- 'nil' in the data replaced by sentinel | ||
146 | if val==nil then | ||
147 | val= nil_sentinel | ||
148 | end | ||
149 | |||
150 | if len==0 then | ||
151 | data[key]= val | ||
152 | len= 1 | ||
153 | else | ||
154 | incoming[key][len]= val | ||
155 | len= len+1 | ||
156 | end | ||
157 | end | ||
158 | return true | 170 | return true |
159 | end | 171 | end |
160 | 172 | ||
@@ -165,94 +177,125 @@ end | |||
165 | -- Read any of the given keys, consuming the data found. Keys are read in | 177 | -- Read any of the given keys, consuming the data found. Keys are read in |
166 | -- order. | 178 | -- order. |
167 | -- | 179 | -- |
168 | function receive( ud, ... ) | 180 | function receive( ud, ...) |
169 | 181 | ||
170 | local data,incoming,_= tables(ud) | 182 | local data, _ = tables( ud) |
171 | 183 | ||
172 | for i=1,select('#',...) do | 184 | for i = 1, select( '#', ...) do |
173 | local key= select(i,...) | 185 | local key = select( i, ...) |
174 | local val= data[key] | 186 | local fifo = data[key] |
175 | 187 | if fifo and fifo.count > 0 then | |
176 | if val~=nil then | 188 | local val = fifo_pop( fifo, 1) |
177 | if incoming[key] and incoming[key][1]~=nil then | 189 | if val ~= nil then |
178 | -- pop [1] from 'incoming[key]' into the actual slot | 190 | return val, key |
179 | data[key]= table_remove( incoming[key], 1 ) | ||
180 | else | ||
181 | data[key]= nil -- empty the slot | ||
182 | end | ||
183 | if val==nil_sentinel then | ||
184 | val= nil | ||
185 | end | 191 | end |
186 | return val, key | ||
187 | end | 192 | end |
188 | end | 193 | end |
189 | --return nil | 194 | end |
195 | |||
196 | |||
197 | ----- | ||
198 | -- [val1, ... valCOUNT]= receive_batched( linda_deep_ud, batch_sentinel, key , COUNT) | ||
199 | -- | ||
200 | -- Read any of the given keys, consuming the data found. Keys are read in | ||
201 | -- order. | ||
202 | -- | ||
203 | receive_batched = function( ud, batch_sentinel, key, count) | ||
204 | if count > 0 then | ||
205 | local data, _ = tables( ud) | ||
206 | local fifo = data[key] | ||
207 | if fifo and fifo.count >= count then | ||
208 | return fifo_pop( fifo, count) | ||
209 | end | ||
210 | end | ||
190 | end | 211 | end |
191 | 212 | ||
192 | 213 | ||
193 | ----- | 214 | ----- |
194 | -- = limit( linda_deep_ud, key, uint ) | 215 | -- = limit( linda_deep_ud, key, uint ) |
195 | -- | 216 | -- |
196 | function limit( ud, key, n ) | 217 | function limit( ud, key, n) |
197 | 218 | ||
198 | local _,_,limits= tables(ud) | 219 | local _, limits = tables( ud) |
199 | 220 | ||
200 | limits[key]= n | 221 | limits[key] = n |
201 | end | 222 | end |
202 | 223 | ||
203 | 224 | ||
204 | ----- | 225 | ----- |
205 | -- void= set( linda_deep_ud, key, [val] ) | 226 | -- void= set( linda_deep_ud, key, [val] ) |
206 | -- | 227 | -- |
207 | function set( ud, key, val ) | 228 | function set( ud, key, val) |
208 | 229 | ||
209 | local data,incoming,_= tables(ud) | 230 | local data, _ = tables( ud) |
210 | 231 | ||
211 | -- Setting a key to 'nil' really clears it; only queing uses sentinels. | 232 | -- Setting a key to 'nil' really clears it; only queing uses sentinels. |
212 | -- | 233 | -- |
213 | data[key]= val | 234 | if val ~= nil then |
214 | incoming[key]= nil | 235 | local fifo = fifo_new() |
236 | fifo_push( fifo, val) | ||
237 | data[key] = fifo | ||
238 | else | ||
239 | data[key] = nil | ||
240 | end | ||
215 | end | 241 | end |
216 | 242 | ||
217 | 243 | ||
218 | ----- | 244 | ----- |
219 | -- [val]= get( linda_deep_ud, key ) | 245 | -- [val]= get( linda_deep_ud, key ) |
220 | -- | 246 | -- |
221 | function get( ud, key ) | 247 | function get( ud, key) |
222 | 248 | local data, _ = tables( ud) | |
223 | local data,_,_= tables(ud) | 249 | local fifo = data[key] |
224 | 250 | return fifo and fifo_peek( fifo, 1) | |
225 | local val= data[key] | ||
226 | if val==nil_sentinel then | ||
227 | val= nil | ||
228 | end | ||
229 | return val | ||
230 | end | 251 | end |
231 | 252 | ||
232 | 253 | ||
233 | ----- | 254 | ----- |
234 | -- [val]= keys( linda_deep_ud) | 255 | -- [val]= count( linda_deep_ud, ...) |
235 | -- | 256 | -- |
236 | function keys( ud) | 257 | -- 3 modes of operation |
237 | 258 | -- linda:count() -> returns a table of key/count pairs | |
238 | local data,_,_= tables(ud) | 259 | -- linda:count(key) returns the number of items waiting in the key |
239 | 260 | -- linda:count(key,...) -> returns a table telling, for each key, the number of items | |
240 | local out = {} | 261 | function count( ud, ...) |
241 | for key, v in pairs( data) do | 262 | local data, _ = tables( ud) |
242 | table_insert( out, key) | 263 | local n = select( '#', ...) |
264 | if n == 0 then | ||
265 | local out | ||
266 | for key, _ in pairs( data) do | ||
267 | local fifo = data[key] | ||
268 | local count = fifo and fifo.count or 0 | ||
269 | out = out or {} | ||
270 | out[key] = count | ||
271 | found = true | ||
272 | end | ||
273 | return out | ||
274 | elseif n == 1 then | ||
275 | local key = ... | ||
276 | local fifo = data[key] | ||
277 | return fifo and fifo.count or nil | ||
278 | else -- more than 1 key | ||
279 | local out | ||
280 | for i = 1, n do | ||
281 | local key = select( i, ...) | ||
282 | local fifo = data[key] | ||
283 | local count = fifo and fifo.count or nil | ||
284 | out = out or {} | ||
285 | out[key] = count | ||
286 | end | ||
287 | return out | ||
243 | end | 288 | end |
244 | return (#out > 0) and out or nil | ||
245 | end | 289 | end |
246 | 290 | ||
247 | 291 | ||
248 | ----- | 292 | ----- |
249 | -- void= clear( linda_deep_ud ) | 293 | -- void= clear( linda_deep_ud) |
250 | -- | 294 | -- |
251 | -- Clear the data structures used for a Linda (at its destructor) | 295 | -- Clear the data structures used for a Linda (at its destructor) |
252 | -- | 296 | -- |
253 | function clear( ud ) | 297 | function clear( ud) |
254 | 298 | ||
255 | _data[ud]= nil | 299 | _data[ud]= nil |
256 | _incoming[ud]= nil | ||
257 | _limits[ud]= nil | 300 | _limits[ud]= nil |
258 | end | 301 | end |