diff options
Diffstat (limited to 'src/linda.cpp')
-rw-r--r-- | src/linda.cpp | 945 |
1 files changed, 945 insertions, 0 deletions
diff --git a/src/linda.cpp b/src/linda.cpp new file mode 100644 index 0000000..eac6458 --- /dev/null +++ b/src/linda.cpp | |||
@@ -0,0 +1,945 @@ | |||
1 | /* | ||
2 | * LINDA.C Copyright (c) 2018, Benoit Germain | ||
3 | * | ||
4 | * Linda deep userdata. | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | =============================================================================== | ||
9 | |||
10 | Copyright (C) 2018 benoit Germain <bnt.germain@gmail.com> | ||
11 | |||
12 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
13 | of this software and associated documentation files (the "Software"), to deal | ||
14 | in the Software without restriction, including without limitation the rights | ||
15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
16 | copies of the Software, and to permit persons to whom the Software is | ||
17 | furnished to do so, subject to the following conditions: | ||
18 | |||
19 | The above copyright notice and this permission notice shall be included in | ||
20 | all copies or substantial portions of the Software. | ||
21 | |||
22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
28 | THE SOFTWARE. | ||
29 | |||
30 | =============================================================================== | ||
31 | */ | ||
32 | |||
33 | #include <stdlib.h> | ||
34 | #include <string.h> | ||
35 | #include <assert.h> | ||
36 | |||
37 | #include "threading.h" | ||
38 | #include "compat.h" | ||
39 | #include "tools.h" | ||
40 | #include "universe.h" | ||
41 | #include "keeper.h" | ||
42 | #include "deep.h" | ||
43 | #include "lanes_private.h" | ||
44 | |||
45 | /* | ||
46 | * Actual data is kept within a keeper state, which is hashed by the 's_Linda' | ||
47 | * pointer (which is same to all userdatas pointing to it). | ||
48 | */ | ||
49 | struct s_Linda | ||
50 | { | ||
51 | DeepPrelude prelude; // Deep userdata MUST start with this header | ||
52 | SIGNAL_T read_happened; | ||
53 | SIGNAL_T write_happened; | ||
54 | Universe* U; // the universe this linda belongs to | ||
55 | ptrdiff_t group; // a group to control keeper allocation between lindas | ||
56 | enum e_cancel_request simulate_cancel; | ||
57 | char name[1]; | ||
58 | }; | ||
59 | #define LINDA_KEEPER_HASHSEED( linda) (linda->group ? linda->group : (ptrdiff_t)linda) | ||
60 | |||
61 | static void* linda_id( lua_State*, DeepOp); | ||
62 | |||
63 | static inline struct s_Linda* lua_toLinda( lua_State* L, int idx_) | ||
64 | { | ||
65 | struct s_Linda* linda = (struct s_Linda*) luaG_todeep( L, linda_id, idx_); | ||
66 | luaL_argcheck( L, linda != NULL, idx_, "expecting a linda object"); | ||
67 | return linda; | ||
68 | } | ||
69 | |||
70 | static void check_key_types( lua_State* L, int start_, int end_) | ||
71 | { | ||
72 | int i; | ||
73 | for( i = start_; i <= end_; ++ i) | ||
74 | { | ||
75 | int t = lua_type( L, i); | ||
76 | if( t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA) | ||
77 | { | ||
78 | continue; | ||
79 | } | ||
80 | (void) luaL_error( L, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", i); | ||
81 | } | ||
82 | } | ||
83 | |||
84 | LUAG_FUNC( linda_protected_call) | ||
85 | { | ||
86 | int rc = LUA_OK; | ||
87 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
88 | |||
89 | // acquire the keeper | ||
90 | Keeper* K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED(linda)); | ||
91 | lua_State* KL = K ? K->L : NULL; // need to do this for 'STACK_CHECK' | ||
92 | if( KL == NULL) return 0; | ||
93 | |||
94 | // retrieve the actual function to be called and move it before the arguments | ||
95 | lua_pushvalue( L, lua_upvalueindex( 1)); | ||
96 | lua_insert( L, 1); | ||
97 | // do a protected call | ||
98 | rc = lua_pcall( L, lua_gettop( L) - 1, LUA_MULTRET, 0); | ||
99 | |||
100 | // release the keeper | ||
101 | keeper_release( K); | ||
102 | |||
103 | // if there was an error, forward it | ||
104 | if( rc != LUA_OK) | ||
105 | { | ||
106 | return lua_error( L); | ||
107 | } | ||
108 | // return whatever the actual operation provided | ||
109 | return lua_gettop( L); | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | * bool= linda_send( linda_ud, [timeout_secs=-1,] [linda.null,] key_num|str|bool|lightuserdata, ... ) | ||
114 | * | ||
115 | * Send one or more values to a Linda. If there is a limit, all values must fit. | ||
116 | * | ||
117 | * Returns: 'true' if the value was queued | ||
118 | * 'false' for timeout (only happens when the queue size is limited) | ||
119 | * nil, CANCEL_ERROR if cancelled | ||
120 | */ | ||
121 | LUAG_FUNC( linda_send) | ||
122 | { | ||
123 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
124 | bool_t ret = FALSE; | ||
125 | enum e_cancel_request cancel = CANCEL_NONE; | ||
126 | int pushed; | ||
127 | time_d timeout = -1.0; | ||
128 | uint_t key_i = 2; // index of first key, if timeout not there | ||
129 | bool_t as_nil_sentinel; // if not NULL, send() will silently send a single nil if nothing is provided | ||
130 | |||
131 | if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion | ||
132 | { | ||
133 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); | ||
134 | ++ key_i; | ||
135 | } | ||
136 | else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key | ||
137 | { | ||
138 | ++ key_i; | ||
139 | } | ||
140 | |||
141 | as_nil_sentinel = equal_unique_key( L, key_i, NIL_SENTINEL); | ||
142 | if( as_nil_sentinel) | ||
143 | { | ||
144 | // the real key to send data to is after the NIL_SENTINEL marker | ||
145 | ++ key_i; | ||
146 | } | ||
147 | |||
148 | // make sure the key is of a valid type | ||
149 | check_key_types( L, key_i, key_i); | ||
150 | |||
151 | STACK_GROW( L, 1); | ||
152 | |||
153 | // make sure there is something to send | ||
154 | if( (uint_t)lua_gettop( L) == key_i) | ||
155 | { | ||
156 | if( as_nil_sentinel) | ||
157 | { | ||
158 | // send a single nil if nothing is provided | ||
159 | push_unique_key( L, NIL_SENTINEL); | ||
160 | } | ||
161 | else | ||
162 | { | ||
163 | return luaL_error( L, "no data to send"); | ||
164 | } | ||
165 | } | ||
166 | |||
167 | // convert nils to some special non-nil sentinel in sent values | ||
168 | keeper_toggle_nil_sentinels( L, key_i + 1, eLM_ToKeeper); | ||
169 | |||
170 | { | ||
171 | bool_t try_again = TRUE; | ||
172 | Lane* const s = get_lane_from_registry( L); | ||
173 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
174 | lua_State* KL = K ? K->L : NULL; // need to do this for 'STACK_CHECK' | ||
175 | if( KL == NULL) return 0; | ||
176 | STACK_CHECK( KL, 0); | ||
177 | for( ;;) | ||
178 | { | ||
179 | if( s != NULL) | ||
180 | { | ||
181 | cancel = s->cancel_request; | ||
182 | } | ||
183 | cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; | ||
184 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | ||
185 | if( !try_again || cancel != CANCEL_NONE) | ||
186 | { | ||
187 | pushed = 0; | ||
188 | break; | ||
189 | } | ||
190 | |||
191 | STACK_MID( KL, 0); | ||
192 | pushed = keeper_call( linda->U, KL, KEEPER_API( send), L, linda, key_i); | ||
193 | if( pushed < 0) | ||
194 | { | ||
195 | break; | ||
196 | } | ||
197 | ASSERT_L( pushed == 1); | ||
198 | |||
199 | ret = lua_toboolean( L, -1); | ||
200 | lua_pop( L, 1); | ||
201 | |||
202 | if( ret) | ||
203 | { | ||
204 | // Wake up ALL waiting threads | ||
205 | SIGNAL_ALL( &linda->write_happened); | ||
206 | break; | ||
207 | } | ||
208 | |||
209 | // instant timout to bypass the wait syscall | ||
210 | if( timeout == 0.0) | ||
211 | { | ||
212 | break; /* no wait; instant timeout */ | ||
213 | } | ||
214 | |||
215 | // storage limit hit, wait until timeout or signalled that we should try again | ||
216 | { | ||
217 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | ||
218 | if( s != NULL) | ||
219 | { | ||
220 | // change status of lane to "waiting" | ||
221 | prev_status = s->status; // RUNNING, most likely | ||
222 | ASSERT_L( prev_status == RUNNING); // but check, just in case | ||
223 | s->status = WAITING; | ||
224 | ASSERT_L( s->waiting_on == NULL); | ||
225 | s->waiting_on = &linda->read_happened; | ||
226 | } | ||
227 | // could not send because no room: wait until some data was read before trying again, or until timeout is reached | ||
228 | try_again = SIGNAL_WAIT( &linda->read_happened, &K->keeper_cs, timeout); | ||
229 | if( s != NULL) | ||
230 | { | ||
231 | s->waiting_on = NULL; | ||
232 | s->status = prev_status; | ||
233 | } | ||
234 | } | ||
235 | } | ||
236 | STACK_END( KL, 0); | ||
237 | } | ||
238 | |||
239 | if( pushed < 0) | ||
240 | { | ||
241 | return luaL_error( L, "tried to copy unsupported types"); | ||
242 | } | ||
243 | |||
244 | switch( cancel) | ||
245 | { | ||
246 | case CANCEL_SOFT: | ||
247 | // if user wants to soft-cancel, the call returns lanes.cancel_error | ||
248 | push_unique_key( L, CANCEL_ERROR); | ||
249 | return 1; | ||
250 | |||
251 | case CANCEL_HARD: | ||
252 | // raise an error interrupting execution only in case of hard cancel | ||
253 | return cancel_error( L); // raises an error and doesn't return | ||
254 | |||
255 | default: | ||
256 | lua_pushboolean( L, ret); // true (success) or false (timeout) | ||
257 | return 1; | ||
258 | } | ||
259 | } | ||
260 | |||
261 | |||
262 | /* | ||
263 | * 2 modes of operation | ||
264 | * [val, key]= linda_receive( linda_ud, [timeout_secs_num=-1], key_num|str|bool|lightuserdata [, ...] ) | ||
265 | * Consumes a single value from the Linda, in any key. | ||
266 | * Returns: received value (which is consumed from the slot), and the key which had it | ||
267 | |||
268 | * [val1, ... valCOUNT]= linda_receive( linda_ud, [timeout_secs_num=-1], linda.batched, key_num|str|bool|lightuserdata, min_COUNT[, max_COUNT]) | ||
269 | * Consumes between min_COUNT and max_COUNT values from the linda, from a single key. | ||
270 | * returns the actual consumed values, or nil if there weren't enough values to consume | ||
271 | * | ||
272 | */ | ||
273 | #define BATCH_SENTINEL "270e6c9d-280f-4983-8fee-a7ecdda01475" | ||
274 | LUAG_FUNC( linda_receive) | ||
275 | { | ||
276 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
277 | int pushed, expected_pushed_min, expected_pushed_max; | ||
278 | enum e_cancel_request cancel = CANCEL_NONE; | ||
279 | keeper_api_t keeper_receive; | ||
280 | |||
281 | time_d timeout = -1.0; | ||
282 | uint_t key_i = 2; | ||
283 | |||
284 | if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion | ||
285 | { | ||
286 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); | ||
287 | ++ key_i; | ||
288 | } | ||
289 | else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key | ||
290 | { | ||
291 | ++ key_i; | ||
292 | } | ||
293 | |||
294 | // are we in batched mode? | ||
295 | { | ||
296 | int is_batched; | ||
297 | lua_pushliteral( L, BATCH_SENTINEL); | ||
298 | is_batched = lua501_equal( L, key_i, -1); | ||
299 | lua_pop( L, 1); | ||
300 | if( is_batched) | ||
301 | { | ||
302 | // no need to pass linda.batched in the keeper state | ||
303 | ++ key_i; | ||
304 | // make sure the keys are of a valid type | ||
305 | check_key_types( L, key_i, key_i); | ||
306 | // receive multiple values from a single slot | ||
307 | keeper_receive = KEEPER_API( receive_batched); | ||
308 | // we expect a user-defined amount of return value | ||
309 | expected_pushed_min = (int)luaL_checkinteger( L, key_i + 1); | ||
310 | expected_pushed_max = (int)luaL_optinteger( L, key_i + 2, expected_pushed_min); | ||
311 | // don't forget to count the key in addition to the values | ||
312 | ++ expected_pushed_min; | ||
313 | ++ expected_pushed_max; | ||
314 | if( expected_pushed_min > expected_pushed_max) | ||
315 | { | ||
316 | return luaL_error( L, "batched min/max error"); | ||
317 | } | ||
318 | } | ||
319 | else | ||
320 | { | ||
321 | // make sure the keys are of a valid type | ||
322 | check_key_types( L, key_i, lua_gettop( L)); | ||
323 | // receive a single value, checking multiple slots | ||
324 | keeper_receive = KEEPER_API( receive); | ||
325 | // we expect a single (value, key) pair of returned values | ||
326 | expected_pushed_min = expected_pushed_max = 2; | ||
327 | } | ||
328 | } | ||
329 | |||
330 | { | ||
331 | bool_t try_again = TRUE; | ||
332 | Lane* const s = get_lane_from_registry( L); | ||
333 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
334 | if( K == NULL) return 0; | ||
335 | for( ;;) | ||
336 | { | ||
337 | if( s != NULL) | ||
338 | { | ||
339 | cancel = s->cancel_request; | ||
340 | } | ||
341 | cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; | ||
342 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | ||
343 | if( !try_again || cancel != CANCEL_NONE) | ||
344 | { | ||
345 | pushed = 0; | ||
346 | break; | ||
347 | } | ||
348 | |||
349 | // all arguments of receive() but the first are passed to the keeper's receive function | ||
350 | pushed = keeper_call( linda->U, K->L, keeper_receive, L, linda, key_i); | ||
351 | if( pushed < 0) | ||
352 | { | ||
353 | break; | ||
354 | } | ||
355 | if( pushed > 0) | ||
356 | { | ||
357 | ASSERT_L( pushed >= expected_pushed_min && pushed <= expected_pushed_max); | ||
358 | // replace sentinels with real nils | ||
359 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper); | ||
360 | // To be done from within the 'K' locking area | ||
361 | // | ||
362 | SIGNAL_ALL( &linda->read_happened); | ||
363 | break; | ||
364 | } | ||
365 | |||
366 | if( timeout == 0.0) | ||
367 | { | ||
368 | break; /* instant timeout */ | ||
369 | } | ||
370 | |||
371 | // nothing received, wait until timeout or signalled that we should try again | ||
372 | { | ||
373 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | ||
374 | if( s != NULL) | ||
375 | { | ||
376 | // change status of lane to "waiting" | ||
377 | prev_status = s->status; // RUNNING, most likely | ||
378 | ASSERT_L( prev_status == RUNNING); // but check, just in case | ||
379 | s->status = WAITING; | ||
380 | ASSERT_L( s->waiting_on == NULL); | ||
381 | s->waiting_on = &linda->write_happened; | ||
382 | } | ||
383 | // not enough data to read: wakeup when data was sent, or when timeout is reached | ||
384 | try_again = SIGNAL_WAIT( &linda->write_happened, &K->keeper_cs, timeout); | ||
385 | if( s != NULL) | ||
386 | { | ||
387 | s->waiting_on = NULL; | ||
388 | s->status = prev_status; | ||
389 | } | ||
390 | } | ||
391 | } | ||
392 | } | ||
393 | |||
394 | if( pushed < 0) | ||
395 | { | ||
396 | return luaL_error( L, "tried to copy unsupported types"); | ||
397 | } | ||
398 | |||
399 | switch( cancel) | ||
400 | { | ||
401 | case CANCEL_SOFT: | ||
402 | // if user wants to soft-cancel, the call returns CANCEL_ERROR | ||
403 | push_unique_key( L, CANCEL_ERROR); | ||
404 | return 1; | ||
405 | |||
406 | case CANCEL_HARD: | ||
407 | // raise an error interrupting execution only in case of hard cancel | ||
408 | return cancel_error( L); // raises an error and doesn't return | ||
409 | |||
410 | default: | ||
411 | return pushed; | ||
412 | } | ||
413 | } | ||
414 | |||
415 | |||
416 | /* | ||
417 | * [true|lanes.cancel_error] = linda_set( linda_ud, key_num|str|bool|lightuserdata [, value [, ...]]) | ||
418 | * | ||
419 | * Set one or more value to Linda. | ||
420 | * TODO: what do we do if we set to non-nil and limit is 0? | ||
421 | * | ||
422 | * Existing slot value is replaced, and possible queued entries removed. | ||
423 | */ | ||
424 | LUAG_FUNC( linda_set) | ||
425 | { | ||
426 | struct s_Linda* const linda = lua_toLinda( L, 1); | ||
427 | int pushed; | ||
428 | bool_t has_value = lua_gettop( L) > 2; | ||
429 | |||
430 | // make sure the key is of a valid type (throws an error if not the case) | ||
431 | check_key_types( L, 2, 2); | ||
432 | |||
433 | { | ||
434 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
435 | |||
436 | if( linda->simulate_cancel == CANCEL_NONE) | ||
437 | { | ||
438 | if( has_value) | ||
439 | { | ||
440 | // convert nils to some special non-nil sentinel in sent values | ||
441 | keeper_toggle_nil_sentinels( L, 3, eLM_ToKeeper); | ||
442 | } | ||
443 | pushed = keeper_call( linda->U, K->L, KEEPER_API( set), L, linda, 2); | ||
444 | if( pushed >= 0) // no error? | ||
445 | { | ||
446 | ASSERT_L( pushed == 0 || pushed == 1); | ||
447 | |||
448 | if( has_value) | ||
449 | { | ||
450 | // we put some data in the slot, tell readers that they should wake | ||
451 | SIGNAL_ALL( &linda->write_happened); // To be done from within the 'K' locking area | ||
452 | } | ||
453 | if( pushed == 1) | ||
454 | { | ||
455 | // the key was full, but it is no longer the case, tell writers they should wake | ||
456 | ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); | ||
457 | SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area | ||
458 | } | ||
459 | } | ||
460 | } | ||
461 | else // linda is cancelled | ||
462 | { | ||
463 | // do nothing and return lanes.cancel_error | ||
464 | push_unique_key( L, CANCEL_ERROR); | ||
465 | pushed = 1; | ||
466 | } | ||
467 | } | ||
468 | |||
469 | // must trigger any error after keeper state has been released | ||
470 | return (pushed < 0) ? luaL_error( L, "tried to copy unsupported types") : pushed; | ||
471 | } | ||
472 | |||
473 | |||
474 | /* | ||
475 | * [val] = linda_count( linda_ud, [key [, ...]]) | ||
476 | * | ||
477 | * Get a count of the pending elements in the specified keys | ||
478 | */ | ||
479 | LUAG_FUNC( linda_count) | ||
480 | { | ||
481 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
482 | int pushed; | ||
483 | |||
484 | // make sure the keys are of a valid type | ||
485 | check_key_types( L, 2, lua_gettop( L)); | ||
486 | |||
487 | { | ||
488 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
489 | pushed = keeper_call( linda->U, K->L, KEEPER_API( count), L, linda, 2); | ||
490 | if( pushed < 0) | ||
491 | { | ||
492 | return luaL_error( L, "tried to count an invalid key"); | ||
493 | } | ||
494 | } | ||
495 | return pushed; | ||
496 | } | ||
497 | |||
498 | |||
499 | /* | ||
500 | * [val [, ...]] = linda_get( linda_ud, key_num|str|bool|lightuserdata [, count = 1]) | ||
501 | * | ||
502 | * Get one or more values from Linda. | ||
503 | */ | ||
504 | LUAG_FUNC( linda_get) | ||
505 | { | ||
506 | struct s_Linda* const linda = lua_toLinda( L, 1); | ||
507 | int pushed; | ||
508 | lua_Integer count = luaL_optinteger( L, 3, 1); | ||
509 | luaL_argcheck( L, count >= 1, 3, "count should be >= 1"); | ||
510 | luaL_argcheck( L, lua_gettop( L) <= 3, 4, "too many arguments"); | ||
511 | |||
512 | // make sure the key is of a valid type (throws an error if not the case) | ||
513 | check_key_types( L, 2, 2); | ||
514 | { | ||
515 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
516 | |||
517 | if( linda->simulate_cancel == CANCEL_NONE) | ||
518 | { | ||
519 | pushed = keeper_call( linda->U, K->L, KEEPER_API( get), L, linda, 2); | ||
520 | if( pushed > 0) | ||
521 | { | ||
522 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper); | ||
523 | } | ||
524 | } | ||
525 | else // linda is cancelled | ||
526 | { | ||
527 | // do nothing and return lanes.cancel_error | ||
528 | push_unique_key( L, CANCEL_ERROR); | ||
529 | pushed = 1; | ||
530 | } | ||
531 | // an error can be raised if we attempt to read an unregistered function | ||
532 | if( pushed < 0) | ||
533 | { | ||
534 | return luaL_error( L, "tried to copy unsupported types"); | ||
535 | } | ||
536 | } | ||
537 | |||
538 | return pushed; | ||
539 | } | ||
540 | |||
541 | |||
542 | /* | ||
543 | * [true] = linda_limit( linda_ud, key_num|str|bool|lightuserdata, int) | ||
544 | * | ||
545 | * Set limit to 1 Linda keys. | ||
546 | * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so | ||
547 | */ | ||
548 | LUAG_FUNC( linda_limit) | ||
549 | { | ||
550 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
551 | int pushed; | ||
552 | |||
553 | // make sure we got 3 arguments: the linda, a key and a limit | ||
554 | luaL_argcheck( L, lua_gettop( L) == 3, 2, "wrong number of arguments"); | ||
555 | // make sure we got a numeric limit | ||
556 | luaL_checknumber( L, 3); | ||
557 | // make sure the key is of a valid type | ||
558 | check_key_types( L, 2, 2); | ||
559 | |||
560 | { | ||
561 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
562 | |||
563 | if( linda->simulate_cancel == CANCEL_NONE) | ||
564 | { | ||
565 | pushed = keeper_call( linda->U, K->L, KEEPER_API( limit), L, linda, 2); | ||
566 | ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads | ||
567 | if( pushed == 1) | ||
568 | { | ||
569 | ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); | ||
570 | SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area | ||
571 | } | ||
572 | } | ||
573 | else // linda is cancelled | ||
574 | { | ||
575 | // do nothing and return lanes.cancel_error | ||
576 | push_unique_key( L, CANCEL_ERROR); | ||
577 | pushed = 1; | ||
578 | } | ||
579 | } | ||
580 | // propagate pushed boolean if any | ||
581 | return pushed; | ||
582 | } | ||
583 | |||
584 | |||
585 | /* | ||
586 | * (void) = linda_cancel( linda_ud, "read"|"write"|"both"|"none") | ||
587 | * | ||
588 | * Signal linda so that waiting threads wake up as if their own lane was cancelled | ||
589 | */ | ||
590 | LUAG_FUNC( linda_cancel) | ||
591 | { | ||
592 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
593 | char const* who = luaL_optstring( L, 2, "both"); | ||
594 | |||
595 | // make sure we got 3 arguments: the linda, a key and a limit | ||
596 | luaL_argcheck( L, lua_gettop( L) <= 2, 2, "wrong number of arguments"); | ||
597 | |||
598 | linda->simulate_cancel = CANCEL_SOFT; | ||
599 | if( strcmp( who, "both") == 0) // tell everyone writers to wake up | ||
600 | { | ||
601 | SIGNAL_ALL( &linda->write_happened); | ||
602 | SIGNAL_ALL( &linda->read_happened); | ||
603 | } | ||
604 | else if( strcmp( who, "none") == 0) // reset flag | ||
605 | { | ||
606 | linda->simulate_cancel = CANCEL_NONE; | ||
607 | } | ||
608 | else if( strcmp( who, "read") == 0) // tell blocked readers to wake up | ||
609 | { | ||
610 | SIGNAL_ALL( &linda->write_happened); | ||
611 | } | ||
612 | else if( strcmp( who, "write") == 0) // tell blocked writers to wake up | ||
613 | { | ||
614 | SIGNAL_ALL( &linda->read_happened); | ||
615 | } | ||
616 | else | ||
617 | { | ||
618 | return luaL_error( L, "unknown wake hint '%s'", who); | ||
619 | } | ||
620 | return 0; | ||
621 | } | ||
622 | |||
623 | |||
624 | /* | ||
625 | * lightuserdata= linda_deep( linda_ud ) | ||
626 | * | ||
627 | * Return the 'deep' userdata pointer, identifying the Linda. | ||
628 | * | ||
629 | * This is needed for using Lindas as key indices (timer system needs it); | ||
630 | * separately created proxies of the same underlying deep object will have | ||
631 | * different userdata and won't be known to be essentially the same deep one | ||
632 | * without this. | ||
633 | */ | ||
634 | LUAG_FUNC( linda_deep) | ||
635 | { | ||
636 | struct s_Linda* linda= lua_toLinda( L, 1); | ||
637 | lua_pushlightuserdata( L, linda); // just the address | ||
638 | return 1; | ||
639 | } | ||
640 | |||
641 | |||
642 | /* | ||
643 | * string = linda:__tostring( linda_ud) | ||
644 | * | ||
645 | * Return the stringification of a linda | ||
646 | * | ||
647 | * Useful for concatenation or debugging purposes | ||
648 | */ | ||
649 | |||
650 | static int linda_tostring( lua_State* L, int idx_, bool_t opt_) | ||
651 | { | ||
652 | struct s_Linda* linda = (struct s_Linda*) luaG_todeep( L, linda_id, idx_); | ||
653 | if( !opt_) | ||
654 | { | ||
655 | luaL_argcheck( L, linda, idx_, "expecting a linda object"); | ||
656 | } | ||
657 | if( linda != NULL) | ||
658 | { | ||
659 | char text[128]; | ||
660 | int len; | ||
661 | if( linda->name[0]) | ||
662 | len = sprintf( text, "Linda: %.*s", (int)sizeof(text) - 8, linda->name); | ||
663 | else | ||
664 | len = sprintf( text, "Linda: %p", linda); | ||
665 | lua_pushlstring( L, text, len); | ||
666 | return 1; | ||
667 | } | ||
668 | return 0; | ||
669 | } | ||
670 | |||
671 | LUAG_FUNC( linda_tostring) | ||
672 | { | ||
673 | return linda_tostring( L, 1, FALSE); | ||
674 | } | ||
675 | |||
676 | |||
677 | /* | ||
678 | * string = linda:__concat( a, b) | ||
679 | * | ||
680 | * Return the concatenation of a pair of items, one of them being a linda | ||
681 | * | ||
682 | * Useful for concatenation or debugging purposes | ||
683 | */ | ||
684 | LUAG_FUNC( linda_concat) | ||
685 | { // linda1? linda2? | ||
686 | bool_t atLeastOneLinda = FALSE; | ||
687 | // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both. | ||
688 | if( linda_tostring( L, 1, TRUE)) | ||
689 | { | ||
690 | atLeastOneLinda = TRUE; | ||
691 | lua_replace( L, 1); | ||
692 | } | ||
693 | if( linda_tostring( L, 2, TRUE)) | ||
694 | { | ||
695 | atLeastOneLinda = TRUE; | ||
696 | lua_replace( L, 2); | ||
697 | } | ||
698 | if( !atLeastOneLinda) // should not be possible | ||
699 | { | ||
700 | return luaL_error( L, "internal error: linda_concat called on non-Linda"); | ||
701 | } | ||
702 | lua_concat( L, 2); | ||
703 | return 1; | ||
704 | } | ||
705 | |||
706 | /* | ||
707 | * table = linda:dump() | ||
708 | * return a table listing all pending data inside the linda | ||
709 | */ | ||
710 | LUAG_FUNC( linda_dump) | ||
711 | { | ||
712 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
713 | ASSERT_L( linda->U == universe_get( L)); | ||
714 | return keeper_push_linda_storage( linda->U, L, linda, LINDA_KEEPER_HASHSEED( linda)); | ||
715 | } | ||
716 | |||
717 | /* | ||
718 | * table = linda:dump() | ||
719 | * return a table listing all pending data inside the linda | ||
720 | */ | ||
721 | LUAG_FUNC( linda_towatch) | ||
722 | { | ||
723 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
724 | int pushed; | ||
725 | ASSERT_L( linda->U == universe_get( L)); | ||
726 | pushed = keeper_push_linda_storage( linda->U, L, linda, LINDA_KEEPER_HASHSEED( linda)); | ||
727 | if( pushed == 0) | ||
728 | { | ||
729 | // if the linda is empty, don't return nil | ||
730 | pushed = linda_tostring( L, 1, FALSE); | ||
731 | } | ||
732 | return pushed; | ||
733 | } | ||
734 | |||
735 | /* | ||
736 | * Identity function of a shared userdata object. | ||
737 | * | ||
738 | * lightuserdata= linda_id( "new" [, ...] ) | ||
739 | * = linda_id( "delete", lightuserdata ) | ||
740 | * | ||
741 | * Creation and cleanup of actual 'deep' objects. 'luaG_...' will wrap them into | ||
742 | * regular userdata proxies, per each state using the deep data. | ||
743 | * | ||
744 | * tbl= linda_id( "metatable" ) | ||
745 | * | ||
746 | * Returns a metatable for the proxy objects ('__gc' method not needed; will | ||
747 | * be added by 'luaG_...') | ||
748 | * | ||
749 | * string= linda_id( "module") | ||
750 | * | ||
751 | * Returns the name of the module that a state should require | ||
752 | * in order to keep a handle on the shared library that exported the idfunc | ||
753 | * | ||
754 | * = linda_id( str, ... ) | ||
755 | * | ||
756 | * For any other strings, the ID function must not react at all. This allows | ||
757 | * future extensions of the system. | ||
758 | */ | ||
759 | static void* linda_id( lua_State* L, DeepOp op_) | ||
760 | { | ||
761 | switch( op_) | ||
762 | { | ||
763 | case eDO_new: | ||
764 | { | ||
765 | struct s_Linda* s; | ||
766 | size_t name_len = 0; | ||
767 | char const* linda_name = NULL; | ||
768 | unsigned long linda_group = 0; | ||
769 | // should have a string and/or a number of the stack as parameters (name and group) | ||
770 | switch( lua_gettop( L)) | ||
771 | { | ||
772 | default: // 0 | ||
773 | break; | ||
774 | |||
775 | case 1: // 1 parameter, either a name or a group | ||
776 | if( lua_type( L, -1) == LUA_TSTRING) | ||
777 | { | ||
778 | linda_name = lua_tolstring( L, -1, &name_len); | ||
779 | } | ||
780 | else | ||
781 | { | ||
782 | linda_group = (unsigned long) lua_tointeger( L, -1); | ||
783 | } | ||
784 | break; | ||
785 | |||
786 | case 2: // 2 parameters, a name and group, in that order | ||
787 | linda_name = lua_tolstring( L, -2, &name_len); | ||
788 | linda_group = (unsigned long) lua_tointeger( L, -1); | ||
789 | break; | ||
790 | } | ||
791 | |||
792 | /* The deep data is allocated separately of Lua stack; we might no | ||
793 | * longer be around when last reference to it is being released. | ||
794 | * One can use any memory allocation scheme. | ||
795 | * just don't use L's allocF because we don't know which state will get the honor of GCing the linda | ||
796 | */ | ||
797 | { | ||
798 | Universe* const U = universe_get(L); | ||
799 | AllocatorDefinition* const allocD = &U->internal_allocator; | ||
800 | s = (struct s_Linda*) allocD->allocF(allocD->allocUD, NULL, 0, sizeof(struct s_Linda) + name_len); // terminating 0 is already included | ||
801 | } | ||
802 | if( s) | ||
803 | { | ||
804 | s->prelude.magic.value = DEEP_VERSION.value; | ||
805 | SIGNAL_INIT( &s->read_happened); | ||
806 | SIGNAL_INIT( &s->write_happened); | ||
807 | s->U = universe_get( L); | ||
808 | s->simulate_cancel = CANCEL_NONE; | ||
809 | s->group = linda_group << KEEPER_MAGIC_SHIFT; | ||
810 | s->name[0] = 0; | ||
811 | memcpy( s->name, linda_name, name_len ? name_len + 1 : 0); | ||
812 | } | ||
813 | return s; | ||
814 | } | ||
815 | |||
816 | case eDO_delete: | ||
817 | { | ||
818 | Keeper* K; | ||
819 | struct s_Linda* linda = (struct s_Linda*) lua_touserdata( L, 1); | ||
820 | ASSERT_L( linda); | ||
821 | |||
822 | // Clean associated structures in the keeper state. | ||
823 | K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
824 | if( K && K->L) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) | ||
825 | { | ||
826 | // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... | ||
827 | keeper_call( linda->U, K->L, KEEPER_API( clear), L, linda, 0); | ||
828 | } | ||
829 | keeper_release( K); | ||
830 | |||
831 | // There aren't any lanes waiting on these lindas, since all proxies have been gc'ed. Right? | ||
832 | SIGNAL_FREE( &linda->read_happened); | ||
833 | SIGNAL_FREE( &linda->write_happened); | ||
834 | { | ||
835 | Universe* const U = universe_get(L); | ||
836 | AllocatorDefinition* const allocD = &U->internal_allocator; | ||
837 | (void) allocD->allocF(allocD->allocUD, linda, sizeof(struct s_Linda) + strlen(linda->name), 0); | ||
838 | } | ||
839 | return NULL; | ||
840 | } | ||
841 | |||
842 | case eDO_metatable: | ||
843 | { | ||
844 | |||
845 | STACK_CHECK( L, 0); | ||
846 | lua_newtable( L); | ||
847 | // metatable is its own index | ||
848 | lua_pushvalue( L, -1); | ||
849 | lua_setfield( L, -2, "__index"); | ||
850 | |||
851 | // protect metatable from external access | ||
852 | lua_pushliteral( L, "Linda"); | ||
853 | lua_setfield( L, -2, "__metatable"); | ||
854 | |||
855 | lua_pushcfunction( L, LG_linda_tostring); | ||
856 | lua_setfield( L, -2, "__tostring"); | ||
857 | |||
858 | // Decoda __towatch support | ||
859 | lua_pushcfunction( L, LG_linda_towatch); | ||
860 | lua_setfield( L, -2, "__towatch"); | ||
861 | |||
862 | lua_pushcfunction( L, LG_linda_concat); | ||
863 | lua_setfield( L, -2, "__concat"); | ||
864 | |||
865 | // protected calls, to ensure associated keeper is always released even in case of error | ||
866 | // all function are the protected call wrapper, where the actual operation is provided as upvalue | ||
867 | // note that this kind of thing can break function lookup as we use the function pointer here and there | ||
868 | |||
869 | lua_pushcfunction( L, LG_linda_send); | ||
870 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
871 | lua_setfield( L, -2, "send"); | ||
872 | |||
873 | lua_pushcfunction( L, LG_linda_receive); | ||
874 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
875 | lua_setfield( L, -2, "receive"); | ||
876 | |||
877 | lua_pushcfunction( L, LG_linda_limit); | ||
878 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
879 | lua_setfield( L, -2, "limit"); | ||
880 | |||
881 | lua_pushcfunction( L, LG_linda_set); | ||
882 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
883 | lua_setfield( L, -2, "set"); | ||
884 | |||
885 | lua_pushcfunction( L, LG_linda_count); | ||
886 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
887 | lua_setfield( L, -2, "count"); | ||
888 | |||
889 | lua_pushcfunction( L, LG_linda_get); | ||
890 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
891 | lua_setfield( L, -2, "get"); | ||
892 | |||
893 | lua_pushcfunction( L, LG_linda_cancel); | ||
894 | lua_setfield( L, -2, "cancel"); | ||
895 | |||
896 | lua_pushcfunction( L, LG_linda_deep); | ||
897 | lua_setfield( L, -2, "deep"); | ||
898 | |||
899 | lua_pushcfunction( L, LG_linda_dump); | ||
900 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
901 | lua_setfield( L, -2, "dump"); | ||
902 | |||
903 | // some constants | ||
904 | lua_pushliteral( L, BATCH_SENTINEL); | ||
905 | lua_setfield( L, -2, "batched"); | ||
906 | |||
907 | push_unique_key( L, NIL_SENTINEL); | ||
908 | lua_setfield( L, -2, "null"); | ||
909 | |||
910 | STACK_END( L, 1); | ||
911 | return NULL; | ||
912 | } | ||
913 | |||
914 | case eDO_module: | ||
915 | // linda is a special case because we know lanes must be loaded from the main lua state | ||
916 | // to be able to ever get here, so we know it will remain loaded as long a the main state is around | ||
917 | // in other words, forever. | ||
918 | default: | ||
919 | { | ||
920 | return NULL; | ||
921 | } | ||
922 | } | ||
923 | } | ||
924 | |||
925 | /* | ||
926 | * ud = lanes.linda( [name[,group]]) | ||
927 | * | ||
928 | * returns a linda object, or raises an error if creation failed | ||
929 | */ | ||
930 | LUAG_FUNC( linda) | ||
931 | { | ||
932 | int const top = lua_gettop( L); | ||
933 | luaL_argcheck( L, top <= 2, top, "too many arguments"); | ||
934 | if( top == 1) | ||
935 | { | ||
936 | int const t = lua_type( L, 1); | ||
937 | luaL_argcheck( L, t == LUA_TSTRING || t == LUA_TNUMBER, 1, "wrong parameter (should be a string or a number)"); | ||
938 | } | ||
939 | else if( top == 2) | ||
940 | { | ||
941 | luaL_checktype( L, 1, LUA_TSTRING); | ||
942 | luaL_checktype( L, 2, LUA_TNUMBER); | ||
943 | } | ||
944 | return luaG_newdeepuserdata( L, linda_id, 0); | ||
945 | } | ||