diff options
author | Benoit Germain <bnt.germain@gmail.com> | 2018-11-15 09:24:46 +0100 |
---|---|---|
committer | Benoit Germain <bnt.germain@gmail.com> | 2018-11-15 09:24:46 +0100 |
commit | f087dfe25d093b1cb1665d12dc5a039f29f3af11 (patch) | |
tree | 14dbc119b902e8c2db22ed78ae4d7ed4e718790e /src | |
parent | b0bb224ce6c4bd769a3f1a868e832d9d38f6e63e (diff) | |
download | lanes-f087dfe25d093b1cb1665d12dc5a039f29f3af11.tar.gz lanes-f087dfe25d093b1cb1665d12dc5a039f29f3af11.tar.bz2 lanes-f087dfe25d093b1cb1665d12dc5a039f29f3af11.zip |
split linda code in a separate file
Diffstat (limited to 'src')
-rw-r--r-- | src/lanes.c | 1014 | ||||
-rw-r--r-- | src/lanes_private.h | 117 | ||||
-rw-r--r-- | src/linda.c | 934 | ||||
-rw-r--r-- | src/tools.h | 2 |
4 files changed, 1055 insertions, 1012 deletions
diff --git a/src/lanes.c b/src/lanes.c index f0a5697..f2e3065 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -52,7 +52,7 @@ | |||
52 | * ... | 52 | * ... |
53 | */ | 53 | */ |
54 | 54 | ||
55 | char const* VERSION = "3.13"; | 55 | char const* VERSION = "3.13.0"; |
56 | 56 | ||
57 | /* | 57 | /* |
58 | =============================================================================== | 58 | =============================================================================== |
@@ -93,7 +93,7 @@ THE SOFTWARE. | |||
93 | #include "tools.h" | 93 | #include "tools.h" |
94 | #include "universe.h" | 94 | #include "universe.h" |
95 | #include "keeper.h" | 95 | #include "keeper.h" |
96 | #include "uniquekey.h" | 96 | #include "lanes_private.h" |
97 | 97 | ||
98 | #if !(defined( PLATFORM_XBOX) || defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC)) | 98 | #if !(defined( PLATFORM_XBOX) || defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC)) |
99 | # include <sys/time.h> | 99 | # include <sys/time.h> |
@@ -111,105 +111,6 @@ THE SOFTWARE. | |||
111 | */ | 111 | */ |
112 | #define ERROR_FULL_STACK 1 // must be either 0 or 1 as we do some index arithmetics with it! | 112 | #define ERROR_FULL_STACK 1 // must be either 0 or 1 as we do some index arithmetics with it! |
113 | 113 | ||
114 | /* | ||
115 | * Lane cancellation request modes | ||
116 | */ | ||
117 | enum e_cancel_request | ||
118 | { | ||
119 | CANCEL_NONE, // no pending cancel request | ||
120 | CANCEL_SOFT, // user wants the lane to cancel itself manually on cancel_test() | ||
121 | CANCEL_HARD // user wants the lane to be interrupted (meaning code won't return from those functions) from inside linda:send/receive calls | ||
122 | }; | ||
123 | |||
124 | // NOTE: values to be changed by either thread, during execution, without | ||
125 | // locking, are marked "volatile" | ||
126 | // | ||
127 | struct s_Lane | ||
128 | { | ||
129 | THREAD_T thread; | ||
130 | // | ||
131 | // M: sub-thread OS thread | ||
132 | // S: not used | ||
133 | |||
134 | char const* debug_name; | ||
135 | |||
136 | lua_State* L; | ||
137 | Universe* U; | ||
138 | // | ||
139 | // M: prepares the state, and reads results | ||
140 | // S: while S is running, M must keep out of modifying the state | ||
141 | |||
142 | volatile enum e_status status; | ||
143 | // | ||
144 | // M: sets to PENDING (before launching) | ||
145 | // S: updates -> RUNNING/WAITING -> DONE/ERROR_ST/CANCELLED | ||
146 | |||
147 | SIGNAL_T* volatile waiting_on; | ||
148 | // | ||
149 | // When status is WAITING, points on the linda's signal the thread waits on, else NULL | ||
150 | |||
151 | volatile enum e_cancel_request cancel_request; | ||
152 | // | ||
153 | // M: sets to FALSE, flags TRUE for cancel request | ||
154 | // S: reads to see if cancel is requested | ||
155 | |||
156 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
157 | SIGNAL_T done_signal; | ||
158 | // | ||
159 | // M: Waited upon at lane ending (if Posix with no PTHREAD_TIMEDJOIN) | ||
160 | // S: sets the signal once cancellation is noticed (avoids a kill) | ||
161 | |||
162 | MUTEX_T done_lock; | ||
163 | // | ||
164 | // Lock required by 'done_signal' condition variable, protecting | ||
165 | // lane status changes to DONE/ERROR_ST/CANCELLED. | ||
166 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
167 | |||
168 | volatile enum | ||
169 | { | ||
170 | NORMAL, // normal master side state | ||
171 | KILLED // issued an OS kill | ||
172 | } mstatus; | ||
173 | // | ||
174 | // M: sets to NORMAL, if issued a kill changes to KILLED | ||
175 | // S: not used | ||
176 | |||
177 | struct s_Lane* volatile selfdestruct_next; | ||
178 | // | ||
179 | // M: sets to non-NULL if facing lane handle '__gc' cycle but the lane | ||
180 | // is still running | ||
181 | // S: cleans up after itself if non-NULL at lane exit | ||
182 | |||
183 | #if HAVE_LANE_TRACKING | ||
184 | struct s_Lane* volatile tracking_next; | ||
185 | #endif // HAVE_LANE_TRACKING | ||
186 | // | ||
187 | // For tracking only | ||
188 | }; | ||
189 | typedef struct s_Lane Lane; | ||
190 | |||
191 | // To allow free-running threads (longer lifespan than the handle's) | ||
192 | // 'Lane' are malloc/free'd and the handle only carries a pointer. | ||
193 | // This is not deep userdata since the handle's not portable among lanes. | ||
194 | // | ||
195 | #define lua_toLane( L, i) (*((Lane**) luaL_checkudata( L, i, "Lane"))) | ||
196 | |||
197 | // crc64/we of string "CANCEL_TEST_KEY" generated at http://www.nitrxgen.net/hashgen/ | ||
198 | static DECLARE_CONST_UNIQUE_KEY( CANCEL_TEST_KEY, 0xe66f5960c57d133a); // used as registry key | ||
199 | |||
200 | static inline Lane* get_lane_from_registry( lua_State* L) | ||
201 | { | ||
202 | Lane* s; | ||
203 | STACK_GROW( L, 1); | ||
204 | STACK_CHECK( L); | ||
205 | push_unique_key( L, CANCEL_TEST_KEY); | ||
206 | lua_rawget( L, LUA_REGISTRYINDEX); | ||
207 | s = lua_touserdata( L, -1); // lightuserdata (true 's_lane' pointer) / nil | ||
208 | lua_pop( L, 1); | ||
209 | STACK_END( L, 0); | ||
210 | return s; | ||
211 | } | ||
212 | |||
213 | // intern the debug name in the specified lua state so that the pointer remains valid when the lane's state is closed | 114 | // intern the debug name in the specified lua state so that the pointer remains valid when the lane's state is closed |
214 | static void securize_debug_threadname( lua_State* L, Lane* s) | 115 | static void securize_debug_threadname( lua_State* L, Lane* s) |
215 | { | 116 | { |
@@ -241,16 +142,6 @@ static inline enum e_cancel_request cancel_test( lua_State* L) | |||
241 | return s ? s->cancel_request : CANCEL_NONE; | 142 | return s ? s->cancel_request : CANCEL_NONE; |
242 | } | 143 | } |
243 | 144 | ||
244 | // crc64/we of string "CANCEL_ERROR" generated at http://www.nitrxgen.net/hashgen/ | ||
245 | static DECLARE_CONST_UNIQUE_KEY( CANCEL_ERROR, 0xe97d41626cc97577); // 'cancel_error' sentinel | ||
246 | |||
247 | static int cancel_error( lua_State* L) | ||
248 | { | ||
249 | STACK_GROW( L, 1); | ||
250 | push_unique_key( L, CANCEL_ERROR); // special error value | ||
251 | return lua_error( L); // doesn't return | ||
252 | } | ||
253 | |||
254 | static void cancel_hook( lua_State* L, lua_Debug* ar) | 145 | static void cancel_hook( lua_State* L, lua_Debug* ar) |
255 | { | 146 | { |
256 | (void)ar; | 147 | (void)ar; |
@@ -411,905 +302,6 @@ static void lane_cleanup( Lane* s) | |||
411 | 302 | ||
412 | /* | 303 | /* |
413 | * ############################################################################################### | 304 | * ############################################################################################### |
414 | * ############################################ Linda ############################################ | ||
415 | * ############################################################################################### | ||
416 | */ | ||
417 | |||
418 | /* | ||
419 | * Actual data is kept within a keeper state, which is hashed by the 's_Linda' | ||
420 | * pointer (which is same to all userdatas pointing to it). | ||
421 | */ | ||
422 | struct s_Linda | ||
423 | { | ||
424 | SIGNAL_T read_happened; | ||
425 | SIGNAL_T write_happened; | ||
426 | Universe* U; // the universe this linda belongs to | ||
427 | ptrdiff_t group; // a group to control keeper allocation between lindas | ||
428 | enum e_cancel_request simulate_cancel; | ||
429 | char name[1]; | ||
430 | }; | ||
431 | #define LINDA_KEEPER_HASHSEED( linda) (linda->group ? linda->group : (ptrdiff_t)linda) | ||
432 | |||
433 | static void* linda_id( lua_State*, DeepOp); | ||
434 | |||
435 | static inline struct s_Linda* lua_toLinda( lua_State* L, int idx_) | ||
436 | { | ||
437 | struct s_Linda* linda = (struct s_Linda*) luaG_todeep( L, linda_id, idx_); | ||
438 | luaL_argcheck( L, linda != NULL, idx_, "expecting a linda object"); | ||
439 | return linda; | ||
440 | } | ||
441 | |||
442 | static void check_key_types( lua_State* L, int start_, int end_) | ||
443 | { | ||
444 | int i; | ||
445 | for( i = start_; i <= end_; ++ i) | ||
446 | { | ||
447 | int t = lua_type( L, i); | ||
448 | if( t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA) | ||
449 | { | ||
450 | continue; | ||
451 | } | ||
452 | (void) luaL_error( L, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", i); | ||
453 | } | ||
454 | } | ||
455 | |||
456 | LUAG_FUNC( linda_protected_call) | ||
457 | { | ||
458 | int rc = LUA_OK; | ||
459 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
460 | |||
461 | // acquire the keeper | ||
462 | Keeper* K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED(linda)); | ||
463 | lua_State* KL = K ? K->L : NULL; // need to do this for 'STACK_CHECK' | ||
464 | if( KL == NULL) return 0; | ||
465 | |||
466 | // retrieve the actual function to be called and move it before the arguments | ||
467 | lua_pushvalue( L, lua_upvalueindex( 1)); | ||
468 | lua_insert( L, 1); | ||
469 | // do a protected call | ||
470 | rc = lua_pcall( L, lua_gettop( L) - 1, LUA_MULTRET, 0); | ||
471 | |||
472 | // release the keeper | ||
473 | keeper_release( K); | ||
474 | |||
475 | // if there was an error, forward it | ||
476 | if( rc != LUA_OK) | ||
477 | { | ||
478 | return lua_error( L); | ||
479 | } | ||
480 | // return whatever the actual operation provided | ||
481 | return lua_gettop( L); | ||
482 | } | ||
483 | |||
484 | /* | ||
485 | * bool= linda_send( linda_ud, [timeout_secs=-1,] [linda.null,] key_num|str|bool|lightuserdata, ... ) | ||
486 | * | ||
487 | * Send one or more values to a Linda. If there is a limit, all values must fit. | ||
488 | * | ||
489 | * Returns: 'true' if the value was queued | ||
490 | * 'false' for timeout (only happens when the queue size is limited) | ||
491 | * nil, CANCEL_ERROR if cancelled | ||
492 | */ | ||
493 | LUAG_FUNC( linda_send) | ||
494 | { | ||
495 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
496 | bool_t ret = FALSE; | ||
497 | enum e_cancel_request cancel = CANCEL_NONE; | ||
498 | int pushed; | ||
499 | time_d timeout = -1.0; | ||
500 | uint_t key_i = 2; // index of first key, if timeout not there | ||
501 | bool_t as_nil_sentinel; // if not NULL, send() will silently send a single nil if nothing is provided | ||
502 | |||
503 | if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion | ||
504 | { | ||
505 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); | ||
506 | ++ key_i; | ||
507 | } | ||
508 | else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key | ||
509 | { | ||
510 | ++ key_i; | ||
511 | } | ||
512 | |||
513 | as_nil_sentinel = equal_unique_key( L, key_i, NIL_SENTINEL); | ||
514 | if( as_nil_sentinel) | ||
515 | { | ||
516 | // the real key to send data to is after the NIL_SENTINEL marker | ||
517 | ++ key_i; | ||
518 | } | ||
519 | |||
520 | // make sure the key is of a valid type | ||
521 | check_key_types( L, key_i, key_i); | ||
522 | |||
523 | STACK_GROW( L, 1); | ||
524 | |||
525 | // make sure there is something to send | ||
526 | if( (uint_t)lua_gettop( L) == key_i) | ||
527 | { | ||
528 | if( as_nil_sentinel) | ||
529 | { | ||
530 | // send a single nil if nothing is provided | ||
531 | push_unique_key( L, NIL_SENTINEL); | ||
532 | } | ||
533 | else | ||
534 | { | ||
535 | return luaL_error( L, "no data to send"); | ||
536 | } | ||
537 | } | ||
538 | |||
539 | // convert nils to some special non-nil sentinel in sent values | ||
540 | keeper_toggle_nil_sentinels( L, key_i + 1, eLM_ToKeeper); | ||
541 | |||
542 | { | ||
543 | bool_t try_again = TRUE; | ||
544 | Lane* const s = get_lane_from_registry( L); | ||
545 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
546 | lua_State* KL = K ? K->L : NULL; // need to do this for 'STACK_CHECK' | ||
547 | if( KL == NULL) return 0; | ||
548 | STACK_CHECK( KL); | ||
549 | for( ;;) | ||
550 | { | ||
551 | if( s != NULL) | ||
552 | { | ||
553 | cancel = s->cancel_request; | ||
554 | } | ||
555 | cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; | ||
556 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | ||
557 | if( !try_again || cancel != CANCEL_NONE) | ||
558 | { | ||
559 | pushed = 0; | ||
560 | break; | ||
561 | } | ||
562 | |||
563 | STACK_MID( KL, 0); | ||
564 | pushed = keeper_call( linda->U, KL, KEEPER_API( send), L, linda, key_i); | ||
565 | if( pushed < 0) | ||
566 | { | ||
567 | break; | ||
568 | } | ||
569 | ASSERT_L( pushed == 1); | ||
570 | |||
571 | ret = lua_toboolean( L, -1); | ||
572 | lua_pop( L, 1); | ||
573 | |||
574 | if( ret) | ||
575 | { | ||
576 | // Wake up ALL waiting threads | ||
577 | SIGNAL_ALL( &linda->write_happened); | ||
578 | break; | ||
579 | } | ||
580 | |||
581 | // instant timout to bypass the wait syscall | ||
582 | if( timeout == 0.0) | ||
583 | { | ||
584 | break; /* no wait; instant timeout */ | ||
585 | } | ||
586 | |||
587 | // storage limit hit, wait until timeout or signalled that we should try again | ||
588 | { | ||
589 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | ||
590 | if( s != NULL) | ||
591 | { | ||
592 | // change status of lane to "waiting" | ||
593 | prev_status = s->status; // RUNNING, most likely | ||
594 | ASSERT_L( prev_status == RUNNING); // but check, just in case | ||
595 | s->status = WAITING; | ||
596 | ASSERT_L( s->waiting_on == NULL); | ||
597 | s->waiting_on = &linda->read_happened; | ||
598 | } | ||
599 | // could not send because no room: wait until some data was read before trying again, or until timeout is reached | ||
600 | try_again = SIGNAL_WAIT( &linda->read_happened, &K->keeper_cs, timeout); | ||
601 | if( s != NULL) | ||
602 | { | ||
603 | s->waiting_on = NULL; | ||
604 | s->status = prev_status; | ||
605 | } | ||
606 | } | ||
607 | } | ||
608 | STACK_END( KL, 0); | ||
609 | } | ||
610 | |||
611 | if( pushed < 0) | ||
612 | { | ||
613 | return luaL_error( L, "tried to copy unsupported types"); | ||
614 | } | ||
615 | |||
616 | switch( cancel) | ||
617 | { | ||
618 | case CANCEL_SOFT: | ||
619 | // if user wants to soft-cancel, the call returns lanes.cancel_error | ||
620 | push_unique_key( L, CANCEL_ERROR); | ||
621 | return 1; | ||
622 | |||
623 | case CANCEL_HARD: | ||
624 | // raise an error interrupting execution only in case of hard cancel | ||
625 | return cancel_error( L); // raises an error and doesn't return | ||
626 | |||
627 | default: | ||
628 | lua_pushboolean( L, ret); // true (success) or false (timeout) | ||
629 | return 1; | ||
630 | } | ||
631 | } | ||
632 | |||
633 | |||
634 | /* | ||
635 | * 2 modes of operation | ||
636 | * [val, key]= linda_receive( linda_ud, [timeout_secs_num=-1], key_num|str|bool|lightuserdata [, ...] ) | ||
637 | * Consumes a single value from the Linda, in any key. | ||
638 | * Returns: received value (which is consumed from the slot), and the key which had it | ||
639 | |||
640 | * [val1, ... valCOUNT]= linda_receive( linda_ud, [timeout_secs_num=-1], linda.batched, key_num|str|bool|lightuserdata, min_COUNT[, max_COUNT]) | ||
641 | * Consumes between min_COUNT and max_COUNT values from the linda, from a single key. | ||
642 | * returns the actual consumed values, or nil if there weren't enough values to consume | ||
643 | * | ||
644 | */ | ||
645 | #define BATCH_SENTINEL "270e6c9d-280f-4983-8fee-a7ecdda01475" | ||
646 | LUAG_FUNC( linda_receive) | ||
647 | { | ||
648 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
649 | int pushed, expected_pushed_min, expected_pushed_max; | ||
650 | enum e_cancel_request cancel = CANCEL_NONE; | ||
651 | keeper_api_t keeper_receive; | ||
652 | |||
653 | time_d timeout = -1.0; | ||
654 | uint_t key_i = 2; | ||
655 | |||
656 | if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion | ||
657 | { | ||
658 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); | ||
659 | ++ key_i; | ||
660 | } | ||
661 | else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key | ||
662 | { | ||
663 | ++ key_i; | ||
664 | } | ||
665 | |||
666 | // are we in batched mode? | ||
667 | { | ||
668 | int is_batched; | ||
669 | lua_pushliteral( L, BATCH_SENTINEL); | ||
670 | is_batched = lua501_equal( L, key_i, -1); | ||
671 | lua_pop( L, 1); | ||
672 | if( is_batched) | ||
673 | { | ||
674 | // no need to pass linda.batched in the keeper state | ||
675 | ++ key_i; | ||
676 | // make sure the keys are of a valid type | ||
677 | check_key_types( L, key_i, key_i); | ||
678 | // receive multiple values from a single slot | ||
679 | keeper_receive = KEEPER_API( receive_batched); | ||
680 | // we expect a user-defined amount of return value | ||
681 | expected_pushed_min = (int)luaL_checkinteger( L, key_i + 1); | ||
682 | expected_pushed_max = (int)luaL_optinteger( L, key_i + 2, expected_pushed_min); | ||
683 | // don't forget to count the key in addition to the values | ||
684 | ++ expected_pushed_min; | ||
685 | ++ expected_pushed_max; | ||
686 | if( expected_pushed_min > expected_pushed_max) | ||
687 | { | ||
688 | return luaL_error( L, "batched min/max error"); | ||
689 | } | ||
690 | } | ||
691 | else | ||
692 | { | ||
693 | // make sure the keys are of a valid type | ||
694 | check_key_types( L, key_i, lua_gettop( L)); | ||
695 | // receive a single value, checking multiple slots | ||
696 | keeper_receive = KEEPER_API( receive); | ||
697 | // we expect a single (value, key) pair of returned values | ||
698 | expected_pushed_min = expected_pushed_max = 2; | ||
699 | } | ||
700 | } | ||
701 | |||
702 | { | ||
703 | bool_t try_again = TRUE; | ||
704 | Lane* const s = get_lane_from_registry( L); | ||
705 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
706 | if( K == NULL) return 0; | ||
707 | for( ;;) | ||
708 | { | ||
709 | if( s != NULL) | ||
710 | { | ||
711 | cancel = s->cancel_request; | ||
712 | } | ||
713 | cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; | ||
714 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | ||
715 | if( !try_again || cancel != CANCEL_NONE) | ||
716 | { | ||
717 | pushed = 0; | ||
718 | break; | ||
719 | } | ||
720 | |||
721 | // all arguments of receive() but the first are passed to the keeper's receive function | ||
722 | pushed = keeper_call( linda->U, K->L, keeper_receive, L, linda, key_i); | ||
723 | if( pushed < 0) | ||
724 | { | ||
725 | break; | ||
726 | } | ||
727 | if( pushed > 0) | ||
728 | { | ||
729 | ASSERT_L( pushed >= expected_pushed_min && pushed <= expected_pushed_max); | ||
730 | // replace sentinels with real nils | ||
731 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper); | ||
732 | // To be done from within the 'K' locking area | ||
733 | // | ||
734 | SIGNAL_ALL( &linda->read_happened); | ||
735 | break; | ||
736 | } | ||
737 | |||
738 | if( timeout == 0.0) | ||
739 | { | ||
740 | break; /* instant timeout */ | ||
741 | } | ||
742 | |||
743 | // nothing received, wait until timeout or signalled that we should try again | ||
744 | { | ||
745 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | ||
746 | if( s != NULL) | ||
747 | { | ||
748 | // change status of lane to "waiting" | ||
749 | prev_status = s->status; // RUNNING, most likely | ||
750 | ASSERT_L( prev_status == RUNNING); // but check, just in case | ||
751 | s->status = WAITING; | ||
752 | ASSERT_L( s->waiting_on == NULL); | ||
753 | s->waiting_on = &linda->write_happened; | ||
754 | } | ||
755 | // not enough data to read: wakeup when data was sent, or when timeout is reached | ||
756 | try_again = SIGNAL_WAIT( &linda->write_happened, &K->keeper_cs, timeout); | ||
757 | if( s != NULL) | ||
758 | { | ||
759 | s->waiting_on = NULL; | ||
760 | s->status = prev_status; | ||
761 | } | ||
762 | } | ||
763 | } | ||
764 | } | ||
765 | |||
766 | if( pushed < 0) | ||
767 | { | ||
768 | return luaL_error( L, "tried to copy unsupported types"); | ||
769 | } | ||
770 | |||
771 | switch( cancel) | ||
772 | { | ||
773 | case CANCEL_SOFT: | ||
774 | // if user wants to soft-cancel, the call returns CANCEL_ERROR | ||
775 | push_unique_key( L, CANCEL_ERROR); | ||
776 | return 1; | ||
777 | |||
778 | case CANCEL_HARD: | ||
779 | // raise an error interrupting execution only in case of hard cancel | ||
780 | return cancel_error( L); // raises an error and doesn't return | ||
781 | |||
782 | default: | ||
783 | return pushed; | ||
784 | } | ||
785 | } | ||
786 | |||
787 | |||
788 | /* | ||
789 | * [true|lanes.cancel_error] = linda_set( linda_ud, key_num|str|bool|lightuserdata [, value [, ...]]) | ||
790 | * | ||
791 | * Set one or more value to Linda. | ||
792 | * TODO: what do we do if we set to non-nil and limit is 0? | ||
793 | * | ||
794 | * Existing slot value is replaced, and possible queued entries removed. | ||
795 | */ | ||
796 | LUAG_FUNC( linda_set) | ||
797 | { | ||
798 | struct s_Linda* const linda = lua_toLinda( L, 1); | ||
799 | int pushed; | ||
800 | bool_t has_value = lua_gettop( L) > 2; | ||
801 | |||
802 | // make sure the key is of a valid type (throws an error if not the case) | ||
803 | check_key_types( L, 2, 2); | ||
804 | |||
805 | { | ||
806 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
807 | |||
808 | if( linda->simulate_cancel == CANCEL_NONE) | ||
809 | { | ||
810 | if( has_value) | ||
811 | { | ||
812 | // convert nils to some special non-nil sentinel in sent values | ||
813 | keeper_toggle_nil_sentinels( L, 3, eLM_ToKeeper); | ||
814 | } | ||
815 | pushed = keeper_call( linda->U, K->L, KEEPER_API( set), L, linda, 2); | ||
816 | if( pushed >= 0) // no error? | ||
817 | { | ||
818 | ASSERT_L( pushed == 0 || pushed == 1); | ||
819 | |||
820 | if( has_value) | ||
821 | { | ||
822 | // we put some data in the slot, tell readers that they should wake | ||
823 | SIGNAL_ALL( &linda->write_happened); // To be done from within the 'K' locking area | ||
824 | } | ||
825 | if( pushed == 1) | ||
826 | { | ||
827 | // the key was full, but it is no longer the case, tell writers they should wake | ||
828 | ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); | ||
829 | SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area | ||
830 | } | ||
831 | } | ||
832 | } | ||
833 | else // linda is cancelled | ||
834 | { | ||
835 | // do nothing and return lanes.cancel_error | ||
836 | push_unique_key( L, CANCEL_ERROR); | ||
837 | pushed = 1; | ||
838 | } | ||
839 | } | ||
840 | |||
841 | // must trigger any error after keeper state has been released | ||
842 | return (pushed < 0) ? luaL_error( L, "tried to copy unsupported types") : pushed; | ||
843 | } | ||
844 | |||
845 | |||
846 | /* | ||
847 | * [val] = linda_count( linda_ud, [key [, ...]]) | ||
848 | * | ||
849 | * Get a count of the pending elements in the specified keys | ||
850 | */ | ||
851 | LUAG_FUNC( linda_count) | ||
852 | { | ||
853 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
854 | int pushed; | ||
855 | |||
856 | // make sure the keys are of a valid type | ||
857 | check_key_types( L, 2, lua_gettop( L)); | ||
858 | |||
859 | { | ||
860 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
861 | pushed = keeper_call( linda->U, K->L, KEEPER_API( count), L, linda, 2); | ||
862 | if( pushed < 0) | ||
863 | { | ||
864 | return luaL_error( L, "tried to count an invalid key"); | ||
865 | } | ||
866 | } | ||
867 | return pushed; | ||
868 | } | ||
869 | |||
870 | |||
871 | /* | ||
872 | * [val [, ...]] = linda_get( linda_ud, key_num|str|bool|lightuserdata [, count = 1]) | ||
873 | * | ||
874 | * Get one or more values from Linda. | ||
875 | */ | ||
876 | LUAG_FUNC( linda_get) | ||
877 | { | ||
878 | struct s_Linda* const linda = lua_toLinda( L, 1); | ||
879 | int pushed; | ||
880 | lua_Integer count = luaL_optinteger( L, 3, 1); | ||
881 | luaL_argcheck( L, count >= 1, 3, "count should be >= 1"); | ||
882 | luaL_argcheck( L, lua_gettop( L) <= 3, 4, "too many arguments"); | ||
883 | |||
884 | // make sure the key is of a valid type (throws an error if not the case) | ||
885 | check_key_types( L, 2, 2); | ||
886 | { | ||
887 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
888 | |||
889 | if( linda->simulate_cancel == CANCEL_NONE) | ||
890 | { | ||
891 | pushed = keeper_call( linda->U, K->L, KEEPER_API( get), L, linda, 2); | ||
892 | if( pushed > 0) | ||
893 | { | ||
894 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper); | ||
895 | } | ||
896 | } | ||
897 | else // linda is cancelled | ||
898 | { | ||
899 | // do nothing and return lanes.cancel_error | ||
900 | push_unique_key( L, CANCEL_ERROR); | ||
901 | pushed = 1; | ||
902 | } | ||
903 | // an error can be raised if we attempt to read an unregistered function | ||
904 | if( pushed < 0) | ||
905 | { | ||
906 | return luaL_error( L, "tried to copy unsupported types"); | ||
907 | } | ||
908 | } | ||
909 | |||
910 | return pushed; | ||
911 | } | ||
912 | |||
913 | |||
914 | /* | ||
915 | * [true] = linda_limit( linda_ud, key_num|str|bool|lightuserdata, int) | ||
916 | * | ||
917 | * Set limit to 1 Linda keys. | ||
918 | * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so | ||
919 | */ | ||
920 | LUAG_FUNC( linda_limit) | ||
921 | { | ||
922 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
923 | int pushed; | ||
924 | |||
925 | // make sure we got 3 arguments: the linda, a key and a limit | ||
926 | luaL_argcheck( L, lua_gettop( L) == 3, 2, "wrong number of arguments"); | ||
927 | // make sure we got a numeric limit | ||
928 | luaL_checknumber( L, 3); | ||
929 | // make sure the key is of a valid type | ||
930 | check_key_types( L, 2, 2); | ||
931 | |||
932 | { | ||
933 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
934 | |||
935 | if( linda->simulate_cancel == CANCEL_NONE) | ||
936 | { | ||
937 | pushed = keeper_call( linda->U, K->L, KEEPER_API( limit), L, linda, 2); | ||
938 | ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads | ||
939 | if( pushed == 1) | ||
940 | { | ||
941 | ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); | ||
942 | SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area | ||
943 | } | ||
944 | } | ||
945 | else // linda is cancelled | ||
946 | { | ||
947 | // do nothing and return lanes.cancel_error | ||
948 | push_unique_key( L, CANCEL_ERROR); | ||
949 | pushed = 1; | ||
950 | } | ||
951 | } | ||
952 | // propagate pushed boolean if any | ||
953 | return pushed; | ||
954 | } | ||
955 | |||
956 | |||
957 | /* | ||
958 | * (void) = linda_cancel( linda_ud, "read"|"write"|"both"|"none") | ||
959 | * | ||
960 | * Signal linda so that waiting threads wake up as if their own lane was cancelled | ||
961 | */ | ||
962 | LUAG_FUNC( linda_cancel) | ||
963 | { | ||
964 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
965 | char const* who = luaL_optstring( L, 2, "both"); | ||
966 | |||
967 | // make sure we got 3 arguments: the linda, a key and a limit | ||
968 | luaL_argcheck( L, lua_gettop( L) <= 2, 2, "wrong number of arguments"); | ||
969 | |||
970 | linda->simulate_cancel = CANCEL_SOFT; | ||
971 | if( strcmp( who, "both") == 0) // tell everyone writers to wake up | ||
972 | { | ||
973 | SIGNAL_ALL( &linda->write_happened); | ||
974 | SIGNAL_ALL( &linda->read_happened); | ||
975 | } | ||
976 | else if( strcmp( who, "none") == 0) // reset flag | ||
977 | { | ||
978 | linda->simulate_cancel = CANCEL_NONE; | ||
979 | } | ||
980 | else if( strcmp( who, "read") == 0) // tell blocked readers to wake up | ||
981 | { | ||
982 | SIGNAL_ALL( &linda->write_happened); | ||
983 | } | ||
984 | else if( strcmp( who, "write") == 0) // tell blocked writers to wake up | ||
985 | { | ||
986 | SIGNAL_ALL( &linda->read_happened); | ||
987 | } | ||
988 | else | ||
989 | { | ||
990 | return luaL_error( L, "unknown wake hint '%s'", who); | ||
991 | } | ||
992 | return 0; | ||
993 | } | ||
994 | |||
995 | |||
996 | /* | ||
997 | * lightuserdata= linda_deep( linda_ud ) | ||
998 | * | ||
999 | * Return the 'deep' userdata pointer, identifying the Linda. | ||
1000 | * | ||
1001 | * This is needed for using Lindas as key indices (timer system needs it); | ||
1002 | * separately created proxies of the same underlying deep object will have | ||
1003 | * different userdata and won't be known to be essentially the same deep one | ||
1004 | * without this. | ||
1005 | */ | ||
1006 | LUAG_FUNC( linda_deep) | ||
1007 | { | ||
1008 | struct s_Linda* linda= lua_toLinda( L, 1); | ||
1009 | lua_pushlightuserdata( L, linda); // just the address | ||
1010 | return 1; | ||
1011 | } | ||
1012 | |||
1013 | |||
1014 | /* | ||
1015 | * string = linda:__tostring( linda_ud) | ||
1016 | * | ||
1017 | * Return the stringification of a linda | ||
1018 | * | ||
1019 | * Useful for concatenation or debugging purposes | ||
1020 | */ | ||
1021 | |||
1022 | static int linda_tostring( lua_State* L, int idx_, bool_t opt_) | ||
1023 | { | ||
1024 | struct s_Linda* linda = (struct s_Linda*) luaG_todeep( L, linda_id, idx_); | ||
1025 | if( !opt_) | ||
1026 | { | ||
1027 | luaL_argcheck( L, linda, idx_, "expecting a linda object"); | ||
1028 | } | ||
1029 | if( linda != NULL) | ||
1030 | { | ||
1031 | char text[128]; | ||
1032 | int len; | ||
1033 | if( linda->name[0]) | ||
1034 | len = sprintf( text, "Linda: %.*s", (int)sizeof(text) - 8, linda->name); | ||
1035 | else | ||
1036 | len = sprintf( text, "Linda: %p", linda); | ||
1037 | lua_pushlstring( L, text, len); | ||
1038 | return 1; | ||
1039 | } | ||
1040 | return 0; | ||
1041 | } | ||
1042 | |||
1043 | LUAG_FUNC( linda_tostring) | ||
1044 | { | ||
1045 | return linda_tostring( L, 1, FALSE); | ||
1046 | } | ||
1047 | |||
1048 | |||
1049 | /* | ||
1050 | * string = linda:__concat( a, b) | ||
1051 | * | ||
1052 | * Return the concatenation of a pair of items, one of them being a linda | ||
1053 | * | ||
1054 | * Useful for concatenation or debugging purposes | ||
1055 | */ | ||
1056 | LUAG_FUNC( linda_concat) | ||
1057 | { // linda1? linda2? | ||
1058 | bool_t atLeastOneLinda = FALSE; | ||
1059 | // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both. | ||
1060 | if( linda_tostring( L, 1, TRUE)) | ||
1061 | { | ||
1062 | atLeastOneLinda = TRUE; | ||
1063 | lua_replace( L, 1); | ||
1064 | } | ||
1065 | if( linda_tostring( L, 2, TRUE)) | ||
1066 | { | ||
1067 | atLeastOneLinda = TRUE; | ||
1068 | lua_replace( L, 2); | ||
1069 | } | ||
1070 | if( !atLeastOneLinda) // should not be possible | ||
1071 | { | ||
1072 | return luaL_error( L, "internal error: linda_concat called on non-Linda"); | ||
1073 | } | ||
1074 | lua_concat( L, 2); | ||
1075 | return 1; | ||
1076 | } | ||
1077 | |||
1078 | /* | ||
1079 | * table = linda:dump() | ||
1080 | * return a table listing all pending data inside the linda | ||
1081 | */ | ||
1082 | LUAG_FUNC( linda_dump) | ||
1083 | { | ||
1084 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
1085 | ASSERT_L( linda->U == universe_get( L)); | ||
1086 | return keeper_push_linda_storage( linda->U, L, linda, LINDA_KEEPER_HASHSEED( linda)); | ||
1087 | } | ||
1088 | |||
1089 | /* | ||
1090 | * table = linda:dump() | ||
1091 | * return a table listing all pending data inside the linda | ||
1092 | */ | ||
1093 | LUAG_FUNC( linda_towatch) | ||
1094 | { | ||
1095 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
1096 | int pushed; | ||
1097 | ASSERT_L( linda->U == universe_get( L)); | ||
1098 | pushed = keeper_push_linda_storage( linda->U, L, linda, LINDA_KEEPER_HASHSEED( linda)); | ||
1099 | if( pushed == 0) | ||
1100 | { | ||
1101 | // if the linda is empty, don't return nil | ||
1102 | pushed = linda_tostring( L, 1, FALSE); | ||
1103 | } | ||
1104 | return pushed; | ||
1105 | } | ||
1106 | |||
1107 | /* | ||
1108 | * Identity function of a shared userdata object. | ||
1109 | * | ||
1110 | * lightuserdata= linda_id( "new" [, ...] ) | ||
1111 | * = linda_id( "delete", lightuserdata ) | ||
1112 | * | ||
1113 | * Creation and cleanup of actual 'deep' objects. 'luaG_...' will wrap them into | ||
1114 | * regular userdata proxies, per each state using the deep data. | ||
1115 | * | ||
1116 | * tbl= linda_id( "metatable" ) | ||
1117 | * | ||
1118 | * Returns a metatable for the proxy objects ('__gc' method not needed; will | ||
1119 | * be added by 'luaG_...') | ||
1120 | * | ||
1121 | * string= linda_id( "module") | ||
1122 | * | ||
1123 | * Returns the name of the module that a state should require | ||
1124 | * in order to keep a handle on the shared library that exported the idfunc | ||
1125 | * | ||
1126 | * = linda_id( str, ... ) | ||
1127 | * | ||
1128 | * For any other strings, the ID function must not react at all. This allows | ||
1129 | * future extensions of the system. | ||
1130 | */ | ||
1131 | static void* linda_id( lua_State* L, DeepOp op_) | ||
1132 | { | ||
1133 | switch( op_) | ||
1134 | { | ||
1135 | case eDO_new: | ||
1136 | { | ||
1137 | struct s_Linda* s; | ||
1138 | size_t name_len = 0; | ||
1139 | char const* linda_name = NULL; | ||
1140 | unsigned long linda_group = 0; | ||
1141 | // should have a string and/or a number of the stack as parameters (name and group) | ||
1142 | switch( lua_gettop( L)) | ||
1143 | { | ||
1144 | default: // 0 | ||
1145 | break; | ||
1146 | |||
1147 | case 1: // 1 parameter, either a name or a group | ||
1148 | if( lua_type( L, -1) == LUA_TSTRING) | ||
1149 | { | ||
1150 | linda_name = lua_tolstring( L, -1, &name_len); | ||
1151 | } | ||
1152 | else | ||
1153 | { | ||
1154 | linda_group = (unsigned long) lua_tointeger( L, -1); | ||
1155 | } | ||
1156 | break; | ||
1157 | |||
1158 | case 2: // 2 parameters, a name and group, in that order | ||
1159 | linda_name = lua_tolstring( L, -2, &name_len); | ||
1160 | linda_group = (unsigned long) lua_tointeger( L, -1); | ||
1161 | break; | ||
1162 | } | ||
1163 | |||
1164 | /* The deep data is allocated separately of Lua stack; we might no | ||
1165 | * longer be around when last reference to it is being released. | ||
1166 | * One can use any memory allocation scheme. | ||
1167 | * just don't use L's allocF because we don't know which state will get the honor of GCing the linda | ||
1168 | */ | ||
1169 | s = (struct s_Linda*) malloc( sizeof(struct s_Linda) + name_len); // terminating 0 is already included | ||
1170 | if( s) | ||
1171 | { | ||
1172 | SIGNAL_INIT( &s->read_happened); | ||
1173 | SIGNAL_INIT( &s->write_happened); | ||
1174 | s->U = universe_get( L); | ||
1175 | s->simulate_cancel = CANCEL_NONE; | ||
1176 | s->group = linda_group << KEEPER_MAGIC_SHIFT; | ||
1177 | s->name[0] = 0; | ||
1178 | memcpy( s->name, linda_name, name_len ? name_len + 1 : 0); | ||
1179 | } | ||
1180 | return s; | ||
1181 | } | ||
1182 | |||
1183 | case eDO_delete: | ||
1184 | { | ||
1185 | Keeper* K; | ||
1186 | struct s_Linda* linda = lua_touserdata( L, 1); | ||
1187 | ASSERT_L( linda); | ||
1188 | |||
1189 | // Clean associated structures in the keeper state. | ||
1190 | K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
1191 | if( K && K->L) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) | ||
1192 | { | ||
1193 | // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... | ||
1194 | keeper_call( linda->U, K->L, KEEPER_API( clear), L, linda, 0); | ||
1195 | } | ||
1196 | keeper_release( K); | ||
1197 | |||
1198 | // There aren't any lanes waiting on these lindas, since all proxies have been gc'ed. Right? | ||
1199 | SIGNAL_FREE( &linda->read_happened); | ||
1200 | SIGNAL_FREE( &linda->write_happened); | ||
1201 | free( linda); | ||
1202 | return NULL; | ||
1203 | } | ||
1204 | |||
1205 | case eDO_metatable: | ||
1206 | { | ||
1207 | |||
1208 | STACK_CHECK( L); | ||
1209 | lua_newtable( L); | ||
1210 | // metatable is its own index | ||
1211 | lua_pushvalue( L, -1); | ||
1212 | lua_setfield( L, -2, "__index"); | ||
1213 | |||
1214 | // protect metatable from external access | ||
1215 | lua_pushliteral( L, "Linda"); | ||
1216 | lua_setfield( L, -2, "__metatable"); | ||
1217 | |||
1218 | lua_pushcfunction( L, LG_linda_tostring); | ||
1219 | lua_setfield( L, -2, "__tostring"); | ||
1220 | |||
1221 | // Decoda __towatch support | ||
1222 | lua_pushcfunction( L, LG_linda_towatch); | ||
1223 | lua_setfield( L, -2, "__towatch"); | ||
1224 | |||
1225 | lua_pushcfunction( L, LG_linda_concat); | ||
1226 | lua_setfield( L, -2, "__concat"); | ||
1227 | |||
1228 | // protected calls, to ensure associated keeper is always released even in case of error | ||
1229 | // all function are the protected call wrapper, where the actual operation is provided as upvalue | ||
1230 | // note that this kind of thing can break function lookup as we use the function pointer here and there | ||
1231 | |||
1232 | lua_pushcfunction( L, LG_linda_send); | ||
1233 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
1234 | lua_setfield( L, -2, "send"); | ||
1235 | |||
1236 | lua_pushcfunction( L, LG_linda_receive); | ||
1237 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
1238 | lua_setfield( L, -2, "receive"); | ||
1239 | |||
1240 | lua_pushcfunction( L, LG_linda_limit); | ||
1241 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
1242 | lua_setfield( L, -2, "limit"); | ||
1243 | |||
1244 | lua_pushcfunction( L, LG_linda_set); | ||
1245 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
1246 | lua_setfield( L, -2, "set"); | ||
1247 | |||
1248 | lua_pushcfunction( L, LG_linda_count); | ||
1249 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
1250 | lua_setfield( L, -2, "count"); | ||
1251 | |||
1252 | lua_pushcfunction( L, LG_linda_get); | ||
1253 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
1254 | lua_setfield( L, -2, "get"); | ||
1255 | |||
1256 | lua_pushcfunction( L, LG_linda_cancel); | ||
1257 | lua_setfield( L, -2, "cancel"); | ||
1258 | |||
1259 | lua_pushcfunction( L, LG_linda_deep); | ||
1260 | lua_setfield( L, -2, "deep"); | ||
1261 | |||
1262 | lua_pushcfunction( L, LG_linda_dump); | ||
1263 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
1264 | lua_setfield( L, -2, "dump"); | ||
1265 | |||
1266 | // some constants | ||
1267 | lua_pushliteral( L, BATCH_SENTINEL); | ||
1268 | lua_setfield(L, -2, "batched"); | ||
1269 | |||
1270 | push_unique_key( L, NIL_SENTINEL); | ||
1271 | lua_setfield(L, -2, "null"); | ||
1272 | |||
1273 | luaG_pushdeepversion( L); | ||
1274 | STACK_END( L, 2); | ||
1275 | return NULL; | ||
1276 | } | ||
1277 | |||
1278 | case eDO_module: | ||
1279 | // linda is a special case because we know lanes must be loaded from the main lua state | ||
1280 | // to be able to ever get here, so we know it will remain loaded as long a the main state is around | ||
1281 | // in other words, forever. | ||
1282 | default: | ||
1283 | { | ||
1284 | return NULL; | ||
1285 | } | ||
1286 | } | ||
1287 | } | ||
1288 | |||
1289 | /* | ||
1290 | * ud = lanes.linda( [name[,group]]) | ||
1291 | * | ||
1292 | * returns a linda object, or raises an error if creation failed | ||
1293 | */ | ||
1294 | LUAG_FUNC( linda) | ||
1295 | { | ||
1296 | int const top = lua_gettop( L); | ||
1297 | luaL_argcheck( L, top <= 2, top, "too many arguments"); | ||
1298 | if( top == 1) | ||
1299 | { | ||
1300 | int const t = lua_type( L, 1); | ||
1301 | luaL_argcheck( L, t == LUA_TSTRING || t == LUA_TNUMBER, 1, "wrong parameter (should be a string or a number)"); | ||
1302 | } | ||
1303 | else if( top == 2) | ||
1304 | { | ||
1305 | luaL_checktype( L, 1, LUA_TSTRING); | ||
1306 | luaL_checktype( L, 2, LUA_TNUMBER); | ||
1307 | } | ||
1308 | return luaG_newdeepuserdata( L, linda_id); | ||
1309 | } | ||
1310 | |||
1311 | /* | ||
1312 | * ############################################################################################### | ||
1313 | * ########################################## Finalizer ########################################## | 305 | * ########################################## Finalizer ########################################## |
1314 | * ############################################################################################### | 306 | * ############################################################################################### |
1315 | */ | 307 | */ |
@@ -3000,6 +1992,7 @@ LUAG_FUNC( wakeup_conv ) | |||
3000 | * ############################################################################################### | 1992 | * ############################################################################################### |
3001 | */ | 1993 | */ |
3002 | 1994 | ||
1995 | extern int LG_linda( lua_State* L); | ||
3003 | static const struct luaL_Reg lanes_functions [] = { | 1996 | static const struct luaL_Reg lanes_functions [] = { |
3004 | {"linda", LG_linda}, | 1997 | {"linda", LG_linda}, |
3005 | {"now_secs", LG_now_secs}, | 1998 | {"now_secs", LG_now_secs}, |
@@ -3012,7 +2005,6 @@ static const struct luaL_Reg lanes_functions [] = { | |||
3012 | {NULL, NULL} | 2005 | {NULL, NULL} |
3013 | }; | 2006 | }; |
3014 | 2007 | ||
3015 | |||
3016 | /* | 2008 | /* |
3017 | * One-time initializations | 2009 | * One-time initializations |
3018 | * settings table it at position 1 on the stack | 2010 | * settings table it at position 1 on the stack |
diff --git a/src/lanes_private.h b/src/lanes_private.h new file mode 100644 index 0000000..a7e21d7 --- /dev/null +++ b/src/lanes_private.h | |||
@@ -0,0 +1,117 @@ | |||
1 | #if !defined __lanes_private_h__ | ||
2 | #define __lanes_private_h__ 1 | ||
3 | |||
4 | #include "uniquekey.h" | ||
5 | |||
6 | /* | ||
7 | * Lane cancellation request modes | ||
8 | */ | ||
9 | enum e_cancel_request | ||
10 | { | ||
11 | CANCEL_NONE, // no pending cancel request | ||
12 | CANCEL_SOFT, // user wants the lane to cancel itself manually on cancel_test() | ||
13 | CANCEL_HARD // user wants the lane to be interrupted (meaning code won't return from those functions) from inside linda:send/receive calls | ||
14 | }; | ||
15 | |||
16 | // NOTE: values to be changed by either thread, during execution, without | ||
17 | // locking, are marked "volatile" | ||
18 | // | ||
19 | struct s_Lane | ||
20 | { | ||
21 | THREAD_T thread; | ||
22 | // | ||
23 | // M: sub-thread OS thread | ||
24 | // S: not used | ||
25 | |||
26 | char const* debug_name; | ||
27 | |||
28 | lua_State* L; | ||
29 | Universe* U; | ||
30 | // | ||
31 | // M: prepares the state, and reads results | ||
32 | // S: while S is running, M must keep out of modifying the state | ||
33 | |||
34 | volatile enum e_status status; | ||
35 | // | ||
36 | // M: sets to PENDING (before launching) | ||
37 | // S: updates -> RUNNING/WAITING -> DONE/ERROR_ST/CANCELLED | ||
38 | |||
39 | SIGNAL_T* volatile waiting_on; | ||
40 | // | ||
41 | // When status is WAITING, points on the linda's signal the thread waits on, else NULL | ||
42 | |||
43 | volatile enum e_cancel_request cancel_request; | ||
44 | // | ||
45 | // M: sets to FALSE, flags TRUE for cancel request | ||
46 | // S: reads to see if cancel is requested | ||
47 | |||
48 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
49 | SIGNAL_T done_signal; | ||
50 | // | ||
51 | // M: Waited upon at lane ending (if Posix with no PTHREAD_TIMEDJOIN) | ||
52 | // S: sets the signal once cancellation is noticed (avoids a kill) | ||
53 | |||
54 | MUTEX_T done_lock; | ||
55 | // | ||
56 | // Lock required by 'done_signal' condition variable, protecting | ||
57 | // lane status changes to DONE/ERROR_ST/CANCELLED. | ||
58 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
59 | |||
60 | volatile enum | ||
61 | { | ||
62 | NORMAL, // normal master side state | ||
63 | KILLED // issued an OS kill | ||
64 | } mstatus; | ||
65 | // | ||
66 | // M: sets to NORMAL, if issued a kill changes to KILLED | ||
67 | // S: not used | ||
68 | |||
69 | struct s_Lane* volatile selfdestruct_next; | ||
70 | // | ||
71 | // M: sets to non-NULL if facing lane handle '__gc' cycle but the lane | ||
72 | // is still running | ||
73 | // S: cleans up after itself if non-NULL at lane exit | ||
74 | |||
75 | #if HAVE_LANE_TRACKING | ||
76 | struct s_Lane* volatile tracking_next; | ||
77 | #endif // HAVE_LANE_TRACKING | ||
78 | // | ||
79 | // For tracking only | ||
80 | }; | ||
81 | typedef struct s_Lane Lane; | ||
82 | |||
83 | // To allow free-running threads (longer lifespan than the handle's) | ||
84 | // 'Lane' are malloc/free'd and the handle only carries a pointer. | ||
85 | // This is not deep userdata since the handle's not portable among lanes. | ||
86 | // | ||
87 | #define lua_toLane( L, i) (*((Lane**) luaL_checkudata( L, i, "Lane"))) | ||
88 | |||
89 | // crc64/we of string "CANCEL_ERROR" generated at http://www.nitrxgen.net/hashgen/ | ||
90 | static DECLARE_CONST_UNIQUE_KEY(CANCEL_ERROR, 0xe97d41626cc97577); // 'cancel_error' sentinel | ||
91 | |||
92 | // crc64/we of string "CANCEL_TEST_KEY" generated at http://www.nitrxgen.net/hashgen/ | ||
93 | static DECLARE_CONST_UNIQUE_KEY(CANCEL_TEST_KEY, 0xe66f5960c57d133a); // used as registry key | ||
94 | |||
95 | static inline Lane* get_lane_from_registry( lua_State* L) | ||
96 | { | ||
97 | Lane* s; | ||
98 | STACK_GROW( L, 1); | ||
99 | STACK_CHECK( L); | ||
100 | push_unique_key( L, CANCEL_TEST_KEY); | ||
101 | lua_rawget( L, LUA_REGISTRYINDEX); | ||
102 | s = lua_touserdata( L, -1); // lightuserdata (true 's_lane' pointer) / nil | ||
103 | lua_pop( L, 1); | ||
104 | STACK_END( L, 0); | ||
105 | return s; | ||
106 | } | ||
107 | |||
108 | static inline int cancel_error( lua_State* L) | ||
109 | { | ||
110 | STACK_GROW( L, 1); | ||
111 | push_unique_key( L, CANCEL_ERROR); // special error value | ||
112 | return lua_error( L); // doesn't return | ||
113 | } | ||
114 | |||
115 | |||
116 | |||
117 | #endif // __lanes_private_h__ \ No newline at end of file | ||
diff --git a/src/linda.c b/src/linda.c new file mode 100644 index 0000000..98d2a8e --- /dev/null +++ b/src/linda.c | |||
@@ -0,0 +1,934 @@ | |||
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 | |||
35 | #include "threading.h" | ||
36 | #include "compat.h" | ||
37 | #include "tools.h" | ||
38 | #include "universe.h" | ||
39 | #include "keeper.h" | ||
40 | #include "deep.h" | ||
41 | #include "lanes_private.h" | ||
42 | |||
43 | /* | ||
44 | * Actual data is kept within a keeper state, which is hashed by the 's_Linda' | ||
45 | * pointer (which is same to all userdatas pointing to it). | ||
46 | */ | ||
47 | struct s_Linda | ||
48 | { | ||
49 | SIGNAL_T read_happened; | ||
50 | SIGNAL_T write_happened; | ||
51 | Universe* U; // the universe this linda belongs to | ||
52 | ptrdiff_t group; // a group to control keeper allocation between lindas | ||
53 | enum e_cancel_request simulate_cancel; | ||
54 | char name[1]; | ||
55 | }; | ||
56 | #define LINDA_KEEPER_HASHSEED( linda) (linda->group ? linda->group : (ptrdiff_t)linda) | ||
57 | |||
58 | static void* linda_id( lua_State*, DeepOp); | ||
59 | |||
60 | static inline struct s_Linda* lua_toLinda( lua_State* L, int idx_) | ||
61 | { | ||
62 | struct s_Linda* linda = (struct s_Linda*) luaG_todeep( L, linda_id, idx_); | ||
63 | luaL_argcheck( L, linda != NULL, idx_, "expecting a linda object"); | ||
64 | return linda; | ||
65 | } | ||
66 | |||
67 | static void check_key_types( lua_State* L, int start_, int end_) | ||
68 | { | ||
69 | int i; | ||
70 | for( i = start_; i <= end_; ++ i) | ||
71 | { | ||
72 | int t = lua_type( L, i); | ||
73 | if( t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA) | ||
74 | { | ||
75 | continue; | ||
76 | } | ||
77 | (void) luaL_error( L, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", i); | ||
78 | } | ||
79 | } | ||
80 | |||
81 | LUAG_FUNC( linda_protected_call) | ||
82 | { | ||
83 | int rc = LUA_OK; | ||
84 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
85 | |||
86 | // acquire the keeper | ||
87 | Keeper* K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED(linda)); | ||
88 | lua_State* KL = K ? K->L : NULL; // need to do this for 'STACK_CHECK' | ||
89 | if( KL == NULL) return 0; | ||
90 | |||
91 | // retrieve the actual function to be called and move it before the arguments | ||
92 | lua_pushvalue( L, lua_upvalueindex( 1)); | ||
93 | lua_insert( L, 1); | ||
94 | // do a protected call | ||
95 | rc = lua_pcall( L, lua_gettop( L) - 1, LUA_MULTRET, 0); | ||
96 | |||
97 | // release the keeper | ||
98 | keeper_release( K); | ||
99 | |||
100 | // if there was an error, forward it | ||
101 | if( rc != LUA_OK) | ||
102 | { | ||
103 | return lua_error( L); | ||
104 | } | ||
105 | // return whatever the actual operation provided | ||
106 | return lua_gettop( L); | ||
107 | } | ||
108 | |||
109 | /* | ||
110 | * bool= linda_send( linda_ud, [timeout_secs=-1,] [linda.null,] key_num|str|bool|lightuserdata, ... ) | ||
111 | * | ||
112 | * Send one or more values to a Linda. If there is a limit, all values must fit. | ||
113 | * | ||
114 | * Returns: 'true' if the value was queued | ||
115 | * 'false' for timeout (only happens when the queue size is limited) | ||
116 | * nil, CANCEL_ERROR if cancelled | ||
117 | */ | ||
118 | LUAG_FUNC( linda_send) | ||
119 | { | ||
120 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
121 | bool_t ret = FALSE; | ||
122 | enum e_cancel_request cancel = CANCEL_NONE; | ||
123 | int pushed; | ||
124 | time_d timeout = -1.0; | ||
125 | uint_t key_i = 2; // index of first key, if timeout not there | ||
126 | bool_t as_nil_sentinel; // if not NULL, send() will silently send a single nil if nothing is provided | ||
127 | |||
128 | if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion | ||
129 | { | ||
130 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); | ||
131 | ++ key_i; | ||
132 | } | ||
133 | else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key | ||
134 | { | ||
135 | ++ key_i; | ||
136 | } | ||
137 | |||
138 | as_nil_sentinel = equal_unique_key( L, key_i, NIL_SENTINEL); | ||
139 | if( as_nil_sentinel) | ||
140 | { | ||
141 | // the real key to send data to is after the NIL_SENTINEL marker | ||
142 | ++ key_i; | ||
143 | } | ||
144 | |||
145 | // make sure the key is of a valid type | ||
146 | check_key_types( L, key_i, key_i); | ||
147 | |||
148 | STACK_GROW( L, 1); | ||
149 | |||
150 | // make sure there is something to send | ||
151 | if( (uint_t)lua_gettop( L) == key_i) | ||
152 | { | ||
153 | if( as_nil_sentinel) | ||
154 | { | ||
155 | // send a single nil if nothing is provided | ||
156 | push_unique_key( L, NIL_SENTINEL); | ||
157 | } | ||
158 | else | ||
159 | { | ||
160 | return luaL_error( L, "no data to send"); | ||
161 | } | ||
162 | } | ||
163 | |||
164 | // convert nils to some special non-nil sentinel in sent values | ||
165 | keeper_toggle_nil_sentinels( L, key_i + 1, eLM_ToKeeper); | ||
166 | |||
167 | { | ||
168 | bool_t try_again = TRUE; | ||
169 | Lane* const s = get_lane_from_registry( L); | ||
170 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
171 | lua_State* KL = K ? K->L : NULL; // need to do this for 'STACK_CHECK' | ||
172 | if( KL == NULL) return 0; | ||
173 | STACK_CHECK( KL); | ||
174 | for( ;;) | ||
175 | { | ||
176 | if( s != NULL) | ||
177 | { | ||
178 | cancel = s->cancel_request; | ||
179 | } | ||
180 | cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; | ||
181 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | ||
182 | if( !try_again || cancel != CANCEL_NONE) | ||
183 | { | ||
184 | pushed = 0; | ||
185 | break; | ||
186 | } | ||
187 | |||
188 | STACK_MID( KL, 0); | ||
189 | pushed = keeper_call( linda->U, KL, KEEPER_API( send), L, linda, key_i); | ||
190 | if( pushed < 0) | ||
191 | { | ||
192 | break; | ||
193 | } | ||
194 | ASSERT_L( pushed == 1); | ||
195 | |||
196 | ret = lua_toboolean( L, -1); | ||
197 | lua_pop( L, 1); | ||
198 | |||
199 | if( ret) | ||
200 | { | ||
201 | // Wake up ALL waiting threads | ||
202 | SIGNAL_ALL( &linda->write_happened); | ||
203 | break; | ||
204 | } | ||
205 | |||
206 | // instant timout to bypass the wait syscall | ||
207 | if( timeout == 0.0) | ||
208 | { | ||
209 | break; /* no wait; instant timeout */ | ||
210 | } | ||
211 | |||
212 | // storage limit hit, wait until timeout or signalled that we should try again | ||
213 | { | ||
214 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | ||
215 | if( s != NULL) | ||
216 | { | ||
217 | // change status of lane to "waiting" | ||
218 | prev_status = s->status; // RUNNING, most likely | ||
219 | ASSERT_L( prev_status == RUNNING); // but check, just in case | ||
220 | s->status = WAITING; | ||
221 | ASSERT_L( s->waiting_on == NULL); | ||
222 | s->waiting_on = &linda->read_happened; | ||
223 | } | ||
224 | // could not send because no room: wait until some data was read before trying again, or until timeout is reached | ||
225 | try_again = SIGNAL_WAIT( &linda->read_happened, &K->keeper_cs, timeout); | ||
226 | if( s != NULL) | ||
227 | { | ||
228 | s->waiting_on = NULL; | ||
229 | s->status = prev_status; | ||
230 | } | ||
231 | } | ||
232 | } | ||
233 | STACK_END( KL, 0); | ||
234 | } | ||
235 | |||
236 | if( pushed < 0) | ||
237 | { | ||
238 | return luaL_error( L, "tried to copy unsupported types"); | ||
239 | } | ||
240 | |||
241 | switch( cancel) | ||
242 | { | ||
243 | case CANCEL_SOFT: | ||
244 | // if user wants to soft-cancel, the call returns lanes.cancel_error | ||
245 | push_unique_key( L, CANCEL_ERROR); | ||
246 | return 1; | ||
247 | |||
248 | case CANCEL_HARD: | ||
249 | // raise an error interrupting execution only in case of hard cancel | ||
250 | return cancel_error( L); // raises an error and doesn't return | ||
251 | |||
252 | default: | ||
253 | lua_pushboolean( L, ret); // true (success) or false (timeout) | ||
254 | return 1; | ||
255 | } | ||
256 | } | ||
257 | |||
258 | |||
259 | /* | ||
260 | * 2 modes of operation | ||
261 | * [val, key]= linda_receive( linda_ud, [timeout_secs_num=-1], key_num|str|bool|lightuserdata [, ...] ) | ||
262 | * Consumes a single value from the Linda, in any key. | ||
263 | * Returns: received value (which is consumed from the slot), and the key which had it | ||
264 | |||
265 | * [val1, ... valCOUNT]= linda_receive( linda_ud, [timeout_secs_num=-1], linda.batched, key_num|str|bool|lightuserdata, min_COUNT[, max_COUNT]) | ||
266 | * Consumes between min_COUNT and max_COUNT values from the linda, from a single key. | ||
267 | * returns the actual consumed values, or nil if there weren't enough values to consume | ||
268 | * | ||
269 | */ | ||
270 | #define BATCH_SENTINEL "270e6c9d-280f-4983-8fee-a7ecdda01475" | ||
271 | LUAG_FUNC( linda_receive) | ||
272 | { | ||
273 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
274 | int pushed, expected_pushed_min, expected_pushed_max; | ||
275 | enum e_cancel_request cancel = CANCEL_NONE; | ||
276 | keeper_api_t keeper_receive; | ||
277 | |||
278 | time_d timeout = -1.0; | ||
279 | uint_t key_i = 2; | ||
280 | |||
281 | if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion | ||
282 | { | ||
283 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); | ||
284 | ++ key_i; | ||
285 | } | ||
286 | else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key | ||
287 | { | ||
288 | ++ key_i; | ||
289 | } | ||
290 | |||
291 | // are we in batched mode? | ||
292 | { | ||
293 | int is_batched; | ||
294 | lua_pushliteral( L, BATCH_SENTINEL); | ||
295 | is_batched = lua501_equal( L, key_i, -1); | ||
296 | lua_pop( L, 1); | ||
297 | if( is_batched) | ||
298 | { | ||
299 | // no need to pass linda.batched in the keeper state | ||
300 | ++ key_i; | ||
301 | // make sure the keys are of a valid type | ||
302 | check_key_types( L, key_i, key_i); | ||
303 | // receive multiple values from a single slot | ||
304 | keeper_receive = KEEPER_API( receive_batched); | ||
305 | // we expect a user-defined amount of return value | ||
306 | expected_pushed_min = (int)luaL_checkinteger( L, key_i + 1); | ||
307 | expected_pushed_max = (int)luaL_optinteger( L, key_i + 2, expected_pushed_min); | ||
308 | // don't forget to count the key in addition to the values | ||
309 | ++ expected_pushed_min; | ||
310 | ++ expected_pushed_max; | ||
311 | if( expected_pushed_min > expected_pushed_max) | ||
312 | { | ||
313 | return luaL_error( L, "batched min/max error"); | ||
314 | } | ||
315 | } | ||
316 | else | ||
317 | { | ||
318 | // make sure the keys are of a valid type | ||
319 | check_key_types( L, key_i, lua_gettop( L)); | ||
320 | // receive a single value, checking multiple slots | ||
321 | keeper_receive = KEEPER_API( receive); | ||
322 | // we expect a single (value, key) pair of returned values | ||
323 | expected_pushed_min = expected_pushed_max = 2; | ||
324 | } | ||
325 | } | ||
326 | |||
327 | { | ||
328 | bool_t try_again = TRUE; | ||
329 | Lane* const s = get_lane_from_registry( L); | ||
330 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
331 | if( K == NULL) return 0; | ||
332 | for( ;;) | ||
333 | { | ||
334 | if( s != NULL) | ||
335 | { | ||
336 | cancel = s->cancel_request; | ||
337 | } | ||
338 | cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; | ||
339 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | ||
340 | if( !try_again || cancel != CANCEL_NONE) | ||
341 | { | ||
342 | pushed = 0; | ||
343 | break; | ||
344 | } | ||
345 | |||
346 | // all arguments of receive() but the first are passed to the keeper's receive function | ||
347 | pushed = keeper_call( linda->U, K->L, keeper_receive, L, linda, key_i); | ||
348 | if( pushed < 0) | ||
349 | { | ||
350 | break; | ||
351 | } | ||
352 | if( pushed > 0) | ||
353 | { | ||
354 | ASSERT_L( pushed >= expected_pushed_min && pushed <= expected_pushed_max); | ||
355 | // replace sentinels with real nils | ||
356 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper); | ||
357 | // To be done from within the 'K' locking area | ||
358 | // | ||
359 | SIGNAL_ALL( &linda->read_happened); | ||
360 | break; | ||
361 | } | ||
362 | |||
363 | if( timeout == 0.0) | ||
364 | { | ||
365 | break; /* instant timeout */ | ||
366 | } | ||
367 | |||
368 | // nothing received, wait until timeout or signalled that we should try again | ||
369 | { | ||
370 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | ||
371 | if( s != NULL) | ||
372 | { | ||
373 | // change status of lane to "waiting" | ||
374 | prev_status = s->status; // RUNNING, most likely | ||
375 | ASSERT_L( prev_status == RUNNING); // but check, just in case | ||
376 | s->status = WAITING; | ||
377 | ASSERT_L( s->waiting_on == NULL); | ||
378 | s->waiting_on = &linda->write_happened; | ||
379 | } | ||
380 | // not enough data to read: wakeup when data was sent, or when timeout is reached | ||
381 | try_again = SIGNAL_WAIT( &linda->write_happened, &K->keeper_cs, timeout); | ||
382 | if( s != NULL) | ||
383 | { | ||
384 | s->waiting_on = NULL; | ||
385 | s->status = prev_status; | ||
386 | } | ||
387 | } | ||
388 | } | ||
389 | } | ||
390 | |||
391 | if( pushed < 0) | ||
392 | { | ||
393 | return luaL_error( L, "tried to copy unsupported types"); | ||
394 | } | ||
395 | |||
396 | switch( cancel) | ||
397 | { | ||
398 | case CANCEL_SOFT: | ||
399 | // if user wants to soft-cancel, the call returns CANCEL_ERROR | ||
400 | push_unique_key( L, CANCEL_ERROR); | ||
401 | return 1; | ||
402 | |||
403 | case CANCEL_HARD: | ||
404 | // raise an error interrupting execution only in case of hard cancel | ||
405 | return cancel_error( L); // raises an error and doesn't return | ||
406 | |||
407 | default: | ||
408 | return pushed; | ||
409 | } | ||
410 | } | ||
411 | |||
412 | |||
413 | /* | ||
414 | * [true|lanes.cancel_error] = linda_set( linda_ud, key_num|str|bool|lightuserdata [, value [, ...]]) | ||
415 | * | ||
416 | * Set one or more value to Linda. | ||
417 | * TODO: what do we do if we set to non-nil and limit is 0? | ||
418 | * | ||
419 | * Existing slot value is replaced, and possible queued entries removed. | ||
420 | */ | ||
421 | LUAG_FUNC( linda_set) | ||
422 | { | ||
423 | struct s_Linda* const linda = lua_toLinda( L, 1); | ||
424 | int pushed; | ||
425 | bool_t has_value = lua_gettop( L) > 2; | ||
426 | |||
427 | // make sure the key is of a valid type (throws an error if not the case) | ||
428 | check_key_types( L, 2, 2); | ||
429 | |||
430 | { | ||
431 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
432 | |||
433 | if( linda->simulate_cancel == CANCEL_NONE) | ||
434 | { | ||
435 | if( has_value) | ||
436 | { | ||
437 | // convert nils to some special non-nil sentinel in sent values | ||
438 | keeper_toggle_nil_sentinels( L, 3, eLM_ToKeeper); | ||
439 | } | ||
440 | pushed = keeper_call( linda->U, K->L, KEEPER_API( set), L, linda, 2); | ||
441 | if( pushed >= 0) // no error? | ||
442 | { | ||
443 | ASSERT_L( pushed == 0 || pushed == 1); | ||
444 | |||
445 | if( has_value) | ||
446 | { | ||
447 | // we put some data in the slot, tell readers that they should wake | ||
448 | SIGNAL_ALL( &linda->write_happened); // To be done from within the 'K' locking area | ||
449 | } | ||
450 | if( pushed == 1) | ||
451 | { | ||
452 | // the key was full, but it is no longer the case, tell writers they should wake | ||
453 | ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); | ||
454 | SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area | ||
455 | } | ||
456 | } | ||
457 | } | ||
458 | else // linda is cancelled | ||
459 | { | ||
460 | // do nothing and return lanes.cancel_error | ||
461 | push_unique_key( L, CANCEL_ERROR); | ||
462 | pushed = 1; | ||
463 | } | ||
464 | } | ||
465 | |||
466 | // must trigger any error after keeper state has been released | ||
467 | return (pushed < 0) ? luaL_error( L, "tried to copy unsupported types") : pushed; | ||
468 | } | ||
469 | |||
470 | |||
471 | /* | ||
472 | * [val] = linda_count( linda_ud, [key [, ...]]) | ||
473 | * | ||
474 | * Get a count of the pending elements in the specified keys | ||
475 | */ | ||
476 | LUAG_FUNC( linda_count) | ||
477 | { | ||
478 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
479 | int pushed; | ||
480 | |||
481 | // make sure the keys are of a valid type | ||
482 | check_key_types( L, 2, lua_gettop( L)); | ||
483 | |||
484 | { | ||
485 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
486 | pushed = keeper_call( linda->U, K->L, KEEPER_API( count), L, linda, 2); | ||
487 | if( pushed < 0) | ||
488 | { | ||
489 | return luaL_error( L, "tried to count an invalid key"); | ||
490 | } | ||
491 | } | ||
492 | return pushed; | ||
493 | } | ||
494 | |||
495 | |||
496 | /* | ||
497 | * [val [, ...]] = linda_get( linda_ud, key_num|str|bool|lightuserdata [, count = 1]) | ||
498 | * | ||
499 | * Get one or more values from Linda. | ||
500 | */ | ||
501 | LUAG_FUNC( linda_get) | ||
502 | { | ||
503 | struct s_Linda* const linda = lua_toLinda( L, 1); | ||
504 | int pushed; | ||
505 | lua_Integer count = luaL_optinteger( L, 3, 1); | ||
506 | luaL_argcheck( L, count >= 1, 3, "count should be >= 1"); | ||
507 | luaL_argcheck( L, lua_gettop( L) <= 3, 4, "too many arguments"); | ||
508 | |||
509 | // make sure the key is of a valid type (throws an error if not the case) | ||
510 | check_key_types( L, 2, 2); | ||
511 | { | ||
512 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
513 | |||
514 | if( linda->simulate_cancel == CANCEL_NONE) | ||
515 | { | ||
516 | pushed = keeper_call( linda->U, K->L, KEEPER_API( get), L, linda, 2); | ||
517 | if( pushed > 0) | ||
518 | { | ||
519 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper); | ||
520 | } | ||
521 | } | ||
522 | else // linda is cancelled | ||
523 | { | ||
524 | // do nothing and return lanes.cancel_error | ||
525 | push_unique_key( L, CANCEL_ERROR); | ||
526 | pushed = 1; | ||
527 | } | ||
528 | // an error can be raised if we attempt to read an unregistered function | ||
529 | if( pushed < 0) | ||
530 | { | ||
531 | return luaL_error( L, "tried to copy unsupported types"); | ||
532 | } | ||
533 | } | ||
534 | |||
535 | return pushed; | ||
536 | } | ||
537 | |||
538 | |||
539 | /* | ||
540 | * [true] = linda_limit( linda_ud, key_num|str|bool|lightuserdata, int) | ||
541 | * | ||
542 | * Set limit to 1 Linda keys. | ||
543 | * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so | ||
544 | */ | ||
545 | LUAG_FUNC( linda_limit) | ||
546 | { | ||
547 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
548 | int pushed; | ||
549 | |||
550 | // make sure we got 3 arguments: the linda, a key and a limit | ||
551 | luaL_argcheck( L, lua_gettop( L) == 3, 2, "wrong number of arguments"); | ||
552 | // make sure we got a numeric limit | ||
553 | luaL_checknumber( L, 3); | ||
554 | // make sure the key is of a valid type | ||
555 | check_key_types( L, 2, 2); | ||
556 | |||
557 | { | ||
558 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
559 | |||
560 | if( linda->simulate_cancel == CANCEL_NONE) | ||
561 | { | ||
562 | pushed = keeper_call( linda->U, K->L, KEEPER_API( limit), L, linda, 2); | ||
563 | ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads | ||
564 | if( pushed == 1) | ||
565 | { | ||
566 | ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); | ||
567 | SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area | ||
568 | } | ||
569 | } | ||
570 | else // linda is cancelled | ||
571 | { | ||
572 | // do nothing and return lanes.cancel_error | ||
573 | push_unique_key( L, CANCEL_ERROR); | ||
574 | pushed = 1; | ||
575 | } | ||
576 | } | ||
577 | // propagate pushed boolean if any | ||
578 | return pushed; | ||
579 | } | ||
580 | |||
581 | |||
582 | /* | ||
583 | * (void) = linda_cancel( linda_ud, "read"|"write"|"both"|"none") | ||
584 | * | ||
585 | * Signal linda so that waiting threads wake up as if their own lane was cancelled | ||
586 | */ | ||
587 | LUAG_FUNC( linda_cancel) | ||
588 | { | ||
589 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
590 | char const* who = luaL_optstring( L, 2, "both"); | ||
591 | |||
592 | // make sure we got 3 arguments: the linda, a key and a limit | ||
593 | luaL_argcheck( L, lua_gettop( L) <= 2, 2, "wrong number of arguments"); | ||
594 | |||
595 | linda->simulate_cancel = CANCEL_SOFT; | ||
596 | if( strcmp( who, "both") == 0) // tell everyone writers to wake up | ||
597 | { | ||
598 | SIGNAL_ALL( &linda->write_happened); | ||
599 | SIGNAL_ALL( &linda->read_happened); | ||
600 | } | ||
601 | else if( strcmp( who, "none") == 0) // reset flag | ||
602 | { | ||
603 | linda->simulate_cancel = CANCEL_NONE; | ||
604 | } | ||
605 | else if( strcmp( who, "read") == 0) // tell blocked readers to wake up | ||
606 | { | ||
607 | SIGNAL_ALL( &linda->write_happened); | ||
608 | } | ||
609 | else if( strcmp( who, "write") == 0) // tell blocked writers to wake up | ||
610 | { | ||
611 | SIGNAL_ALL( &linda->read_happened); | ||
612 | } | ||
613 | else | ||
614 | { | ||
615 | return luaL_error( L, "unknown wake hint '%s'", who); | ||
616 | } | ||
617 | return 0; | ||
618 | } | ||
619 | |||
620 | |||
621 | /* | ||
622 | * lightuserdata= linda_deep( linda_ud ) | ||
623 | * | ||
624 | * Return the 'deep' userdata pointer, identifying the Linda. | ||
625 | * | ||
626 | * This is needed for using Lindas as key indices (timer system needs it); | ||
627 | * separately created proxies of the same underlying deep object will have | ||
628 | * different userdata and won't be known to be essentially the same deep one | ||
629 | * without this. | ||
630 | */ | ||
631 | LUAG_FUNC( linda_deep) | ||
632 | { | ||
633 | struct s_Linda* linda= lua_toLinda( L, 1); | ||
634 | lua_pushlightuserdata( L, linda); // just the address | ||
635 | return 1; | ||
636 | } | ||
637 | |||
638 | |||
639 | /* | ||
640 | * string = linda:__tostring( linda_ud) | ||
641 | * | ||
642 | * Return the stringification of a linda | ||
643 | * | ||
644 | * Useful for concatenation or debugging purposes | ||
645 | */ | ||
646 | |||
647 | static int linda_tostring( lua_State* L, int idx_, bool_t opt_) | ||
648 | { | ||
649 | struct s_Linda* linda = (struct s_Linda*) luaG_todeep( L, linda_id, idx_); | ||
650 | if( !opt_) | ||
651 | { | ||
652 | luaL_argcheck( L, linda, idx_, "expecting a linda object"); | ||
653 | } | ||
654 | if( linda != NULL) | ||
655 | { | ||
656 | char text[128]; | ||
657 | int len; | ||
658 | if( linda->name[0]) | ||
659 | len = sprintf( text, "Linda: %.*s", (int)sizeof(text) - 8, linda->name); | ||
660 | else | ||
661 | len = sprintf( text, "Linda: %p", linda); | ||
662 | lua_pushlstring( L, text, len); | ||
663 | return 1; | ||
664 | } | ||
665 | return 0; | ||
666 | } | ||
667 | |||
668 | LUAG_FUNC( linda_tostring) | ||
669 | { | ||
670 | return linda_tostring( L, 1, FALSE); | ||
671 | } | ||
672 | |||
673 | |||
674 | /* | ||
675 | * string = linda:__concat( a, b) | ||
676 | * | ||
677 | * Return the concatenation of a pair of items, one of them being a linda | ||
678 | * | ||
679 | * Useful for concatenation or debugging purposes | ||
680 | */ | ||
681 | LUAG_FUNC( linda_concat) | ||
682 | { // linda1? linda2? | ||
683 | bool_t atLeastOneLinda = FALSE; | ||
684 | // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both. | ||
685 | if( linda_tostring( L, 1, TRUE)) | ||
686 | { | ||
687 | atLeastOneLinda = TRUE; | ||
688 | lua_replace( L, 1); | ||
689 | } | ||
690 | if( linda_tostring( L, 2, TRUE)) | ||
691 | { | ||
692 | atLeastOneLinda = TRUE; | ||
693 | lua_replace( L, 2); | ||
694 | } | ||
695 | if( !atLeastOneLinda) // should not be possible | ||
696 | { | ||
697 | return luaL_error( L, "internal error: linda_concat called on non-Linda"); | ||
698 | } | ||
699 | lua_concat( L, 2); | ||
700 | return 1; | ||
701 | } | ||
702 | |||
703 | /* | ||
704 | * table = linda:dump() | ||
705 | * return a table listing all pending data inside the linda | ||
706 | */ | ||
707 | LUAG_FUNC( linda_dump) | ||
708 | { | ||
709 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
710 | ASSERT_L( linda->U == universe_get( L)); | ||
711 | return keeper_push_linda_storage( linda->U, L, linda, LINDA_KEEPER_HASHSEED( linda)); | ||
712 | } | ||
713 | |||
714 | /* | ||
715 | * table = linda:dump() | ||
716 | * return a table listing all pending data inside the linda | ||
717 | */ | ||
718 | LUAG_FUNC( linda_towatch) | ||
719 | { | ||
720 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
721 | int pushed; | ||
722 | ASSERT_L( linda->U == universe_get( L)); | ||
723 | pushed = keeper_push_linda_storage( linda->U, L, linda, LINDA_KEEPER_HASHSEED( linda)); | ||
724 | if( pushed == 0) | ||
725 | { | ||
726 | // if the linda is empty, don't return nil | ||
727 | pushed = linda_tostring( L, 1, FALSE); | ||
728 | } | ||
729 | return pushed; | ||
730 | } | ||
731 | |||
732 | /* | ||
733 | * Identity function of a shared userdata object. | ||
734 | * | ||
735 | * lightuserdata= linda_id( "new" [, ...] ) | ||
736 | * = linda_id( "delete", lightuserdata ) | ||
737 | * | ||
738 | * Creation and cleanup of actual 'deep' objects. 'luaG_...' will wrap them into | ||
739 | * regular userdata proxies, per each state using the deep data. | ||
740 | * | ||
741 | * tbl= linda_id( "metatable" ) | ||
742 | * | ||
743 | * Returns a metatable for the proxy objects ('__gc' method not needed; will | ||
744 | * be added by 'luaG_...') | ||
745 | * | ||
746 | * string= linda_id( "module") | ||
747 | * | ||
748 | * Returns the name of the module that a state should require | ||
749 | * in order to keep a handle on the shared library that exported the idfunc | ||
750 | * | ||
751 | * = linda_id( str, ... ) | ||
752 | * | ||
753 | * For any other strings, the ID function must not react at all. This allows | ||
754 | * future extensions of the system. | ||
755 | */ | ||
756 | static void* linda_id( lua_State* L, DeepOp op_) | ||
757 | { | ||
758 | switch( op_) | ||
759 | { | ||
760 | case eDO_new: | ||
761 | { | ||
762 | struct s_Linda* s; | ||
763 | size_t name_len = 0; | ||
764 | char const* linda_name = NULL; | ||
765 | unsigned long linda_group = 0; | ||
766 | // should have a string and/or a number of the stack as parameters (name and group) | ||
767 | switch( lua_gettop( L)) | ||
768 | { | ||
769 | default: // 0 | ||
770 | break; | ||
771 | |||
772 | case 1: // 1 parameter, either a name or a group | ||
773 | if( lua_type( L, -1) == LUA_TSTRING) | ||
774 | { | ||
775 | linda_name = lua_tolstring( L, -1, &name_len); | ||
776 | } | ||
777 | else | ||
778 | { | ||
779 | linda_group = (unsigned long) lua_tointeger( L, -1); | ||
780 | } | ||
781 | break; | ||
782 | |||
783 | case 2: // 2 parameters, a name and group, in that order | ||
784 | linda_name = lua_tolstring( L, -2, &name_len); | ||
785 | linda_group = (unsigned long) lua_tointeger( L, -1); | ||
786 | break; | ||
787 | } | ||
788 | |||
789 | /* The deep data is allocated separately of Lua stack; we might no | ||
790 | * longer be around when last reference to it is being released. | ||
791 | * One can use any memory allocation scheme. | ||
792 | * just don't use L's allocF because we don't know which state will get the honor of GCing the linda | ||
793 | */ | ||
794 | s = (struct s_Linda*) malloc( sizeof(struct s_Linda) + name_len); // terminating 0 is already included | ||
795 | if( s) | ||
796 | { | ||
797 | SIGNAL_INIT( &s->read_happened); | ||
798 | SIGNAL_INIT( &s->write_happened); | ||
799 | s->U = universe_get( L); | ||
800 | s->simulate_cancel = CANCEL_NONE; | ||
801 | s->group = linda_group << KEEPER_MAGIC_SHIFT; | ||
802 | s->name[0] = 0; | ||
803 | memcpy( s->name, linda_name, name_len ? name_len + 1 : 0); | ||
804 | } | ||
805 | return s; | ||
806 | } | ||
807 | |||
808 | case eDO_delete: | ||
809 | { | ||
810 | Keeper* K; | ||
811 | struct s_Linda* linda = lua_touserdata( L, 1); | ||
812 | ASSERT_L( linda); | ||
813 | |||
814 | // Clean associated structures in the keeper state. | ||
815 | K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
816 | if( K && K->L) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) | ||
817 | { | ||
818 | // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... | ||
819 | keeper_call( linda->U, K->L, KEEPER_API( clear), L, linda, 0); | ||
820 | } | ||
821 | keeper_release( K); | ||
822 | |||
823 | // There aren't any lanes waiting on these lindas, since all proxies have been gc'ed. Right? | ||
824 | SIGNAL_FREE( &linda->read_happened); | ||
825 | SIGNAL_FREE( &linda->write_happened); | ||
826 | free( linda); | ||
827 | return NULL; | ||
828 | } | ||
829 | |||
830 | case eDO_metatable: | ||
831 | { | ||
832 | |||
833 | STACK_CHECK( L); | ||
834 | lua_newtable( L); | ||
835 | // metatable is its own index | ||
836 | lua_pushvalue( L, -1); | ||
837 | lua_setfield( L, -2, "__index"); | ||
838 | |||
839 | // protect metatable from external access | ||
840 | lua_pushliteral( L, "Linda"); | ||
841 | lua_setfield( L, -2, "__metatable"); | ||
842 | |||
843 | lua_pushcfunction( L, LG_linda_tostring); | ||
844 | lua_setfield( L, -2, "__tostring"); | ||
845 | |||
846 | // Decoda __towatch support | ||
847 | lua_pushcfunction( L, LG_linda_towatch); | ||
848 | lua_setfield( L, -2, "__towatch"); | ||
849 | |||
850 | lua_pushcfunction( L, LG_linda_concat); | ||
851 | lua_setfield( L, -2, "__concat"); | ||
852 | |||
853 | // protected calls, to ensure associated keeper is always released even in case of error | ||
854 | // all function are the protected call wrapper, where the actual operation is provided as upvalue | ||
855 | // note that this kind of thing can break function lookup as we use the function pointer here and there | ||
856 | |||
857 | lua_pushcfunction( L, LG_linda_send); | ||
858 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
859 | lua_setfield( L, -2, "send"); | ||
860 | |||
861 | lua_pushcfunction( L, LG_linda_receive); | ||
862 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
863 | lua_setfield( L, -2, "receive"); | ||
864 | |||
865 | lua_pushcfunction( L, LG_linda_limit); | ||
866 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
867 | lua_setfield( L, -2, "limit"); | ||
868 | |||
869 | lua_pushcfunction( L, LG_linda_set); | ||
870 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
871 | lua_setfield( L, -2, "set"); | ||
872 | |||
873 | lua_pushcfunction( L, LG_linda_count); | ||
874 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
875 | lua_setfield( L, -2, "count"); | ||
876 | |||
877 | lua_pushcfunction( L, LG_linda_get); | ||
878 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
879 | lua_setfield( L, -2, "get"); | ||
880 | |||
881 | lua_pushcfunction( L, LG_linda_cancel); | ||
882 | lua_setfield( L, -2, "cancel"); | ||
883 | |||
884 | lua_pushcfunction( L, LG_linda_deep); | ||
885 | lua_setfield( L, -2, "deep"); | ||
886 | |||
887 | lua_pushcfunction( L, LG_linda_dump); | ||
888 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
889 | lua_setfield( L, -2, "dump"); | ||
890 | |||
891 | // some constants | ||
892 | lua_pushliteral( L, BATCH_SENTINEL); | ||
893 | lua_setfield(L, -2, "batched"); | ||
894 | |||
895 | push_unique_key( L, NIL_SENTINEL); | ||
896 | lua_setfield(L, -2, "null"); | ||
897 | |||
898 | luaG_pushdeepversion( L); | ||
899 | STACK_END( L, 2); | ||
900 | return NULL; | ||
901 | } | ||
902 | |||
903 | case eDO_module: | ||
904 | // linda is a special case because we know lanes must be loaded from the main lua state | ||
905 | // to be able to ever get here, so we know it will remain loaded as long a the main state is around | ||
906 | // in other words, forever. | ||
907 | default: | ||
908 | { | ||
909 | return NULL; | ||
910 | } | ||
911 | } | ||
912 | } | ||
913 | |||
914 | /* | ||
915 | * ud = lanes.linda( [name[,group]]) | ||
916 | * | ||
917 | * returns a linda object, or raises an error if creation failed | ||
918 | */ | ||
919 | LUAG_FUNC( linda) | ||
920 | { | ||
921 | int const top = lua_gettop( L); | ||
922 | luaL_argcheck( L, top <= 2, top, "too many arguments"); | ||
923 | if( top == 1) | ||
924 | { | ||
925 | int const t = lua_type( L, 1); | ||
926 | luaL_argcheck( L, t == LUA_TSTRING || t == LUA_TNUMBER, 1, "wrong parameter (should be a string or a number)"); | ||
927 | } | ||
928 | else if( top == 2) | ||
929 | { | ||
930 | luaL_checktype( L, 1, LUA_TSTRING); | ||
931 | luaL_checktype( L, 2, LUA_TNUMBER); | ||
932 | } | ||
933 | return luaG_newdeepuserdata( L, linda_id); | ||
934 | } | ||
diff --git a/src/tools.h b/src/tools.h index 2f78b73..84e323c 100644 --- a/src/tools.h +++ b/src/tools.h | |||
@@ -13,7 +13,7 @@ typedef struct s_Universe Universe; | |||
13 | 13 | ||
14 | // ################################################################################################ | 14 | // ################################################################################################ |
15 | 15 | ||
16 | #define LUAG_FUNC( func_name ) static int LG_##func_name( lua_State* L) | 16 | #define LUAG_FUNC( func_name) int LG_##func_name( lua_State* L) |
17 | 17 | ||
18 | #define luaG_optunsigned(L,i,d) ((uint_t) luaL_optinteger(L,i,d)) | 18 | #define luaG_optunsigned(L,i,d) ((uint_t) luaL_optinteger(L,i,d)) |
19 | #define luaG_tounsigned(L,i) ((uint_t) lua_tointeger(L,i)) | 19 | #define luaG_tounsigned(L,i) ((uint_t) lua_tointeger(L,i)) |