aboutsummaryrefslogtreecommitdiff
path: root/src/keeper.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/keeper.c')
-rw-r--r--src/keeper.c825
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
62typedef struct
63{
64 lua_Integer first;
65 lua_Integer count;
66 lua_Integer limit;
67} keeper_fifo;
68
69static int const CONTENTS_TABLE = 1;
70
71// replaces the fifo ud by its uservalue on the stack
72static 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}
88static 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
103static 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
122static 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)
134static 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/
169static DECLARE_CONST_UNIQUE_KEY( FIFOS_KEY, 0xdce50bbc351cd465);
170static 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
192int 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
242int 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
258int 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
294int 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]
329int 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
362int 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
398int 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
471int 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 [, ...]]
497int 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
583void 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 */
632void 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)
724Keeper* 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
731Keeper* 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
757void keeper_release( Keeper* K)
758{
759 //-- K->count;
760 if( K) MUTEX_UNLOCK( &K->keeper_cs);
761}
762
763void 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*/
796int 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}