diff options
Diffstat (limited to 'src/keeper.lua')
-rw-r--r-- | src/keeper.lua | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/src/keeper.lua b/src/keeper.lua new file mode 100644 index 0000000..f76173b --- /dev/null +++ b/src/keeper.lua | |||
@@ -0,0 +1,244 @@ | |||
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 | |||
14 | Copyright (C) 2008 Asko Kauppi <akauppi@gmail.com> | ||
15 | |||
16 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
17 | of this software and associated documentation files (the "Software"), to deal | ||
18 | in the Software without restriction, including without limitation the rights | ||
19 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
20 | copies of the Software, and to permit persons to whom the Software is | ||
21 | furnished to do so, subject to the following conditions: | ||
22 | |||
23 | The above copyright notice and this permission notice shall be included in | ||
24 | all copies or substantial portions of the Software. | ||
25 | |||
26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
27 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
29 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
31 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
32 | THE SOFTWARE. | ||
33 | |||
34 | =============================================================================== | ||
35 | ]]-- | ||
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) | ||
42 | -- | ||
43 | local table_remove= assert( table.remove ) | ||
44 | local table_concat= assert( table.concat ) | ||
45 | |||
46 | local function WR(...) | ||
47 | if io then | ||
48 | io.stderr:write( table_concat({...},'\t').."\n" ) | ||
49 | end | ||
50 | end | ||
51 | |||
52 | ----- | ||
53 | -- Actual data store | ||
54 | -- | ||
55 | -- { [linda_deep_ud]= { key= val [, ...] } | ||
56 | -- ... | ||
57 | -- } | ||
58 | -- | ||
59 | local _data= {} | ||
60 | |||
61 | ----- | ||
62 | -- Entries queued for use when the existing 'data[ud][key]' entry is consumed. | ||
63 | -- | ||
64 | -- { [linda_deep_ud]= { key= { val [, ... } [, ...] } | ||
65 | -- ... | ||
66 | -- } | ||
67 | -- | ||
68 | local _incoming= {} | ||
69 | |||
70 | ----- | ||
71 | -- Length limits (if any) for queues | ||
72 | -- | ||
73 | -- 0: don't queue values at all; ':send()' waits if the slot is not vacant | ||
74 | -- N: allow N values to be queued (slot itself + N-1); wait if full | ||
75 | -- nil: no limits, '_incoming' may grow endlessly | ||
76 | -- | ||
77 | local _limits= {} | ||
78 | |||
79 | ----- | ||
80 | -- data_tbl, incoming_tbl, limits_tbl = tables( linda_deep_ud ) | ||
81 | -- | ||
82 | -- Gives appropriate tables for a certain Linda (creates them if needed) | ||
83 | -- | ||
84 | local function tables( ud ) | ||
85 | -- tables are created either all or nothing | ||
86 | -- | ||
87 | if not _data[ud] then | ||
88 | _data[ud]= {} | ||
89 | _incoming[ud]= {} | ||
90 | _limits[ud]= {} | ||
91 | end | ||
92 | return _data[ud], _incoming[ud], _limits[ud] | ||
93 | end | ||
94 | |||
95 | |||
96 | local function DEBUG(title,ud,key) | ||
97 | assert( title and ud and key ) | ||
98 | |||
99 | local data,incoming,_= tables(ud) | ||
100 | |||
101 | local s= tostring(data[key]) | ||
102 | for _,v in ipairs( incoming[key] or {} ) do | ||
103 | s= s..", "..tostring(v) | ||
104 | end | ||
105 | WR( "*** "..title.." ("..tostring(key).."): ", s ) | ||
106 | end | ||
107 | |||
108 | |||
109 | ----- | ||
110 | -- bool= send( linda_deep_ud, key, ... ) | ||
111 | -- | ||
112 | -- Send new data (1..N) to 'key' slot. This send is atomic; all the values | ||
113 | -- end up one after each other (this is why having possibility for sending | ||
114 | -- multiple values in one call is deemed important). | ||
115 | -- | ||
116 | -- If the queue has a limit, values are sent only if all of them fit in. | ||
117 | -- | ||
118 | -- Returns: 'true' if all the values were placed | ||
119 | -- 'false' if sending would exceed the queue limit (wait & retry) | ||
120 | -- | ||
121 | function send( ud, key, ... ) | ||
122 | |||
123 | local data,incoming,limits= tables(ud) | ||
124 | |||
125 | local n= select('#',...) | ||
126 | if n==0 then return true end -- nothing to send | ||
127 | |||
128 | -- Initialize queue for all keys that have been used with ':send()' | ||
129 | -- | ||
130 | if incoming[key]==nil then | ||
131 | incoming[key]= {} | ||
132 | end | ||
133 | |||
134 | local len= data[key] and 1+#incoming[key] or 0 | ||
135 | local m= limits[key] | ||
136 | |||
137 | if m and len+n > m then | ||
138 | return false -- would exceed the limit; try again later | ||
139 | end | ||
140 | |||
141 | for i=1,n do | ||
142 | local val= select(i,...) | ||
143 | |||
144 | -- 'nil' in the data replaced by sentinel | ||
145 | if val==nil then | ||
146 | val= nil_sentinel | ||
147 | end | ||
148 | |||
149 | if len==0 then | ||
150 | data[key]= val | ||
151 | len= 1 | ||
152 | else | ||
153 | incoming[key][len]= val | ||
154 | len= len+1 | ||
155 | end | ||
156 | end | ||
157 | return true | ||
158 | end | ||
159 | |||
160 | |||
161 | ----- | ||
162 | -- [val, key]= receive( linda_deep_ud, key [, ...] ) | ||
163 | -- | ||
164 | -- Read any of the given keys, consuming the data found. Keys are read in | ||
165 | -- order. | ||
166 | -- | ||
167 | function receive( ud, ... ) | ||
168 | |||
169 | local data,incoming,_= tables(ud) | ||
170 | |||
171 | for i=1,select('#',...) do | ||
172 | local key= select(i,...) | ||
173 | local val= data[key] | ||
174 | |||
175 | if val~=nil then | ||
176 | if incoming[key] and incoming[key][1]~=nil then | ||
177 | -- pop [1] from 'incoming[key]' into the actual slot | ||
178 | data[key]= table_remove( incoming[key], 1 ) | ||
179 | else | ||
180 | data[key]= nil -- empty the slot | ||
181 | end | ||
182 | if val==nil_sentinel then | ||
183 | val= nil | ||
184 | end | ||
185 | return val, key | ||
186 | end | ||
187 | end | ||
188 | --return nil | ||
189 | end | ||
190 | |||
191 | |||
192 | ----- | ||
193 | -- = limit( linda_deep_ud, key, uint ) | ||
194 | -- | ||
195 | function limit( ud, key, n ) | ||
196 | |||
197 | local _,_,limits= tables(ud) | ||
198 | |||
199 | limits[key]= n | ||
200 | end | ||
201 | |||
202 | |||
203 | ----- | ||
204 | -- void= set( linda_deep_ud, key, [val] ) | ||
205 | -- | ||
206 | function set( ud, key, val ) | ||
207 | |||
208 | local data,incoming,_= tables(ud) | ||
209 | |||
210 | -- Setting a key to 'nil' really clears it; only queing uses sentinels. | ||
211 | -- | ||
212 | data[key]= val | ||
213 | incoming[key]= nil | ||
214 | end | ||
215 | |||
216 | |||
217 | ----- | ||
218 | -- [val]= get( linda_deep_ud, key ) | ||
219 | -- | ||
220 | function get( ud, key ) | ||
221 | |||
222 | local data,_,_= tables(ud) | ||
223 | |||
224 | local val= data[key] | ||
225 | if val==nil_sentinel then | ||
226 | val= nil | ||
227 | end | ||
228 | return val | ||
229 | end | ||
230 | |||
231 | |||
232 | ----- | ||
233 | -- void= clear( linda_deep_ud ) | ||
234 | -- | ||
235 | -- Clear the data structures used for a Linda (at its destructor) | ||
236 | -- | ||
237 | function clear( ud ) | ||
238 | |||
239 | _data[ud]= nil | ||
240 | _incoming[ud]= nil | ||
241 | _limits[ud]= nil | ||
242 | end | ||
243 | |||
244 | |||