diff options
Diffstat (limited to 'src/keeper.c')
-rw-r--r-- | src/keeper.c | 825 |
1 files changed, 0 insertions, 825 deletions
diff --git a/src/keeper.c b/src/keeper.c deleted file mode 100644 index 8aa734a..0000000 --- a/src/keeper.c +++ /dev/null | |||
@@ -1,825 +0,0 @@ | |||
1 | /* | ||
2 | -- | ||
3 | -- KEEPER.C | ||
4 | -- | ||
5 | -- Keeper state logic | ||
6 | -- | ||
7 | -- This code is read in for each "keeper state", which are the hidden, inter- | ||
8 | -- mediate data stores used by Lanes inter-state communication objects. | ||
9 | -- | ||
10 | -- Author: Benoit Germain <bnt.germain@gmail.com> | ||
11 | -- | ||
12 | -- C implementation replacement of the original keeper.lua | ||
13 | -- | ||
14 | --[[ | ||
15 | =============================================================================== | ||
16 | |||
17 | Copyright (C) 2011-2013 Benoit Germain <bnt.germain@gmail.com> | ||
18 | |||
19 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
20 | of this software and associated documentation files (the "Software"), to deal | ||
21 | in the Software without restriction, including without limitation the rights | ||
22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
23 | copies of the Software, and to permit persons to whom the Software is | ||
24 | furnished to do so, subject to the following conditions: | ||
25 | |||
26 | The above copyright notice and this permission notice shall be included in | ||
27 | all copies or substantial portions of the Software. | ||
28 | |||
29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
35 | THE SOFTWARE. | ||
36 | |||
37 | =============================================================================== | ||
38 | ]]-- | ||
39 | */ | ||
40 | |||
41 | #include <string.h> | ||
42 | #include <stdio.h> | ||
43 | #include <stdlib.h> | ||
44 | #include <ctype.h> | ||
45 | #include <assert.h> | ||
46 | |||
47 | #include "keeper.h" | ||
48 | #include "compat.h" | ||
49 | #include "tools.h" | ||
50 | #include "state.h" | ||
51 | #include "universe.h" | ||
52 | #include "uniquekey.h" | ||
53 | |||
54 | //################################################################################### | ||
55 | // Keeper implementation | ||
56 | //################################################################################### | ||
57 | |||
58 | #ifndef __min | ||
59 | #define __min( a, b) (((a) < (b)) ? (a) : (b)) | ||
60 | #endif // __min | ||
61 | |||
62 | typedef struct | ||
63 | { | ||
64 | lua_Integer first; | ||
65 | lua_Integer count; | ||
66 | lua_Integer limit; | ||
67 | } keeper_fifo; | ||
68 | |||
69 | static int const CONTENTS_TABLE = 1; | ||
70 | |||
71 | // replaces the fifo ud by its uservalue on the stack | ||
72 | static keeper_fifo* prepare_fifo_access( lua_State* L, int idx_) | ||
73 | { | ||
74 | keeper_fifo* fifo = (keeper_fifo*) lua_touserdata( L, idx_); | ||
75 | if( fifo != NULL) | ||
76 | { | ||
77 | idx_ = lua_absindex( L, idx_); | ||
78 | STACK_GROW( L, 1); | ||
79 | // we can replace the fifo userdata in the stack without fear of it being GCed, there are other references around | ||
80 | lua_getiuservalue( L, idx_, CONTENTS_TABLE); | ||
81 | lua_replace( L, idx_); | ||
82 | } | ||
83 | return fifo; | ||
84 | } | ||
85 | |||
86 | // in: nothing | ||
87 | // out: { first = 1, count = 0, limit = -1} | ||
88 | static void fifo_new( lua_State* L) | ||
89 | { | ||
90 | keeper_fifo* fifo; | ||
91 | STACK_GROW( L, 2); | ||
92 | // a fifo full userdata has one uservalue, the table that holds the actual fifo contents | ||
93 | fifo = (keeper_fifo*)lua_newuserdatauv( L, sizeof( keeper_fifo), 1); | ||
94 | fifo->first = 1; | ||
95 | fifo->count = 0; | ||
96 | fifo->limit = -1; | ||
97 | lua_newtable( L); | ||
98 | lua_setiuservalue( L, -2, CONTENTS_TABLE); | ||
99 | } | ||
100 | |||
101 | // in: expect fifo ... on top of the stack | ||
102 | // out: nothing, removes all pushed values from the stack | ||
103 | static void fifo_push( lua_State* L, keeper_fifo* fifo_, lua_Integer count_) | ||
104 | { | ||
105 | int const idx = lua_gettop( L) - (int) count_; | ||
106 | lua_Integer start = fifo_->first + fifo_->count - 1; | ||
107 | lua_Integer i; | ||
108 | // pop all additional arguments, storing them in the fifo | ||
109 | for( i = count_; i >= 1; -- i) | ||
110 | { | ||
111 | // store in the fifo the value at the top of the stack at the specified index, popping it from the stack | ||
112 | lua_rawseti( L, idx, (int)(start + i)); | ||
113 | } | ||
114 | fifo_->count += count_; | ||
115 | } | ||
116 | |||
117 | // in: fifo | ||
118 | // out: ...|nothing | ||
119 | // expects exactly 1 value on the stack! | ||
120 | // currently only called with a count of 1, but this may change in the future | ||
121 | // function assumes that there is enough data in the fifo to satisfy the request | ||
122 | static void fifo_peek( lua_State* L, keeper_fifo* fifo_, lua_Integer count_) | ||
123 | { | ||
124 | lua_Integer i; | ||
125 | STACK_GROW( L, count_); | ||
126 | for( i = 0; i < count_; ++ i) | ||
127 | { | ||
128 | lua_rawgeti( L, 1, (int)( fifo_->first + i)); | ||
129 | } | ||
130 | } | ||
131 | |||
132 | // in: fifo | ||
133 | // out: remove the fifo from the stack, push as many items as required on the stack (function assumes they exist in sufficient number) | ||
134 | static void fifo_pop( lua_State* L, keeper_fifo* fifo_, lua_Integer count_) | ||
135 | { | ||
136 | int const fifo_idx = lua_gettop( L); // ... fifo | ||
137 | int i; | ||
138 | // each iteration pushes a value on the stack! | ||
139 | STACK_GROW( L, count_ + 2); | ||
140 | // skip first item, we will push it last | ||
141 | for( i = 1; i < count_; ++ i) | ||
142 | { | ||
143 | int const at = (int)( fifo_->first + i); | ||
144 | // push item on the stack | ||
145 | lua_rawgeti( L, fifo_idx, at); // ... fifo val | ||
146 | // remove item from the fifo | ||
147 | lua_pushnil( L); // ... fifo val nil | ||
148 | lua_rawseti( L, fifo_idx, at); // ... fifo val | ||
149 | } | ||
150 | // now process first item | ||
151 | { | ||
152 | int const at = (int)( fifo_->first); | ||
153 | lua_rawgeti( L, fifo_idx, at); // ... fifo vals val | ||
154 | lua_pushnil( L); // ... fifo vals val nil | ||
155 | lua_rawseti( L, fifo_idx, at); // ... fifo vals val | ||
156 | lua_replace( L, fifo_idx); // ... vals | ||
157 | } | ||
158 | { | ||
159 | // avoid ever-growing indexes by resetting each time we detect the fifo is empty | ||
160 | lua_Integer const new_count = fifo_->count - count_; | ||
161 | fifo_->first = (new_count == 0) ? 1 : (fifo_->first + count_); | ||
162 | fifo_->count = new_count; | ||
163 | } | ||
164 | } | ||
165 | |||
166 | // in: linda_ud expected at *absolute* stack slot idx | ||
167 | // out: fifos[ud] | ||
168 | // crc64/we of string "FIFOS_KEY" generated at http://www.nitrxgen.net/hashgen/ | ||
169 | static DECLARE_CONST_UNIQUE_KEY( FIFOS_KEY, 0xdce50bbc351cd465); | ||
170 | static void push_table( lua_State* L, int idx_) | ||
171 | { | ||
172 | STACK_GROW( L, 4); | ||
173 | STACK_CHECK( L, 0); | ||
174 | idx_ = lua_absindex( L, idx_); | ||
175 | REGISTRY_GET( L, FIFOS_KEY); // ud fifos | ||
176 | lua_pushvalue( L, idx_); // ud fifos ud | ||
177 | lua_rawget( L, -2); // ud fifos fifos[ud] | ||
178 | STACK_MID( L, 2); | ||
179 | if( lua_isnil( L, -1)) | ||
180 | { | ||
181 | lua_pop( L, 1); // ud fifos | ||
182 | // add a new fifos table for this linda | ||
183 | lua_newtable( L); // ud fifos fifos[ud] | ||
184 | lua_pushvalue( L, idx_); // ud fifos fifos[ud] ud | ||
185 | lua_pushvalue( L, -2); // ud fifos fifos[ud] ud fifos[ud] | ||
186 | lua_rawset( L, -4); // ud fifos fifos[ud] | ||
187 | } | ||
188 | lua_remove( L, -2); // ud fifos[ud] | ||
189 | STACK_END( L, 1); | ||
190 | } | ||
191 | |||
192 | int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, ptrdiff_t magic_) | ||
193 | { | ||
194 | Keeper* const K = which_keeper( U->keepers, magic_); | ||
195 | lua_State* const KL = K ? K->L : NULL; | ||
196 | if( KL == NULL) return 0; | ||
197 | STACK_GROW( KL, 4); | ||
198 | STACK_CHECK( KL, 0); | ||
199 | REGISTRY_GET( KL, FIFOS_KEY); // fifos | ||
200 | lua_pushlightuserdata( KL, ptr_); // fifos ud | ||
201 | lua_rawget( KL, -2); // fifos storage | ||
202 | lua_remove( KL, -2); // storage | ||
203 | if( !lua_istable( KL, -1)) | ||
204 | { | ||
205 | lua_pop( KL, 1); // | ||
206 | STACK_MID( KL, 0); | ||
207 | return 0; | ||
208 | } | ||
209 | // move data from keeper to destination state KEEPER MAIN | ||
210 | lua_pushnil( KL); // storage nil | ||
211 | STACK_GROW( L, 5); | ||
212 | STACK_CHECK( L, 0); | ||
213 | lua_newtable( L); // out | ||
214 | while( lua_next( KL, -2)) // storage key fifo | ||
215 | { | ||
216 | keeper_fifo* fifo = prepare_fifo_access( KL, -1); // storage key fifo | ||
217 | lua_pushvalue( KL, -2); // storage key fifo key | ||
218 | luaG_inter_move( U, KL, L, 1, eLM_FromKeeper); // storage key fifo // out key | ||
219 | STACK_MID( L, 2); | ||
220 | lua_newtable( L); // out key keyout | ||
221 | luaG_inter_move( U, KL, L, 1, eLM_FromKeeper); // storage key // out key keyout fifo | ||
222 | lua_pushinteger( L, fifo->first); // out key keyout fifo first | ||
223 | STACK_MID( L, 5); | ||
224 | lua_setfield( L, -3, "first"); // out key keyout fifo | ||
225 | lua_pushinteger( L, fifo->count); // out key keyout fifo count | ||
226 | STACK_MID( L, 5); | ||
227 | lua_setfield( L, -3, "count"); // out key keyout fifo | ||
228 | lua_pushinteger( L, fifo->limit); // out key keyout fifo limit | ||
229 | STACK_MID( L, 5); | ||
230 | lua_setfield( L, -3, "limit"); // out key keyout fifo | ||
231 | lua_setfield( L, -2, "fifo"); // out key keyout | ||
232 | lua_rawset( L, -3); // out | ||
233 | STACK_MID( L, 1); | ||
234 | } | ||
235 | STACK_END( L, 1); | ||
236 | lua_pop( KL, 1); // | ||
237 | STACK_END( KL, 0); | ||
238 | return 1; | ||
239 | } | ||
240 | |||
241 | // in: linda_ud | ||
242 | int keepercall_clear( lua_State* L) | ||
243 | { | ||
244 | STACK_GROW( L, 3); | ||
245 | STACK_CHECK( L, 0); | ||
246 | REGISTRY_GET( L, FIFOS_KEY); // ud fifos | ||
247 | lua_pushvalue( L, 1); // ud fifos ud | ||
248 | lua_pushnil( L); // ud fifos ud nil | ||
249 | lua_rawset( L, -3); // ud fifos | ||
250 | lua_pop( L, 1); // ud | ||
251 | STACK_END( L, 0); | ||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | |||
256 | // in: linda_ud, key, ... | ||
257 | // out: true|false | ||
258 | int keepercall_send( lua_State* L) | ||
259 | { | ||
260 | keeper_fifo* fifo; | ||
261 | int n = lua_gettop( L) - 2; | ||
262 | push_table( L, 1); // ud key ... fifos | ||
263 | // get the fifo associated to this key in this linda, create it if it doesn't exist | ||
264 | lua_pushvalue( L, 2); // ud key ... fifos key | ||
265 | lua_rawget( L, -2); // ud key ... fifos fifo | ||
266 | if( lua_isnil( L, -1)) | ||
267 | { | ||
268 | lua_pop( L, 1); // ud key ... fifos | ||
269 | fifo_new( L); // ud key ... fifos fifo | ||
270 | lua_pushvalue( L, 2); // ud key ... fifos fifo key | ||
271 | lua_pushvalue( L, -2); // ud key ... fifos fifo key fifo | ||
272 | lua_rawset( L, -4); // ud key ... fifos fifo | ||
273 | } | ||
274 | lua_remove( L, -2); // ud key ... fifo | ||
275 | fifo = (keeper_fifo*) lua_touserdata( L, -1); | ||
276 | if( fifo->limit >= 0 && fifo->count + n > fifo->limit) | ||
277 | { | ||
278 | lua_settop( L, 0); // | ||
279 | lua_pushboolean( L, 0); // false | ||
280 | } | ||
281 | else | ||
282 | { | ||
283 | fifo = prepare_fifo_access( L, -1); | ||
284 | lua_replace( L, 2); // ud fifo ... | ||
285 | fifo_push( L, fifo, n); // ud fifo | ||
286 | lua_settop( L, 0); // | ||
287 | lua_pushboolean( L, 1); // true | ||
288 | } | ||
289 | return 1; | ||
290 | } | ||
291 | |||
292 | // in: linda_ud, key [, key]? | ||
293 | // out: (key, val) or nothing | ||
294 | int keepercall_receive( lua_State* L) | ||
295 | { | ||
296 | int top = lua_gettop( L); | ||
297 | int i; | ||
298 | push_table( L, 1); // ud keys fifos | ||
299 | lua_replace( L, 1); // fifos keys | ||
300 | for( i = 2; i <= top; ++ i) | ||
301 | { | ||
302 | keeper_fifo* fifo; | ||
303 | lua_pushvalue( L, i); // fifos keys key[i] | ||
304 | lua_rawget( L, 1); // fifos keys fifo | ||
305 | fifo = prepare_fifo_access( L, -1); // fifos keys fifo | ||
306 | if( fifo != NULL && fifo->count > 0) | ||
307 | { | ||
308 | fifo_pop( L, fifo, 1); // fifos keys val | ||
309 | if( !lua_isnil( L, -1)) | ||
310 | { | ||
311 | lua_replace( L, 1); // val keys | ||
312 | lua_settop( L, i); // val keys key[i] | ||
313 | if( i != 2) | ||
314 | { | ||
315 | lua_replace( L, 2); // val key keys | ||
316 | lua_settop( L, 2); // val key | ||
317 | } | ||
318 | lua_insert( L, 1); // key, val | ||
319 | return 2; | ||
320 | } | ||
321 | } | ||
322 | lua_settop( L, top); // data keys | ||
323 | } | ||
324 | // nothing to receive | ||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | //in: linda_ud key mincount [maxcount] | ||
329 | int keepercall_receive_batched( lua_State* L) | ||
330 | { | ||
331 | lua_Integer const min_count = lua_tointeger( L, 3); | ||
332 | if( min_count > 0) | ||
333 | { | ||
334 | keeper_fifo* fifo; | ||
335 | lua_Integer const max_count = luaL_optinteger( L, 4, min_count); | ||
336 | lua_settop( L, 2); // ud key | ||
337 | lua_insert( L, 1); // key ud | ||
338 | push_table( L, 2); // key ud fifos | ||
339 | lua_remove( L, 2); // key fifos | ||
340 | lua_pushvalue( L, 1); // key fifos key | ||
341 | lua_rawget( L, 2); // key fifos fifo | ||
342 | lua_remove( L, 2); // key fifo | ||
343 | fifo = prepare_fifo_access( L, 2); // key fifo | ||
344 | if( fifo != NULL && fifo->count >= min_count) | ||
345 | { | ||
346 | fifo_pop( L, fifo, __min( max_count, fifo->count)); // key ... | ||
347 | } | ||
348 | else | ||
349 | { | ||
350 | lua_settop( L, 0); | ||
351 | } | ||
352 | return lua_gettop( L); | ||
353 | } | ||
354 | else | ||
355 | { | ||
356 | return 0; | ||
357 | } | ||
358 | } | ||
359 | |||
360 | // in: linda_ud key n | ||
361 | // out: true or nil | ||
362 | int keepercall_limit( lua_State* L) | ||
363 | { | ||
364 | keeper_fifo* fifo; | ||
365 | lua_Integer limit = lua_tointeger( L, 3); | ||
366 | push_table( L, 1); // ud key n fifos | ||
367 | lua_replace( L, 1); // fifos key n | ||
368 | lua_pop( L, 1); // fifos key | ||
369 | lua_pushvalue( L, -1); // fifos key key | ||
370 | lua_rawget( L, -3); // fifos key fifo|nil | ||
371 | fifo = (keeper_fifo*) lua_touserdata( L, -1); | ||
372 | if( fifo == NULL) | ||
373 | { // fifos key nil | ||
374 | lua_pop( L, 1); // fifos key | ||
375 | fifo_new( L); // fifos key fifo | ||
376 | fifo = (keeper_fifo*) lua_touserdata( L, -1); | ||
377 | lua_rawset( L, -3); // fifos | ||
378 | } | ||
379 | // remove any clutter on the stack | ||
380 | lua_settop( L, 0); | ||
381 | // return true if we decide that blocked threads waiting to write on that key should be awakened | ||
382 | // this is the case if we detect the key was full but it is no longer the case | ||
383 | if( | ||
384 | ((fifo->limit >= 0) && (fifo->count >= fifo->limit)) // the key was full if limited and count exceeded the previous limit | ||
385 | && ((limit < 0) || (fifo->count < limit)) // the key is not full if unlimited or count is lower than the new limit | ||
386 | ) | ||
387 | { | ||
388 | lua_pushboolean( L, 1); | ||
389 | } | ||
390 | // set the new limit | ||
391 | fifo->limit = limit; | ||
392 | // return 0 or 1 value | ||
393 | return lua_gettop( L); | ||
394 | } | ||
395 | |||
396 | //in: linda_ud key [[val] ...] | ||
397 | //out: true or nil | ||
398 | int keepercall_set( lua_State* L) | ||
399 | { | ||
400 | bool_t should_wake_writers = FALSE; | ||
401 | STACK_GROW( L, 6); | ||
402 | |||
403 | // retrieve fifos associated with the linda | ||
404 | push_table( L, 1); // ud key [val [, ...]] fifos | ||
405 | lua_replace( L, 1); // fifos key [val [, ...]] | ||
406 | |||
407 | // make sure we have a value on the stack | ||
408 | if( lua_gettop( L) == 2) // fifos key | ||
409 | { | ||
410 | keeper_fifo* fifo; | ||
411 | lua_pushvalue( L, -1); // fifos key key | ||
412 | lua_rawget( L, 1); // fifos key fifo|nil | ||
413 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! | ||
414 | fifo = (keeper_fifo*) lua_touserdata( L, -1); | ||
415 | if( fifo != NULL) // might be NULL if we set a nonexistent key to nil | ||
416 | { // fifos key fifo | ||
417 | if( fifo->limit < 0) // fifo limit value is the default (unlimited): we can totally remove it | ||
418 | { | ||
419 | lua_pop( L, 1); // fifos key | ||
420 | lua_pushnil( L); // fifos key nil | ||
421 | lua_rawset( L, -3); // fifos | ||
422 | } | ||
423 | else | ||
424 | { | ||
425 | // we create room if the fifo was full but it is no longer the case | ||
426 | should_wake_writers = (fifo->limit > 0) && (fifo->count >= fifo->limit); | ||
427 | lua_remove( L, -2); // fifos fifo | ||
428 | lua_newtable( L); // fifos fifo {} | ||
429 | lua_setiuservalue( L, -2, CONTENTS_TABLE); // fifos fifo | ||
430 | fifo->first = 1; | ||
431 | fifo->count = 0; | ||
432 | } | ||
433 | } | ||
434 | } | ||
435 | else // set/replace contents stored at the specified key? | ||
436 | { | ||
437 | lua_Integer count = lua_gettop( L) - 2; // number of items we want to store | ||
438 | keeper_fifo* fifo; // fifos key [val [, ...]] | ||
439 | lua_pushvalue( L, 2); // fifos key [val [, ...]] key | ||
440 | lua_rawget( L, 1); // fifos key [val [, ...]] fifo|nil | ||
441 | fifo = (keeper_fifo*) lua_touserdata( L, -1); | ||
442 | if( fifo == NULL) // can be NULL if we store a value at a new key | ||
443 | { // fifos key [val [, ...]] nil | ||
444 | // no need to wake writers in that case, because a writer can't wait on an inexistent key | ||
445 | lua_pop( L, 1); // fifos key [val [, ...]] | ||
446 | fifo_new( L); // fifos key [val [, ...]] fifo | ||
447 | lua_pushvalue( L, 2); // fifos key [val [, ...]] fifo key | ||
448 | lua_pushvalue( L, -2); // fifos key [val [, ...]] fifo key fifo | ||
449 | lua_rawset( L, 1); // fifos key [val [, ...]] fifo | ||
450 | } | ||
451 | else // the fifo exists, we just want to update its contents | ||
452 | { // fifos key [val [, ...]] fifo | ||
453 | // we create room if the fifo was full but it is no longer the case | ||
454 | should_wake_writers = (fifo->limit > 0) && (fifo->count >= fifo->limit) && (count < fifo->limit); | ||
455 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! | ||
456 | lua_newtable( L); // fifos key [val [, ...]] fifo {} | ||
457 | lua_setiuservalue( L, -2, CONTENTS_TABLE); // fifos key [val [, ...]] fifo | ||
458 | fifo->first = 1; | ||
459 | fifo->count = 0; | ||
460 | } | ||
461 | fifo = prepare_fifo_access( L, -1); | ||
462 | // move the fifo below the values we want to store | ||
463 | lua_insert( L, 3); // fifos key fifo [val [, ...]] | ||
464 | fifo_push( L, fifo, count); // fifos key fifo | ||
465 | } | ||
466 | return should_wake_writers ? (lua_pushboolean( L, 1), 1) : 0; | ||
467 | } | ||
468 | |||
469 | // in: linda_ud key [count] | ||
470 | // out: at most <count> values | ||
471 | int keepercall_get( lua_State* L) | ||
472 | { | ||
473 | keeper_fifo* fifo; | ||
474 | lua_Integer count = 1; | ||
475 | if( lua_gettop( L) == 3) // ud key count | ||
476 | { | ||
477 | count = lua_tointeger( L, 3); | ||
478 | lua_pop( L, 1); // ud key | ||
479 | } | ||
480 | push_table( L, 1); // ud key fifos | ||
481 | lua_replace( L, 1); // fifos key | ||
482 | lua_rawget( L, 1); // fifos fifo | ||
483 | fifo = prepare_fifo_access( L, -1); // fifos fifo | ||
484 | if( fifo != NULL && fifo->count > 0) | ||
485 | { | ||
486 | lua_remove( L, 1); // fifo | ||
487 | count = __min( count, fifo->count); | ||
488 | // read <count> value off the fifo | ||
489 | fifo_peek( L, fifo, count); // fifo ... | ||
490 | return (int) count; | ||
491 | } | ||
492 | // no fifo was ever registered for this key, or it is empty | ||
493 | return 0; | ||
494 | } | ||
495 | |||
496 | // in: linda_ud [, key [, ...]] | ||
497 | int keepercall_count( lua_State* L) | ||
498 | { | ||
499 | push_table( L, 1); // ud keys fifos | ||
500 | switch( lua_gettop( L)) | ||
501 | { | ||
502 | // no key is specified: return a table giving the count of all known keys | ||
503 | case 2: // ud fifos | ||
504 | lua_newtable( L); // ud fifos out | ||
505 | lua_replace( L, 1); // out fifos | ||
506 | lua_pushnil( L); // out fifos nil | ||
507 | while( lua_next( L, 2)) // out fifos key fifo | ||
508 | { | ||
509 | keeper_fifo* fifo = prepare_fifo_access( L, -1); // out fifos key fifo | ||
510 | lua_pop( L, 1); // out fifos key | ||
511 | lua_pushvalue( L, -1); // out fifos key key | ||
512 | lua_pushinteger( L, fifo->count); // out fifos key key count | ||
513 | lua_rawset( L, -5); // out fifos key | ||
514 | } | ||
515 | lua_pop( L, 1); // out | ||
516 | break; | ||
517 | |||
518 | // 1 key is specified: return its count | ||
519 | case 3: // ud key fifos | ||
520 | { | ||
521 | keeper_fifo* fifo; | ||
522 | lua_replace( L, 1); // fifos key | ||
523 | lua_rawget( L, -2); // fifos fifo|nil | ||
524 | if( lua_isnil( L, -1)) // the key is unknown | ||
525 | { // fifos nil | ||
526 | lua_remove( L, -2); // nil | ||
527 | } | ||
528 | else // the key is known | ||
529 | { // fifos fifo | ||
530 | fifo = prepare_fifo_access( L, -1); // fifos fifo | ||
531 | lua_pushinteger( L, fifo->count); // fifos fifo count | ||
532 | lua_replace( L, -3); // count fifo | ||
533 | lua_pop( L, 1); // count | ||
534 | } | ||
535 | } | ||
536 | break; | ||
537 | |||
538 | // a variable number of keys is specified: return a table of their counts | ||
539 | default: // ud keys fifos | ||
540 | lua_newtable( L); // ud keys fifos out | ||
541 | lua_replace( L, 1); // out keys fifos | ||
542 | // shifts all keys up in the stack. potentially slow if there are a lot of them, but then it should be bearable | ||
543 | lua_insert( L, 2); // out fifos keys | ||
544 | while( lua_gettop( L) > 2) | ||
545 | { | ||
546 | keeper_fifo* fifo; | ||
547 | lua_pushvalue( L, -1); // out fifos keys key | ||
548 | lua_rawget( L, 2); // out fifos keys fifo|nil | ||
549 | fifo = prepare_fifo_access( L, -1); // out fifos keys fifo|nil | ||
550 | lua_pop( L, 1); // out fifos keys | ||
551 | if( fifo != NULL) // the key is known | ||
552 | { | ||
553 | lua_pushinteger( L, fifo->count); // out fifos keys count | ||
554 | lua_rawset( L, 1); // out fifos keys | ||
555 | } | ||
556 | else // the key is unknown | ||
557 | { | ||
558 | lua_pop( L, 1); // out fifos keys | ||
559 | } | ||
560 | } | ||
561 | lua_pop( L, 1); // out | ||
562 | } | ||
563 | ASSERT_L( lua_gettop( L) == 1); | ||
564 | return 1; | ||
565 | } | ||
566 | |||
567 | //################################################################################### | ||
568 | // Keeper API, accessed from linda methods | ||
569 | //################################################################################### | ||
570 | |||
571 | /*---=== Keeper states ===--- | ||
572 | */ | ||
573 | |||
574 | /* | ||
575 | * Pool of keeper states | ||
576 | * | ||
577 | * Access to keeper states is locked (only one OS thread at a time) so the | ||
578 | * bigger the pool, the less chances of unnecessary waits. Lindas map to the | ||
579 | * keepers randomly, by a hash. | ||
580 | */ | ||
581 | |||
582 | // called as __gc for the keepers array userdata | ||
583 | void close_keepers( Universe* U) | ||
584 | { | ||
585 | if( U->keepers != NULL) | ||
586 | { | ||
587 | int i; | ||
588 | int nbKeepers = U->keepers->nb_keepers; | ||
589 | // NOTE: imagine some keeper state N+1 currently holds a linda that uses another keeper N, and a _gc that will make use of it | ||
590 | // when keeper N+1 is closed, object is GCed, linda operation is called, which attempts to acquire keeper N, whose Lua state no longer exists | ||
591 | // in that case, the linda operation should do nothing. which means that these operations must check for keeper acquisition success | ||
592 | // which is early-outed with a U->keepers->nbKeepers null-check | ||
593 | U->keepers->nb_keepers = 0; | ||
594 | for( i = 0; i < nbKeepers; ++ i) | ||
595 | { | ||
596 | lua_State* K = U->keepers->keeper_array[i].L; | ||
597 | U->keepers->keeper_array[i].L = NULL; | ||
598 | if( K != NULL) | ||
599 | { | ||
600 | lua_close( K); | ||
601 | } | ||
602 | else | ||
603 | { | ||
604 | // detected partial init: destroy only the mutexes that got initialized properly | ||
605 | nbKeepers = i; | ||
606 | } | ||
607 | } | ||
608 | for( i = 0; i < nbKeepers; ++ i) | ||
609 | { | ||
610 | MUTEX_FREE( &U->keepers->keeper_array[i].keeper_cs); | ||
611 | } | ||
612 | // free the keeper bookkeeping structure | ||
613 | { | ||
614 | AllocatorDefinition* const allocD = &U->internal_allocator; | ||
615 | (void) allocD->allocF( allocD->allocUD, U->keepers, sizeof( Keepers) + (nbKeepers - 1) * sizeof( Keeper), 0); | ||
616 | U->keepers = NULL; | ||
617 | } | ||
618 | } | ||
619 | } | ||
620 | |||
621 | /* | ||
622 | * Initialize keeper states | ||
623 | * | ||
624 | * If there is a problem, returns NULL and pushes the error message on the stack | ||
625 | * else returns the keepers bookkeeping structure. | ||
626 | * | ||
627 | * Note: Any problems would be design flaws; the created Lua state is left | ||
628 | * unclosed, because it does not really matter. In production code, this | ||
629 | * function never fails. | ||
630 | * settings table is at position 1 on the stack | ||
631 | */ | ||
632 | void init_keepers( Universe* U, lua_State* L) | ||
633 | { | ||
634 | int i; | ||
635 | int nb_keepers; | ||
636 | |||
637 | STACK_CHECK( L, 0); // L K | ||
638 | lua_getfield( L, 1, "nb_keepers"); // nb_keepers | ||
639 | nb_keepers = (int) lua_tointeger( L, -1); | ||
640 | lua_pop( L, 1); // | ||
641 | if( nb_keepers < 1) | ||
642 | { | ||
643 | (void) luaL_error( L, "Bad number of keepers (%d)", nb_keepers); | ||
644 | } | ||
645 | |||
646 | // Keepers contains an array of 1 s_Keeper, adjust for the actual number of keeper states | ||
647 | { | ||
648 | size_t const bytes = sizeof( Keepers) + (nb_keepers - 1) * sizeof( Keeper); | ||
649 | { | ||
650 | AllocatorDefinition* const allocD = &U->internal_allocator; | ||
651 | U->keepers = (Keepers*) allocD->allocF( allocD->allocUD, NULL, 0, bytes); | ||
652 | } | ||
653 | if( U->keepers == NULL) | ||
654 | { | ||
655 | (void) luaL_error( L, "init_keepers() failed while creating keeper array; out of memory"); | ||
656 | return; | ||
657 | } | ||
658 | memset( U->keepers, 0, bytes); | ||
659 | U->keepers->nb_keepers = nb_keepers; | ||
660 | } | ||
661 | for( i = 0; i < nb_keepers; ++ i) // keepersUD | ||
662 | { | ||
663 | // note that we will leak K if we raise an error later | ||
664 | lua_State* K = create_state( U, L); | ||
665 | if( K == NULL) | ||
666 | { | ||
667 | (void) luaL_error( L, "init_keepers() failed while creating keeper states; out of memory"); | ||
668 | return; | ||
669 | } | ||
670 | |||
671 | U->keepers->keeper_array[i].L = K; | ||
672 | // we can trigger a GC from inside keeper_call(), where a keeper is acquired | ||
673 | // from there, GC can collect a linda, which would acquire the keeper again, and deadlock the thread. | ||
674 | // therefore, we need a recursive mutex. | ||
675 | MUTEX_RECURSIVE_INIT( &U->keepers->keeper_array[i].keeper_cs); | ||
676 | |||
677 | STACK_CHECK( K, 0); | ||
678 | |||
679 | // copy the universe pointer in the keeper itself | ||
680 | universe_store( K, U); | ||
681 | STACK_MID( K, 0); | ||
682 | |||
683 | // make sure 'package' is initialized in keeper states, so that we have require() | ||
684 | // this because this is needed when transferring deep userdata object | ||
685 | luaL_requiref( K, "package", luaopen_package, 1); // package | ||
686 | lua_pop( K, 1); // | ||
687 | STACK_MID( K, 0); | ||
688 | serialize_require( DEBUGSPEW_PARAM_COMMA( U) K); | ||
689 | STACK_MID( K, 0); | ||
690 | |||
691 | // copy package.path and package.cpath from the source state | ||
692 | lua_getglobal( L, "package"); // "..." keepersUD package | ||
693 | if( !lua_isnil( L, -1)) | ||
694 | { | ||
695 | // when copying with mode eLM_ToKeeper, error message is pushed at the top of the stack, not raised immediately | ||
696 | if( luaG_inter_copy_package( U, L, K, -1, eLM_ToKeeper)) | ||
697 | { | ||
698 | // if something went wrong, the error message is at the top of the stack | ||
699 | lua_remove( L, -2); // error_msg | ||
700 | (void) lua_error( L); | ||
701 | return; | ||
702 | } | ||
703 | } | ||
704 | lua_pop( L, 1); // | ||
705 | STACK_MID( L, 0); | ||
706 | |||
707 | // attempt to call on_state_create(), if we have one and it is a C function | ||
708 | // (only support a C function because we can't transfer executable Lua code in keepers) | ||
709 | // will raise an error in L in case of problem | ||
710 | call_on_state_create( U, K, L, eLM_ToKeeper); | ||
711 | |||
712 | // to see VM name in Decoda debugger | ||
713 | lua_pushfstring( K, "Keeper #%d", i + 1); // "Keeper #n" | ||
714 | lua_setglobal( K, "decoda_name"); // | ||
715 | |||
716 | // create the fifos table in the keeper state | ||
717 | REGISTRY_SET( K, FIFOS_KEY, lua_newtable( K)); | ||
718 | STACK_END( K, 0); | ||
719 | } | ||
720 | STACK_END( L, 0); | ||
721 | } | ||
722 | |||
723 | // should be called only when inside a keeper_acquire/keeper_release pair (see linda_protected_call) | ||
724 | Keeper* which_keeper(Keepers* keepers_, ptrdiff_t magic_) | ||
725 | { | ||
726 | int const nbKeepers = keepers_->nb_keepers; | ||
727 | unsigned int i = (unsigned int)((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers); | ||
728 | return &keepers_->keeper_array[i]; | ||
729 | } | ||
730 | |||
731 | Keeper* keeper_acquire( Keepers* keepers_, ptrdiff_t magic_) | ||
732 | { | ||
733 | int const nbKeepers = keepers_->nb_keepers; | ||
734 | // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers) | ||
735 | if( nbKeepers == 0) | ||
736 | { | ||
737 | return NULL; | ||
738 | } | ||
739 | else | ||
740 | { | ||
741 | /* | ||
742 | * Any hashing will do that maps pointers to 0..GNbKeepers-1 | ||
743 | * consistently. | ||
744 | * | ||
745 | * Pointers are often aligned by 8 or so - ignore the low order bits | ||
746 | * have to cast to unsigned long to avoid compilation warnings about loss of data when converting pointer-to-integer | ||
747 | */ | ||
748 | unsigned int i = (unsigned int)((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers); | ||
749 | Keeper* K = &keepers_->keeper_array[i]; | ||
750 | |||
751 | MUTEX_LOCK( &K->keeper_cs); | ||
752 | //++ K->count; | ||
753 | return K; | ||
754 | } | ||
755 | } | ||
756 | |||
757 | void keeper_release( Keeper* K) | ||
758 | { | ||
759 | //-- K->count; | ||
760 | if( K) MUTEX_UNLOCK( &K->keeper_cs); | ||
761 | } | ||
762 | |||
763 | void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mode_) | ||
764 | { | ||
765 | int i, n = lua_gettop( L); | ||
766 | for( i = val_i_; i <= n; ++ i) | ||
767 | { | ||
768 | if( mode_ == eLM_ToKeeper) | ||
769 | { | ||
770 | if( lua_isnil( L, i)) | ||
771 | { | ||
772 | push_unique_key( L, NIL_SENTINEL); | ||
773 | lua_replace( L, i); | ||
774 | } | ||
775 | } | ||
776 | else | ||
777 | { | ||
778 | if( equal_unique_key( L, i, NIL_SENTINEL)) | ||
779 | { | ||
780 | lua_pushnil( L); | ||
781 | lua_replace( L, i); | ||
782 | } | ||
783 | } | ||
784 | } | ||
785 | } | ||
786 | |||
787 | /* | ||
788 | * Call a function ('func_name') in the keeper state, and pass on the returned | ||
789 | * values to 'L'. | ||
790 | * | ||
791 | * 'linda': deep Linda pointer (used only as a unique table key, first parameter) | ||
792 | * 'starting_index': first of the rest of parameters (none if 0) | ||
793 | * | ||
794 | * Returns: number of return values (pushed to 'L') or -1 in case of error | ||
795 | */ | ||
796 | int keeper_call( Universe* U, lua_State* K, keeper_api_t func_, lua_State* L, void* linda, uint_t starting_index) | ||
797 | { | ||
798 | int const args = starting_index ? (lua_gettop( L) - starting_index + 1) : 0; | ||
799 | int const Ktos = lua_gettop( K); | ||
800 | int retvals = -1; | ||
801 | |||
802 | STACK_GROW( K, 2); | ||
803 | |||
804 | PUSH_KEEPER_FUNC( K, func_); | ||
805 | |||
806 | lua_pushlightuserdata( K, linda); | ||
807 | |||
808 | if( (args == 0) || luaG_inter_copy( U, L, K, args, eLM_ToKeeper) == 0) // L->K | ||
809 | { | ||
810 | lua_call( K, 1 + args, LUA_MULTRET); | ||
811 | |||
812 | retvals = lua_gettop( K) - Ktos; | ||
813 | // note that this can raise a luaL_error while the keeper state (and its mutex) is acquired | ||
814 | // this may interrupt a lane, causing the destruction of the underlying OS thread | ||
815 | // after this, another lane making use of this keeper can get an error code from the mutex-locking function | ||
816 | // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread) | ||
817 | if( (retvals > 0) && luaG_inter_move( U, K, L, retvals, eLM_FromKeeper) != 0) // K->L | ||
818 | { | ||
819 | retvals = -1; | ||
820 | } | ||
821 | } | ||
822 | // whatever happens, restore the stack to where it was at the origin | ||
823 | lua_settop( K, Ktos); | ||
824 | return retvals; | ||
825 | } | ||