diff options
Diffstat (limited to 'src/lanes.c')
-rw-r--r-- | src/lanes.c | 696 |
1 files changed, 502 insertions, 194 deletions
diff --git a/src/lanes.c b/src/lanes.c index 0c943aa..513a006 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -51,7 +51,7 @@ | |||
51 | * ... | 51 | * ... |
52 | */ | 52 | */ |
53 | 53 | ||
54 | const char *VERSION= "2.1.0"; | 54 | char const* VERSION = "3.1.0"; |
55 | 55 | ||
56 | /* | 56 | /* |
57 | =============================================================================== | 57 | =============================================================================== |
@@ -139,7 +139,7 @@ struct s_lane { | |||
139 | // M: sets to FALSE, flags TRUE for cancel request | 139 | // M: sets to FALSE, flags TRUE for cancel request |
140 | // S: reads to see if cancel is requested | 140 | // S: reads to see if cancel is requested |
141 | 141 | ||
142 | #if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) ) | 142 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR |
143 | SIGNAL_T done_signal_; | 143 | SIGNAL_T done_signal_; |
144 | // | 144 | // |
145 | // M: Waited upon at lane ending (if Posix with no PTHREAD_TIMEDJOIN) | 145 | // M: Waited upon at lane ending (if Posix with no PTHREAD_TIMEDJOIN) |
@@ -149,7 +149,7 @@ struct s_lane { | |||
149 | // | 149 | // |
150 | // Lock required by 'done_signal' condition variable, protecting | 150 | // Lock required by 'done_signal' condition variable, protecting |
151 | // lane status changes to DONE/ERROR_ST/CANCELLED. | 151 | // lane status changes to DONE/ERROR_ST/CANCELLED. |
152 | #endif | 152 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
153 | 153 | ||
154 | volatile enum { | 154 | volatile enum { |
155 | NORMAL, // normal master side state | 155 | NORMAL, // normal master side state |
@@ -249,6 +249,20 @@ static void linda_id( lua_State*, char const * const which); | |||
249 | #define lua_toLinda(L,n) ((struct s_Linda *)luaG_todeep( L, linda_id, n )) | 249 | #define lua_toLinda(L,n) ((struct s_Linda *)luaG_todeep( L, linda_id, n )) |
250 | 250 | ||
251 | 251 | ||
252 | static void check_key_types( lua_State *L, int _start, int _end) | ||
253 | { | ||
254 | int i; | ||
255 | for( i = _start; i <= _end; ++ i) | ||
256 | { | ||
257 | int t = lua_type( L, i); | ||
258 | if( t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA) | ||
259 | { | ||
260 | continue; | ||
261 | } | ||
262 | luaL_error( L, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", i); | ||
263 | } | ||
264 | } | ||
265 | |||
252 | /* | 266 | /* |
253 | * bool= linda_send( linda_ud, [timeout_secs=-1,] key_num|str|bool|lightuserdata, ... ) | 267 | * bool= linda_send( linda_ud, [timeout_secs=-1,] key_num|str|bool|lightuserdata, ... ) |
254 | * | 268 | * |
@@ -271,15 +285,24 @@ LUAG_FUNC( linda_send) | |||
271 | if( lua_isnumber(L, 2)) | 285 | if( lua_isnumber(L, 2)) |
272 | { | 286 | { |
273 | timeout= SIGNAL_TIMEOUT_PREPARE( lua_tonumber(L,2) ); | 287 | timeout= SIGNAL_TIMEOUT_PREPARE( lua_tonumber(L,2) ); |
274 | key_i++; | 288 | ++ key_i; |
275 | } | 289 | } |
276 | else if( lua_isnil( L, 2)) | 290 | else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key |
277 | { | 291 | { |
278 | key_i++; | 292 | ++ key_i; |
279 | } | 293 | } |
280 | 294 | ||
281 | if( lua_isnil( L, key_i)) | 295 | // make sure the keys are of a valid type |
282 | luaL_error( L, "nil key" ); | 296 | check_key_types( L, key_i, key_i); |
297 | |||
298 | // make sure there is something to send | ||
299 | if( (uint_t)lua_gettop( L) == key_i) | ||
300 | { | ||
301 | luaL_error( L, "no data to send"); | ||
302 | } | ||
303 | |||
304 | // convert nils to some special non-nil sentinel in sent values | ||
305 | keeper_toggle_nil_sentinels( L, key_i + 1, 1); | ||
283 | 306 | ||
284 | STACK_GROW(L, 1); | 307 | STACK_GROW(L, 1); |
285 | { | 308 | { |
@@ -315,11 +338,11 @@ LUAG_FUNC( linda_send) | |||
315 | 338 | ||
316 | cancel = cancel_test( L); // testing here causes no delays | 339 | cancel = cancel_test( L); // testing here causes no delays |
317 | if (cancel) | 340 | if (cancel) |
341 | { | ||
318 | break; | 342 | break; |
343 | } | ||
319 | 344 | ||
320 | // Bugfix by Benoit Germain Dec-2009: change status of lane to "waiting" | 345 | // change status of lane to "waiting" |
321 | // | ||
322 | #if 1 | ||
323 | { | 346 | { |
324 | struct s_lane *s; | 347 | struct s_lane *s; |
325 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | 348 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings |
@@ -338,6 +361,7 @@ LUAG_FUNC( linda_send) | |||
338 | ASSERT_L( s->waiting_on == NULL); | 361 | ASSERT_L( s->waiting_on == NULL); |
339 | s->waiting_on = &linda->read_happened; | 362 | s->waiting_on = &linda->read_happened; |
340 | } | 363 | } |
364 | // could not send because no room: wait until some data was read before trying again, or until timeout is reached | ||
341 | if( !SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout)) | 365 | if( !SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout)) |
342 | { | 366 | { |
343 | if( s) | 367 | if( s) |
@@ -353,12 +377,6 @@ LUAG_FUNC( linda_send) | |||
353 | s->status = prev_status; | 377 | s->status = prev_status; |
354 | } | 378 | } |
355 | } | 379 | } |
356 | #else | ||
357 | // K lock will be released for the duration of wait and re-acquired | ||
358 | // | ||
359 | if( !SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout)) | ||
360 | break; // timeout | ||
361 | #endif | ||
362 | } | 380 | } |
363 | STACK_END( KL, 0) | 381 | STACK_END( KL, 0) |
364 | keeper_release( K); | 382 | keeper_release( K); |
@@ -379,18 +397,23 @@ LUAG_FUNC( linda_send) | |||
379 | 397 | ||
380 | 398 | ||
381 | /* | 399 | /* |
382 | * [val, key]= linda_receive( linda_ud, [timeout_secs_num=-1], key_num|str|bool|lightuserdata [, ...] ) | 400 | * 2 modes of operation |
383 | * | 401 | * [val, key]= linda_receive( linda_ud, [timeout_secs_num=-1], key_num|str|bool|lightuserdata [, ...] ) |
384 | * Receive a value from Linda, consuming it. | 402 | * Consumes a single value from the Linda, in any key. |
385 | * | 403 | * Returns: received value (which is consumed from the slot), and the key which had it |
386 | * Returns: value received (which is consumed from the slot) | 404 | |
387 | * key which had it | 405 | * [val1, ... valCOUNT]= linda_receive( linda_ud, [timeout_secs_num=-1], linda.batched, key_num|str|bool|lightuserdata, COUNT) |
388 | */ | 406 | * Consumes COUNT values from the linda, from a single key. |
407 | * returns the COUNT consumed values, or nil if there weren't enough values to consume | ||
408 | * | ||
409 | */ | ||
410 | #define BATCH_SENTINEL "270e6c9d-280f-4983-8fee-a7ecdda01475" | ||
389 | LUAG_FUNC( linda_receive) | 411 | LUAG_FUNC( linda_receive) |
390 | { | 412 | { |
391 | struct s_Linda *linda = lua_toLinda( L, 1); | 413 | struct s_Linda *linda = lua_toLinda( L, 1); |
392 | int pushed; | 414 | int pushed, expected_pushed; |
393 | bool_t cancel = FALSE; | 415 | bool_t cancel = FALSE; |
416 | char *keeper_receive; | ||
394 | 417 | ||
395 | time_d timeout = -1.0; | 418 | time_d timeout = -1.0; |
396 | uint_t key_i = 2; | 419 | uint_t key_i = 2; |
@@ -400,26 +423,44 @@ LUAG_FUNC( linda_receive) | |||
400 | if( lua_isnumber( L, 2)) | 423 | if( lua_isnumber( L, 2)) |
401 | { | 424 | { |
402 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); | 425 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); |
403 | key_i++; | 426 | ++ key_i; |
427 | } | ||
428 | else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key | ||
429 | { | ||
430 | ++ key_i; | ||
431 | } | ||
432 | |||
433 | // make sure the keys are of a valid type | ||
434 | check_key_types( L, key_i, lua_gettop( L)); | ||
435 | |||
436 | // are we in batched mode? | ||
437 | lua_pushliteral( L, BATCH_SENTINEL); | ||
438 | if( lua_equal( L, key_i, -1)) | ||
439 | { | ||
440 | keeper_receive = "receive_batched"; | ||
441 | expected_pushed = (int)luaL_checkinteger( L, key_i + 2); | ||
404 | } | 442 | } |
405 | else if( lua_isnil( L, 2)) | 443 | else |
406 | { | 444 | { |
407 | key_i++; | 445 | keeper_receive = "receive"; |
446 | expected_pushed = 2; | ||
408 | } | 447 | } |
448 | lua_pop( L, 1); | ||
409 | 449 | ||
410 | { | 450 | { |
411 | struct s_Keeper *K = keeper_acquire( linda); | 451 | struct s_Keeper *K = keeper_acquire( linda); |
412 | for( ;;) | 452 | for( ;;) |
413 | { | 453 | { |
414 | pushed = keeper_call( K->L, "receive", L, linda, key_i); | 454 | pushed = keeper_call( K->L, keeper_receive, L, linda, key_i); |
415 | if( pushed < 0) | 455 | if( pushed < 0) |
416 | { | 456 | { |
417 | break; | 457 | break; |
418 | } | 458 | } |
419 | if( pushed > 0) | 459 | if( pushed > 0) |
420 | { | 460 | { |
421 | ASSERT_L( pushed == 2); | 461 | ASSERT_L( pushed == expected_pushed); |
422 | 462 | // replace sentinels with real nils | |
463 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, 0); | ||
423 | // To be done from within the 'K' locking area | 464 | // To be done from within the 'K' locking area |
424 | // | 465 | // |
425 | SIGNAL_ALL( &linda->read_happened); | 466 | SIGNAL_ALL( &linda->read_happened); |
@@ -438,9 +479,7 @@ LUAG_FUNC( linda_receive) | |||
438 | break; | 479 | break; |
439 | } | 480 | } |
440 | 481 | ||
441 | // Bugfix by Benoit Germain Dec-2009: change status of lane to "waiting" | 482 | // change status of lane to "waiting" |
442 | // | ||
443 | #if 1 | ||
444 | { | 483 | { |
445 | struct s_lane *s; | 484 | struct s_lane *s; |
446 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | 485 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings |
@@ -459,6 +498,7 @@ LUAG_FUNC( linda_receive) | |||
459 | ASSERT_L( s->waiting_on == NULL); | 498 | ASSERT_L( s->waiting_on == NULL); |
460 | s->waiting_on = &linda->write_happened; | 499 | s->waiting_on = &linda->write_happened; |
461 | } | 500 | } |
501 | // not enough data to read: wakeup when data was sent, or when timeout is reached | ||
462 | if( !SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout)) | 502 | if( !SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout)) |
463 | { | 503 | { |
464 | if( s) | 504 | if( s) |
@@ -474,12 +514,6 @@ LUAG_FUNC( linda_receive) | |||
474 | s->status = prev_status; | 514 | s->status = prev_status; |
475 | } | 515 | } |
476 | } | 516 | } |
477 | #else | ||
478 | // Release the K lock for the duration of wait, and re-acquire | ||
479 | // | ||
480 | if( !SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout)) | ||
481 | break; | ||
482 | #endif | ||
483 | } | 517 | } |
484 | keeper_release( K); | 518 | keeper_release( K); |
485 | } | 519 | } |
@@ -501,6 +535,7 @@ LUAG_FUNC( linda_receive) | |||
501 | * = linda_set( linda_ud, key_num|str|bool|lightuserdata [,value] ) | 535 | * = linda_set( linda_ud, key_num|str|bool|lightuserdata [,value] ) |
502 | * | 536 | * |
503 | * Set a value to Linda. | 537 | * Set a value to Linda. |
538 | * TODO: what do we do if we set to non-nil and limit is 0? | ||
504 | * | 539 | * |
505 | * Existing slot value is replaced, and possible queue entries removed. | 540 | * Existing slot value is replaced, and possible queue entries removed. |
506 | */ | 541 | */ |
@@ -510,9 +545,14 @@ LUAG_FUNC( linda_set) | |||
510 | bool_t has_value = !lua_isnil( L, 3); | 545 | bool_t has_value = !lua_isnil( L, 3); |
511 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | 546 | luaL_argcheck( L, linda, 1, "expected a linda object!"); |
512 | 547 | ||
548 | // make sure the key is of a valid type | ||
549 | check_key_types( L, 2, 2); | ||
550 | |||
513 | { | 551 | { |
552 | int pushed; | ||
514 | struct s_Keeper *K = keeper_acquire( linda); | 553 | struct s_Keeper *K = keeper_acquire( linda); |
515 | int pushed = keeper_call( K->L, "set", L, linda, 2); | 554 | // no nil->sentinel toggling, we really clear the linda contents for the given key with a set() |
555 | pushed = keeper_call( K->L, "set", L, linda, 2); | ||
516 | if( pushed >= 0) // no error? | 556 | if( pushed >= 0) // no error? |
517 | { | 557 | { |
518 | ASSERT_L( pushed == 0); | 558 | ASSERT_L( pushed == 0); |
@@ -537,20 +577,55 @@ LUAG_FUNC( linda_set) | |||
537 | 577 | ||
538 | 578 | ||
539 | /* | 579 | /* |
580 | * [val] = linda_count( linda_ud, [key [, ...]]) | ||
581 | * | ||
582 | * Get a count of the pending elements in the specified keys | ||
583 | */ | ||
584 | LUAG_FUNC( linda_count) | ||
585 | { | ||
586 | struct s_Linda *linda= lua_toLinda( L, 1); | ||
587 | int pushed; | ||
588 | |||
589 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | ||
590 | // make sure the keys are of a valid type | ||
591 | check_key_types( L, 2, lua_gettop( L)); | ||
592 | |||
593 | { | ||
594 | struct s_Keeper *K = keeper_acquire( linda); | ||
595 | pushed = keeper_call( K->L, "count", L, linda, 2); | ||
596 | keeper_release( K); | ||
597 | if( pushed < 0) | ||
598 | { | ||
599 | luaL_error( L, "tried to count an invalid key"); | ||
600 | } | ||
601 | } | ||
602 | return pushed; | ||
603 | } | ||
604 | |||
605 | |||
606 | /* | ||
540 | * [val]= linda_get( linda_ud, key_num|str|bool|lightuserdata ) | 607 | * [val]= linda_get( linda_ud, key_num|str|bool|lightuserdata ) |
541 | * | 608 | * |
542 | * Get a value from Linda. | 609 | * Get a value from Linda. |
610 | * TODO: add support to get multiple values? | ||
543 | */ | 611 | */ |
544 | LUAG_FUNC( linda_get) | 612 | LUAG_FUNC( linda_get) |
545 | { | 613 | { |
546 | struct s_Linda *linda= lua_toLinda( L, 1); | 614 | struct s_Linda *linda= lua_toLinda( L, 1); |
547 | int pushed; | 615 | int pushed; |
616 | |||
548 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | 617 | luaL_argcheck( L, linda, 1, "expected a linda object!"); |
618 | // make sure the key is of a valid type | ||
619 | check_key_types( L, 2, 2); | ||
549 | 620 | ||
550 | { | 621 | { |
551 | struct s_Keeper *K = keeper_acquire( linda); | 622 | struct s_Keeper *K = keeper_acquire( linda); |
552 | pushed = keeper_call( K->L, "get", L, linda, 2); | 623 | pushed = keeper_call( K->L, "get", L, linda, 2); |
553 | ASSERT_L( pushed==0 || pushed==1 ); | 624 | ASSERT_L( pushed==0 || pushed==1 ); |
625 | if( pushed > 0) | ||
626 | { | ||
627 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, 0); | ||
628 | } | ||
554 | keeper_release(K); | 629 | keeper_release(K); |
555 | // must trigger error after keeper state has been released | 630 | // must trigger error after keeper state has been released |
556 | if( pushed < 0) | 631 | if( pushed < 0) |
@@ -571,7 +646,10 @@ LUAG_FUNC( linda_get) | |||
571 | LUAG_FUNC( linda_limit) | 646 | LUAG_FUNC( linda_limit) |
572 | { | 647 | { |
573 | struct s_Linda *linda= lua_toLinda( L, 1 ); | 648 | struct s_Linda *linda= lua_toLinda( L, 1 ); |
649 | |||
574 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | 650 | luaL_argcheck( L, linda, 1, "expected a linda object!"); |
651 | // make sure the key is of a valid type | ||
652 | check_key_types( L, 2, 2); | ||
575 | 653 | ||
576 | { | 654 | { |
577 | struct s_Keeper *K = keeper_acquire( linda); | 655 | struct s_Keeper *K = keeper_acquire( linda); |
@@ -608,6 +686,57 @@ LUAG_FUNC( linda_deep ) { | |||
608 | 686 | ||
609 | 687 | ||
610 | /* | 688 | /* |
689 | * string = linda:__tostring( linda_ud) | ||
690 | * | ||
691 | * Return the stringification of a linda | ||
692 | * | ||
693 | * Useful for concatenation or debugging purposes | ||
694 | */ | ||
695 | LUAG_FUNC( linda_tostring) | ||
696 | { | ||
697 | char text[32]; | ||
698 | struct s_Linda *linda = lua_toLinda( L, 1); | ||
699 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | ||
700 | sprintf( text, "linda: %p", linda); | ||
701 | lua_pushstring( L, text); | ||
702 | return 1; | ||
703 | } | ||
704 | |||
705 | |||
706 | /* | ||
707 | * string = linda:__concat( a, b) | ||
708 | * | ||
709 | * Return the concatenation of a pair of items, one of them being a linda | ||
710 | * | ||
711 | * Useful for concatenation or debugging purposes | ||
712 | */ | ||
713 | LUAG_FUNC( linda_concat) | ||
714 | { | ||
715 | struct s_Linda *linda1 = lua_toLinda( L, 1); | ||
716 | struct s_Linda *linda2 = lua_toLinda( L, 2); | ||
717 | // lua semantics should enforce that one of the parameters we got is a linda | ||
718 | luaL_argcheck( L, linda1 || linda2, 1, "expected a linda object!"); | ||
719 | // replace the lindas by their string equivalents in the stack | ||
720 | if ( linda1) | ||
721 | { | ||
722 | char text[32]; | ||
723 | sprintf( text, "linda: %p", linda1); | ||
724 | lua_pushstring( L, text); | ||
725 | lua_replace( L, 1); | ||
726 | } | ||
727 | if ( linda2) | ||
728 | { | ||
729 | char text[32]; | ||
730 | sprintf( text, "linda: %p", linda2); | ||
731 | lua_pushstring( L, text); | ||
732 | lua_replace( L, 2); | ||
733 | } | ||
734 | // concat the result | ||
735 | lua_concat( L, 2); | ||
736 | return 1; | ||
737 | } | ||
738 | |||
739 | /* | ||
611 | * Identity function of a shared userdata object. | 740 | * Identity function of a shared userdata object. |
612 | * | 741 | * |
613 | * lightuserdata= linda_id( "new" [, ...] ) | 742 | * lightuserdata= linda_id( "new" [, ...] ) |
@@ -658,10 +787,11 @@ static void linda_id( lua_State *L, char const * const which) | |||
658 | /* Clean associated structures in the keeper state. | 787 | /* Clean associated structures in the keeper state. |
659 | */ | 788 | */ |
660 | K= keeper_acquire(s); | 789 | K= keeper_acquire(s); |
790 | if( K) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) | ||
661 | { | 791 | { |
662 | keeper_call( K->L, "clear", L, s, 0 ); | 792 | keeper_call( K->L, "clear", L, s, 0 ); |
793 | keeper_release(K); | ||
663 | } | 794 | } |
664 | keeper_release(K); | ||
665 | 795 | ||
666 | /* There aren't any lanes waiting on these lindas, since all proxies | 796 | /* There aren't any lanes waiting on these lindas, since all proxies |
667 | * have been gc'ed. Right? | 797 | * have been gc'ed. Right? |
@@ -678,29 +808,43 @@ static void linda_id( lua_State *L, char const * const which) | |||
678 | // metatable is its own index | 808 | // metatable is its own index |
679 | lua_pushvalue( L, -1); | 809 | lua_pushvalue( L, -1); |
680 | lua_setfield( L, -2, "__index"); | 810 | lua_setfield( L, -2, "__index"); |
811 | |||
681 | // protect metatable from external access | 812 | // protect metatable from external access |
682 | lua_pushboolean( L, 0); | 813 | lua_pushboolean( L, 0); |
683 | lua_setfield( L, -2, "__metatable"); | 814 | lua_setfield( L, -2, "__metatable"); |
815 | |||
816 | lua_pushcfunction( L, LG_linda_tostring); | ||
817 | lua_setfield( L, -2, "__tostring"); | ||
818 | |||
819 | lua_pushcfunction( L, LG_linda_concat); | ||
820 | lua_setfield( L, -2, "__concat"); | ||
821 | |||
684 | // | 822 | // |
685 | // [-1]: linda metatable | 823 | // [-1]: linda metatable |
686 | lua_pushcfunction( L, LG_linda_send ); | 824 | lua_pushcfunction( L, LG_linda_send ); |
687 | lua_setfield( L, -2, "send" ); | 825 | lua_setfield( L, -2, "send" ); |
688 | 826 | ||
689 | lua_pushcfunction( L, LG_linda_receive ); | 827 | lua_pushcfunction( L, LG_linda_receive ); |
690 | lua_setfield( L, -2, "receive" ); | 828 | lua_setfield( L, -2, "receive" ); |
691 | 829 | ||
692 | lua_pushcfunction( L, LG_linda_limit ); | 830 | lua_pushcfunction( L, LG_linda_limit ); |
693 | lua_setfield( L, -2, "limit" ); | 831 | lua_setfield( L, -2, "limit" ); |
694 | 832 | ||
695 | lua_pushcfunction( L, LG_linda_set ); | 833 | lua_pushcfunction( L, LG_linda_set ); |
696 | lua_setfield( L, -2, "set" ); | 834 | lua_setfield( L, -2, "set" ); |
697 | 835 | ||
836 | lua_pushcfunction( L, LG_linda_count ); | ||
837 | lua_setfield( L, -2, "count" ); | ||
838 | |||
698 | lua_pushcfunction( L, LG_linda_get ); | 839 | lua_pushcfunction( L, LG_linda_get ); |
699 | lua_setfield( L, -2, "get" ); | 840 | lua_setfield( L, -2, "get" ); |
700 | 841 | ||
701 | lua_pushcfunction( L, LG_linda_deep ); | 842 | lua_pushcfunction( L, LG_linda_deep ); |
702 | lua_setfield( L, -2, "deep" ); | 843 | lua_setfield( L, -2, "deep" ); |
703 | 844 | ||
845 | lua_pushliteral( L, BATCH_SENTINEL); | ||
846 | lua_setfield(L, -2, "batched"); | ||
847 | |||
704 | STACK_END(L,1) | 848 | STACK_END(L,1) |
705 | } | 849 | } |
706 | else if( strcmp( which, "module") == 0) | 850 | else if( strcmp( which, "module") == 0) |
@@ -714,6 +858,11 @@ static void linda_id( lua_State *L, char const * const which) | |||
714 | } | 858 | } |
715 | } | 859 | } |
716 | 860 | ||
861 | /* | ||
862 | * ud = lanes.linda() | ||
863 | * | ||
864 | * returns a linda object | ||
865 | */ | ||
717 | LUAG_FUNC( linda) | 866 | LUAG_FUNC( linda) |
718 | { | 867 | { |
719 | return luaG_deep_userdata( L, linda_id); | 868 | return luaG_deep_userdata( L, linda_id); |
@@ -845,8 +994,9 @@ static void selfdestruct_add( struct s_lane *s ) { | |||
845 | /* | 994 | /* |
846 | * A free-running lane has ended; remove it from selfdestruct chain | 995 | * A free-running lane has ended; remove it from selfdestruct chain |
847 | */ | 996 | */ |
848 | static void selfdestruct_remove( struct s_lane *s ) { | 997 | static bool_t selfdestruct_remove( struct s_lane *s ) |
849 | 998 | { | |
999 | bool_t found = FALSE; | ||
850 | MUTEX_LOCK( &selfdestruct_cs ); | 1000 | MUTEX_LOCK( &selfdestruct_cs ); |
851 | { | 1001 | { |
852 | // Make sure (within the MUTEX) that we actually are in the chain | 1002 | // Make sure (within the MUTEX) that we actually are in the chain |
@@ -855,7 +1005,6 @@ static void selfdestruct_remove( struct s_lane *s ) { | |||
855 | // | 1005 | // |
856 | if (s->selfdestruct_next != NULL) { | 1006 | if (s->selfdestruct_next != NULL) { |
857 | struct s_lane **ref= (struct s_lane **) &selfdestruct_first; | 1007 | struct s_lane **ref= (struct s_lane **) &selfdestruct_first; |
858 | bool_t found= FALSE; | ||
859 | 1008 | ||
860 | while( *ref != SELFDESTRUCT_END ) { | 1009 | while( *ref != SELFDESTRUCT_END ) { |
861 | if (*ref == s) { | 1010 | if (*ref == s) { |
@@ -870,6 +1019,7 @@ static void selfdestruct_remove( struct s_lane *s ) { | |||
870 | } | 1019 | } |
871 | } | 1020 | } |
872 | MUTEX_UNLOCK( &selfdestruct_cs ); | 1021 | MUTEX_UNLOCK( &selfdestruct_cs ); |
1022 | return found; | ||
873 | } | 1023 | } |
874 | 1024 | ||
875 | // Initialized by 'init_once_LOCKED()': the deep userdata Linda object | 1025 | // Initialized by 'init_once_LOCKED()': the deep userdata Linda object |
@@ -880,9 +1030,10 @@ volatile DEEP_PRELUDE *timer_deep; // = NULL | |||
880 | /* | 1030 | /* |
881 | * Process end; cancel any still free-running threads | 1031 | * Process end; cancel any still free-running threads |
882 | */ | 1032 | */ |
883 | static void selfdestruct_atexit( void ) | 1033 | static int selfdestruct_atexit( lua_State *L) |
884 | { | 1034 | { |
885 | if (selfdestruct_first == SELFDESTRUCT_END) return; // no free-running threads | 1035 | (void)L; // unused |
1036 | if (selfdestruct_first == SELFDESTRUCT_END) return 0; // no free-running threads | ||
886 | 1037 | ||
887 | // Signal _all_ still running threads to exit (including the timer thread) | 1038 | // Signal _all_ still running threads to exit (including the timer thread) |
888 | // | 1039 | // |
@@ -899,7 +1050,7 @@ static void selfdestruct_atexit( void ) | |||
899 | // signal the linda the wake up the thread so that it can react to the cancel query | 1050 | // signal the linda the wake up the thread so that it can react to the cancel query |
900 | // let us hope we never land here with a pointer on a linda that has been destroyed... | 1051 | // let us hope we never land here with a pointer on a linda that has been destroyed... |
901 | SIGNAL_T *waiting_on = s->waiting_on; | 1052 | SIGNAL_T *waiting_on = s->waiting_on; |
902 | s->waiting_on = NULL; | 1053 | //s->waiting_on = NULL; // useful, or not? |
903 | SIGNAL_ALL( waiting_on); | 1054 | SIGNAL_ALL( waiting_on); |
904 | } | 1055 | } |
905 | s = s->selfdestruct_next; | 1056 | s = s->selfdestruct_next; |
@@ -969,6 +1120,7 @@ static void selfdestruct_atexit( void ) | |||
969 | // | 1120 | // |
970 | if ( selfdestruct_first != SELFDESTRUCT_END ) { | 1121 | if ( selfdestruct_first != SELFDESTRUCT_END ) { |
971 | unsigned n=0; | 1122 | unsigned n=0; |
1123 | #if 0 | ||
972 | MUTEX_LOCK( &selfdestruct_cs ); | 1124 | MUTEX_LOCK( &selfdestruct_cs ); |
973 | { | 1125 | { |
974 | struct s_lane *s= selfdestruct_first; | 1126 | struct s_lane *s= selfdestruct_first; |
@@ -983,15 +1135,14 @@ static void selfdestruct_atexit( void ) | |||
983 | // and works without the block (so let's leave those lanes running) | 1135 | // and works without the block (so let's leave those lanes running) |
984 | // | 1136 | // |
985 | //we want to free memory and such when we exit. | 1137 | //we want to free memory and such when we exit. |
986 | #if 0 | ||
987 | // 2.0.2: at least timer lane is still here | 1138 | // 2.0.2: at least timer lane is still here |
988 | // | 1139 | // |
989 | DEBUGEXEC(fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n )); | 1140 | DEBUGEXEC(fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n )); |
1141 | n=0; | ||
990 | #else | 1142 | #else |
991 | // first thing we did was to raise the linda signals the threads were waiting on (if any) | 1143 | // first thing we did was to raise the linda signals the threads were waiting on (if any) |
992 | // therefore, any well-behaved thread should be in CANCELLED state | 1144 | // therefore, any well-behaved thread should be in CANCELLED state |
993 | // these are not running, and the state can be closed | 1145 | // these are not running, and the state can be closed |
994 | n=0; | ||
995 | MUTEX_LOCK( &selfdestruct_cs ); | 1146 | MUTEX_LOCK( &selfdestruct_cs ); |
996 | { | 1147 | { |
997 | struct s_lane *s= selfdestruct_first; | 1148 | struct s_lane *s= selfdestruct_first; |
@@ -999,8 +1150,19 @@ static void selfdestruct_atexit( void ) | |||
999 | { | 1150 | { |
1000 | struct s_lane *next_s= s->selfdestruct_next; | 1151 | struct s_lane *next_s= s->selfdestruct_next; |
1001 | s->selfdestruct_next= NULL; // detach from selfdestruct chain | 1152 | s->selfdestruct_next= NULL; // detach from selfdestruct chain |
1002 | THREAD_KILL( &s->thread); | 1153 | if( !THREAD_ISNULL( s->thread)) // can be NULL if previous 'soft' termination succeeded |
1154 | { | ||
1155 | THREAD_KILL( &s->thread); | ||
1156 | #if THREADAPI == THREADAPI_PTHREAD | ||
1157 | // pthread: make sure the thread is really stopped! | ||
1158 | THREAD_WAIT( &s->thread, -1, &s->done_signal_, &s->done_lock_, &s->status); | ||
1159 | #endif // THREADAPI == THREADAPI_PTHREAD | ||
1160 | } | ||
1003 | // NO lua_close() in this case because we don't know where execution of the state was interrupted | 1161 | // NO lua_close() in this case because we don't know where execution of the state was interrupted |
1162 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
1163 | SIGNAL_FREE( &s->done_signal_); | ||
1164 | MUTEX_FREE( &s->done_lock_); | ||
1165 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
1004 | free( s); | 1166 | free( s); |
1005 | s = next_s; | 1167 | s = next_s; |
1006 | n++; | 1168 | n++; |
@@ -1013,6 +1175,7 @@ static void selfdestruct_atexit( void ) | |||
1013 | #endif | 1175 | #endif |
1014 | } | 1176 | } |
1015 | close_keepers(); | 1177 | close_keepers(); |
1178 | return 0; | ||
1016 | } | 1179 | } |
1017 | 1180 | ||
1018 | 1181 | ||
@@ -1205,15 +1368,15 @@ void SetThreadName( DWORD dwThreadID, char const *_threadName) | |||
1205 | 1368 | ||
1206 | LUAG_FUNC( set_debug_threadname) | 1369 | LUAG_FUNC( set_debug_threadname) |
1207 | { | 1370 | { |
1208 | char const *threadName; | ||
1209 | luaL_checktype( L, -1, LUA_TSTRING); | 1371 | luaL_checktype( L, -1, LUA_TSTRING); |
1210 | threadName = lua_tostring( L, -1); | ||
1211 | |||
1212 | #if defined PLATFORM_WIN32 && !defined __GNUC__ | 1372 | #if defined PLATFORM_WIN32 && !defined __GNUC__ |
1213 | // to see thead name in Visual Studio C debugger | 1373 | { |
1214 | SetThreadName(-1, threadName); | 1374 | char const *threadName = lua_tostring( L, -1); |
1215 | #endif | ||
1216 | 1375 | ||
1376 | // to see thead name in Visual Studio C debugger | ||
1377 | SetThreadName(-1, threadName); | ||
1378 | } | ||
1379 | #endif // defined PLATFORM_WIN32 && !defined __GNUC__ | ||
1217 | // to see VM name in Decoda debugger Virtual Machine window | 1380 | // to see VM name in Decoda debugger Virtual Machine window |
1218 | lua_setglobal( L, "decoda_name"); | 1381 | lua_setglobal( L, "decoda_name"); |
1219 | 1382 | ||
@@ -1221,11 +1384,7 @@ LUAG_FUNC( set_debug_threadname) | |||
1221 | } | 1384 | } |
1222 | 1385 | ||
1223 | //--- | 1386 | //--- |
1224 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | 1387 | static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) |
1225 | static THREAD_RETURN_T __stdcall lane_main( void *vs ) | ||
1226 | #else | ||
1227 | static THREAD_RETURN_T lane_main( void *vs ) | ||
1228 | #endif | ||
1229 | { | 1388 | { |
1230 | struct s_lane *s= (struct s_lane *)vs; | 1389 | struct s_lane *s= (struct s_lane *)vs; |
1231 | int rc, rc2; | 1390 | int rc, rc2; |
@@ -1315,20 +1474,22 @@ LUAG_FUNC( set_debug_threadname) | |||
1315 | lua_newtable(L); | 1474 | lua_newtable(L); |
1316 | } | 1475 | } |
1317 | s->waiting_on = NULL; // just in case | 1476 | s->waiting_on = NULL; // just in case |
1318 | if (s->selfdestruct_next != NULL) { | 1477 | if( selfdestruct_remove( s)) // check and remove (under lock!) |
1478 | { | ||
1319 | // We're a free-running thread and no-one's there to clean us up. | 1479 | // We're a free-running thread and no-one's there to clean us up. |
1320 | // | 1480 | // |
1321 | lua_close( s->L ); | 1481 | lua_close( s->L ); |
1322 | s->L = L = 0; | 1482 | s->L = L = 0; |
1323 | 1483 | ||
1324 | #if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) ) | 1484 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1325 | SIGNAL_FREE( &s->done_signal_ ); | 1485 | SIGNAL_FREE( &s->done_signal_); |
1326 | MUTEX_FREE( &s->done_lock_ ); | 1486 | MUTEX_FREE( &s->done_lock_); |
1327 | #endif | 1487 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1328 | selfdestruct_remove(s); // away from selfdestruct chain | ||
1329 | free(s); | 1488 | free(s); |
1330 | 1489 | ||
1331 | } else { | 1490 | } |
1491 | else | ||
1492 | { | ||
1332 | // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them | 1493 | // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them |
1333 | 1494 | ||
1334 | enum e_status st= | 1495 | enum e_status st= |
@@ -1339,16 +1500,16 @@ LUAG_FUNC( set_debug_threadname) | |||
1339 | // Posix no PTHREAD_TIMEDJOIN: | 1500 | // Posix no PTHREAD_TIMEDJOIN: |
1340 | // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change | 1501 | // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change |
1341 | // | 1502 | // |
1342 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) | 1503 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1343 | s->status= st; | 1504 | MUTEX_LOCK( &s->done_lock_); |
1344 | #else | ||
1345 | MUTEX_LOCK( &s->done_lock_ ); | ||
1346 | { | 1505 | { |
1347 | s->status= st; | 1506 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1348 | SIGNAL_ONE( &s->done_signal_ ); // wake up master (while 's->done_lock' is on) | 1507 | s->status = st; |
1508 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
1509 | SIGNAL_ONE( &s->done_signal_); // wake up master (while 's->done_lock' is on) | ||
1349 | } | 1510 | } |
1350 | MUTEX_UNLOCK( &s->done_lock_ ); | 1511 | MUTEX_UNLOCK( &s->done_lock_); |
1351 | #endif | 1512 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1352 | } | 1513 | } |
1353 | return 0; // ignored | 1514 | return 0; // ignored |
1354 | } | 1515 | } |
@@ -1359,22 +1520,52 @@ LUAG_FUNC( set_debug_threadname) | |||
1359 | // [cancelstep_uint=0], | 1520 | // [cancelstep_uint=0], |
1360 | // [prio_int=0], | 1521 | // [prio_int=0], |
1361 | // [globals_tbl], | 1522 | // [globals_tbl], |
1523 | // [package_tbl], | ||
1524 | // [required], | ||
1362 | // [... args ...] ) | 1525 | // [... args ...] ) |
1363 | // | 1526 | // |
1364 | // Upvalues: metatable to use for 'lane_ud' | 1527 | // Upvalues: metatable to use for 'lane_ud' |
1365 | // | 1528 | // |
1529 | |||
1530 | // helper function to require a module in the keeper states and in the target state | ||
1531 | // source state contains module name at the top of the stack | ||
1532 | static void require_one_module( lua_State *L, lua_State *L2, bool_t _fatal) | ||
1533 | { | ||
1534 | size_t len; | ||
1535 | char const *name = lua_tolstring( L, -1, &len); | ||
1536 | // require the module in the target lane | ||
1537 | STACK_GROW( L2, 2); | ||
1538 | lua_getglobal( L2, "require"); | ||
1539 | if( lua_isnil( L2, -1)) | ||
1540 | { | ||
1541 | lua_pop( L2, 1); | ||
1542 | if( _fatal) | ||
1543 | luaL_error( L, "cannot pre-require modules without loading 'package' library first"); | ||
1544 | } | ||
1545 | else | ||
1546 | { | ||
1547 | lua_pushlstring( L2, name, len); | ||
1548 | lua_pcall( L2, 1, 0, 0); | ||
1549 | // we need to require this module in the keeper states as well | ||
1550 | populate_keepers( L); | ||
1551 | } | ||
1552 | } | ||
1553 | |||
1366 | LUAG_FUNC( thread_new ) | 1554 | LUAG_FUNC( thread_new ) |
1367 | { | 1555 | { |
1368 | lua_State *L2; | 1556 | lua_State *L2; |
1369 | struct s_lane *s; | 1557 | struct s_lane *s; |
1370 | struct s_lane **ud; | 1558 | struct s_lane **ud; |
1371 | 1559 | ||
1372 | const char *libs= lua_tostring( L, 2 ); | 1560 | char const* libs = lua_tostring( L, 2); |
1373 | uint_t cs= luaG_optunsigned( L, 3,0); | 1561 | lua_CFunction on_state_create = lua_iscfunction( L, 3) ? lua_tocfunction( L, 3) : NULL; |
1374 | int prio= (int)luaL_optinteger( L, 4,0); | 1562 | uint_t cs = luaG_optunsigned( L, 4, 0); |
1375 | uint_t glob= luaG_isany(L,5) ? 5:0; | 1563 | int prio = (int) luaL_optinteger( L, 5, 0); |
1564 | uint_t glob = luaG_isany( L, 6) ? 6 : 0; | ||
1565 | uint_t package = luaG_isany( L,7) ? 7 : 0; | ||
1566 | uint_t required = luaG_isany( L, 8) ? 8 : 0; | ||
1376 | 1567 | ||
1377 | #define FIXED_ARGS (5) | 1568 | #define FIXED_ARGS 8 |
1378 | uint_t args= lua_gettop(L) - FIXED_ARGS; | 1569 | uint_t args= lua_gettop(L) - FIXED_ARGS; |
1379 | 1570 | ||
1380 | if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) | 1571 | if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) |
@@ -1385,45 +1576,109 @@ LUAG_FUNC( thread_new ) | |||
1385 | 1576 | ||
1386 | /* --- Create and prepare the sub state --- */ | 1577 | /* --- Create and prepare the sub state --- */ |
1387 | 1578 | ||
1388 | L2 = luaL_newstate(); // uses standard 'realloc()'-based allocator, | 1579 | // populate with selected libraries at the same time |
1389 | // sets the panic callback | 1580 | // |
1390 | 1581 | L2 = luaG_newstate( libs, on_state_create); | |
1391 | if (!L2) luaL_error( L, "'luaL_newstate()' failed; out of memory" ); | 1582 | if (!L2) luaL_error( L, "'luaL_newstate()' failed; out of memory" ); |
1392 | 1583 | ||
1393 | STACK_GROW( L,2 ); | 1584 | STACK_GROW( L, 2); |
1394 | 1585 | ||
1395 | // Setting the globals table (needs to be done before loading stdlibs, | 1586 | ASSERT_L( lua_gettop(L2) == 0); |
1396 | // and the lane function) | ||
1397 | // | ||
1398 | if (glob!=0) | ||
1399 | { | ||
1400 | STACK_CHECK(L) | ||
1401 | if (!lua_istable(L,glob)) | ||
1402 | luaL_error( L, "Expected table, got %s", luaG_typename(L,glob) ); | ||
1403 | 1587 | ||
1404 | lua_pushvalue( L, glob ); | 1588 | // package.path |
1589 | STACK_CHECK(L) | ||
1590 | STACK_CHECK(L2) | ||
1591 | if( package) | ||
1592 | { | ||
1593 | if (lua_type(L,package) != LUA_TTABLE) | ||
1594 | luaL_error( L, "expected package as table, got %s", luaL_typename(L,package)); | ||
1595 | lua_getglobal( L2, "package"); | ||
1596 | if( !lua_isnil( L2, -1)) // package library not loaded: do nothing | ||
1597 | { | ||
1598 | int i; | ||
1599 | char const *entries[] = { "path", "cpath", "preload", "loaders", NULL}; | ||
1600 | for( i = 0; entries[i]; ++ i) | ||
1601 | { | ||
1602 | lua_getfield( L, package, entries[i]); | ||
1603 | if( lua_isnil( L, -1)) | ||
1604 | { | ||
1605 | lua_pop( L, 1); | ||
1606 | } | ||
1607 | else | ||
1608 | { | ||
1609 | luaG_inter_move( L, L2, 1); // moves the entry to L2 | ||
1610 | lua_setfield( L2, -2, entries[i]); // set package[entries[i]] | ||
1611 | } | ||
1612 | } | ||
1613 | } | ||
1614 | lua_pop( L2, 1); | ||
1615 | } | ||
1616 | STACK_END(L2,0) | ||
1617 | STACK_END(L,0) | ||
1405 | 1618 | ||
1406 | luaG_inter_move( L, L2, 1); // moves the table to L2 | 1619 | // modules to require in the target lane *before* the function is transfered! |
1407 | 1620 | ||
1408 | // L2 [-1]: table of globals | 1621 | //start by requiring lua51-lanes, since it is a bit special |
1622 | // it is not fatal if 'require' isn't loaded, just ignore (may cause function transfer errors later on if the lane pulls the lanes module itself) | ||
1623 | STACK_CHECK(L) | ||
1624 | STACK_CHECK(L2) | ||
1625 | lua_pushliteral( L, "lua51-lanes"); | ||
1626 | require_one_module( L, L2, FALSE); | ||
1627 | lua_pop( L, 1); | ||
1628 | STACK_END(L2,0) | ||
1629 | STACK_END(L,0) | ||
1409 | 1630 | ||
1410 | // "You can change the global environment of a Lua thread using lua_replace" | 1631 | STACK_CHECK(L) |
1411 | // (refman-5.0.pdf p. 30) | 1632 | STACK_CHECK(L2) |
1412 | // | 1633 | if( required) |
1413 | lua_replace( L2, LUA_GLOBALSINDEX ); | 1634 | { |
1414 | STACK_END(L,0) | 1635 | int nbRequired = 1; |
1636 | // should not happen, was checked in lanes.lua before calling thread_new() | ||
1637 | if (lua_type(L, required) != LUA_TTABLE) | ||
1638 | luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required)); | ||
1639 | lua_pushnil( L); | ||
1640 | while( lua_next( L, required) != 0) | ||
1641 | { | ||
1642 | if (lua_type(L,-1) != LUA_TSTRING || lua_type(L,-2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired) | ||
1643 | { | ||
1644 | luaL_error( L, "required module list should be a list of strings."); | ||
1645 | } | ||
1646 | else | ||
1647 | { | ||
1648 | require_one_module( L, L2, TRUE); | ||
1649 | } | ||
1650 | lua_pop( L, 1); | ||
1651 | ++ nbRequired; | ||
1652 | } | ||
1415 | } | 1653 | } |
1654 | STACK_END(L2,0) | ||
1655 | STACK_END(L,0) | ||
1416 | 1656 | ||
1417 | // Selected libraries | 1657 | // Appending the specified globals to the global environment |
1658 | // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed... | ||
1418 | // | 1659 | // |
1419 | if (libs) | 1660 | if (glob!=0) |
1420 | { | 1661 | { |
1421 | const char *err= luaG_openlibs( L2, libs ); | 1662 | STACK_CHECK(L) |
1422 | ASSERT_L( !err ); // bad libs should have been noticed by 'lanes.lua' | 1663 | STACK_CHECK(L2) |
1664 | if (!lua_istable(L,glob)) | ||
1665 | luaL_error( L, "Expected table, got %s", luaL_typename(L,glob)); | ||
1423 | 1666 | ||
1424 | serialize_require( L2 ); | 1667 | lua_pushnil( L); |
1668 | while( lua_next( L, glob)) | ||
1669 | { | ||
1670 | luaG_inter_copy( L, L2, 2); // moves the key/value pair to the L2 stack | ||
1671 | // assign it in the globals table | ||
1672 | lua_rawset( L2, LUA_GLOBALSINDEX); | ||
1673 | lua_pop( L, 1); | ||
1674 | } | ||
1675 | |||
1676 | STACK_END(L2, 0) | ||
1677 | STACK_END(L, 0) | ||
1425 | } | 1678 | } |
1426 | 1679 | ||
1680 | ASSERT_L( lua_gettop(L2) == 0); | ||
1681 | |||
1427 | // Lane main function | 1682 | // Lane main function |
1428 | // | 1683 | // |
1429 | STACK_CHECK(L) | 1684 | STACK_CHECK(L) |
@@ -1470,10 +1725,10 @@ LUAG_FUNC( thread_new ) | |||
1470 | s->waiting_on = NULL; | 1725 | s->waiting_on = NULL; |
1471 | s->cancel_request= FALSE; | 1726 | s->cancel_request= FALSE; |
1472 | 1727 | ||
1473 | #if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) ) | 1728 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1474 | MUTEX_INIT( &s->done_lock_ ); | 1729 | MUTEX_INIT( &s->done_lock_); |
1475 | SIGNAL_INIT( &s->done_signal_ ); | 1730 | SIGNAL_INIT( &s->done_signal_); |
1476 | #endif | 1731 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1477 | s->mstatus= NORMAL; | 1732 | s->mstatus= NORMAL; |
1478 | s->selfdestruct_next= NULL; | 1733 | s->selfdestruct_next= NULL; |
1479 | 1734 | ||
@@ -1488,7 +1743,7 @@ LUAG_FUNC( thread_new ) | |||
1488 | lua_newtable( L); | 1743 | lua_newtable( L); |
1489 | lua_setfenv( L, -2); | 1744 | lua_setfenv( L, -2); |
1490 | 1745 | ||
1491 | // Place 's' to registry, for 'cancel_test()' (even if 'cs'==0 we still | 1746 | // Place 's' in registry, for 'cancel_test()' (even if 'cs'==0 we still |
1492 | // do cancel tests at pending send/receive). | 1747 | // do cancel tests at pending send/receive). |
1493 | // | 1748 | // |
1494 | lua_pushlightuserdata( L2, CANCEL_TEST_KEY ); | 1749 | lua_pushlightuserdata( L2, CANCEL_TEST_KEY ); |
@@ -1545,6 +1800,7 @@ LUAG_FUNC( thread_gc ) | |||
1545 | { | 1800 | { |
1546 | // Make sure a kill has proceeded, before cleaning up the data structure. | 1801 | // Make sure a kill has proceeded, before cleaning up the data structure. |
1547 | // | 1802 | // |
1803 | // NO lua_close() in this case because we don't know where execution of the state was interrupted | ||
1548 | // If not doing 'THREAD_WAIT()' we should close the Lua state here | 1804 | // If not doing 'THREAD_WAIT()' we should close the Lua state here |
1549 | // (can it be out of order, since we killed the lane abruptly?) | 1805 | // (can it be out of order, since we killed the lane abruptly?) |
1550 | // | 1806 | // |
@@ -1553,11 +1809,7 @@ LUAG_FUNC( thread_gc ) | |||
1553 | s->L = 0; | 1809 | s->L = 0; |
1554 | #else // 0 | 1810 | #else // 0 |
1555 | DEBUGEXEC(fprintf( stderr, "** Joining with a killed thread (needs testing) **" )); | 1811 | DEBUGEXEC(fprintf( stderr, "** Joining with a killed thread (needs testing) **" )); |
1556 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) | 1812 | THREAD_WAIT( &s->thread, -1, &s->done_signal_, &s->done_lock_, &s->status); |
1557 | THREAD_WAIT( &s->thread, -1 ); | ||
1558 | #else | ||
1559 | THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, -1 ); | ||
1560 | #endif | ||
1561 | DEBUGEXEC(fprintf( stderr, "** Joined ok **" )); | 1813 | DEBUGEXEC(fprintf( stderr, "** Joined ok **" )); |
1562 | #endif // 0 | 1814 | #endif // 0 |
1563 | } | 1815 | } |
@@ -1569,12 +1821,12 @@ LUAG_FUNC( thread_gc ) | |||
1569 | 1821 | ||
1570 | // Clean up after a (finished) thread | 1822 | // Clean up after a (finished) thread |
1571 | // | 1823 | // |
1572 | #if (! ((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN))) | 1824 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1573 | SIGNAL_FREE( &s->done_signal_ ); | 1825 | SIGNAL_FREE( &s->done_signal_); |
1574 | MUTEX_FREE( &s->done_lock_ ); | 1826 | MUTEX_FREE( &s->done_lock_); |
1575 | #endif | 1827 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1576 | 1828 | ||
1577 | free(s); | 1829 | free( s); |
1578 | 1830 | ||
1579 | return 0; | 1831 | return 0; |
1580 | } | 1832 | } |
@@ -1603,13 +1855,19 @@ static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force) | |||
1603 | // | 1855 | // |
1604 | if( s->status < DONE) | 1856 | if( s->status < DONE) |
1605 | { | 1857 | { |
1606 | s->cancel_request = TRUE; // it's now signalled to stop | 1858 | s->cancel_request = TRUE; // it's now signaled to stop |
1607 | done= | 1859 | // signal the linda the wake up the thread so that it can react to the cancel query |
1608 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) | 1860 | // let us hope we never land here with a pointer on a linda that has been destroyed... |
1609 | THREAD_WAIT( &s->thread, secs); | 1861 | //MUTEX_LOCK( &selfdestruct_cs ); |
1610 | #else | 1862 | { |
1611 | THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, secs); | 1863 | SIGNAL_T *waiting_on = s->waiting_on; |
1612 | #endif | 1864 | if( s->status == WAITING && waiting_on != NULL) |
1865 | { | ||
1866 | SIGNAL_ALL( waiting_on); | ||
1867 | } | ||
1868 | } | ||
1869 | //MUTEX_UNLOCK( &selfdestruct_cs ); | ||
1870 | done = THREAD_WAIT( &s->thread, secs, &s->done_signal_, &s->done_lock_, &s->status); | ||
1613 | 1871 | ||
1614 | if ((!done) && force) | 1872 | if ((!done) && force) |
1615 | { | 1873 | { |
@@ -1627,23 +1885,32 @@ static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force) | |||
1627 | 1885 | ||
1628 | LUAG_FUNC( thread_cancel) | 1886 | LUAG_FUNC( thread_cancel) |
1629 | { | 1887 | { |
1630 | struct s_lane *s= lua_toLane(L,1); | 1888 | if( lua_gettop( L) != 1 || lua_type( L, 1) != LUA_TUSERDATA) |
1631 | double secs= 0.0; | 1889 | { |
1632 | uint_t force_i=2; | 1890 | return luaL_error( L, "invalid argument #1, did you use ':' as you should?"); |
1633 | bool_t force, done= TRUE; | 1891 | } |
1892 | else | ||
1893 | { | ||
1894 | struct s_lane *s = lua_toLane( L, 1); | ||
1895 | double secs = 0.0; | ||
1896 | uint_t force_i = 2; | ||
1897 | bool_t force, done= TRUE; | ||
1634 | 1898 | ||
1635 | if (lua_isnumber(L,2)) { | 1899 | if( lua_isnumber( L, 2)) |
1636 | secs= lua_tonumber(L,2); | 1900 | { |
1637 | force_i++; | 1901 | secs = lua_tonumber( L, 2); |
1638 | } else if (lua_isnil(L,2)) | 1902 | ++ force_i; |
1639 | force_i++; | 1903 | } |
1904 | else if( lua_isnil( L, 2)) | ||
1905 | ++ force_i; | ||
1640 | 1906 | ||
1641 | force= lua_toboolean(L,force_i); // FALSE if nothing there | 1907 | force = lua_toboolean( L, force_i); // FALSE if nothing there |
1642 | 1908 | ||
1643 | done = thread_cancel( s, secs, force); | 1909 | done = thread_cancel( s, secs, force); |
1644 | 1910 | ||
1645 | lua_pushboolean( L, done); | 1911 | lua_pushboolean( L, done); |
1646 | return 1; | 1912 | return 1; |
1913 | } | ||
1647 | } | 1914 | } |
1648 | 1915 | ||
1649 | //--- | 1916 | //--- |
@@ -1698,12 +1965,7 @@ LUAG_FUNC( thread_join ) | |||
1698 | int ret; | 1965 | int ret; |
1699 | bool_t done; | 1966 | bool_t done; |
1700 | 1967 | ||
1701 | done = (s->thread == 0) || | 1968 | done = THREAD_ISNULL( s->thread) || THREAD_WAIT( &s->thread, wait_secs, &s->done_signal_, &s->done_lock_, &s->status); |
1702 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) | ||
1703 | THREAD_WAIT( &s->thread, wait_secs ); | ||
1704 | #else | ||
1705 | THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, wait_secs); | ||
1706 | #endif | ||
1707 | if (!done || !L2) | 1969 | if (!done || !L2) |
1708 | return 0; // timeout: pushes none, leaves 'L2' alive | 1970 | return 0; // timeout: pushes none, leaves 'L2' alive |
1709 | 1971 | ||
@@ -1915,7 +2177,8 @@ LUAG_FUNC( now_secs ) | |||
1915 | LUAG_FUNC( wakeup_conv ) | 2177 | LUAG_FUNC( wakeup_conv ) |
1916 | { | 2178 | { |
1917 | int year, month, day, hour, min, sec, isdst; | 2179 | int year, month, day, hour, min, sec, isdst; |
1918 | struct tm tm= {0}; | 2180 | struct tm t; |
2181 | memset( &t, 0, sizeof( t)); | ||
1919 | // | 2182 | // |
1920 | // .year (four digits) | 2183 | // .year (four digits) |
1921 | // .month (1..12) | 2184 | // .month (1..12) |
@@ -1942,15 +2205,15 @@ LUAG_FUNC( wakeup_conv ) | |||
1942 | lua_pop(L,1); | 2205 | lua_pop(L,1); |
1943 | STACK_END(L,0) | 2206 | STACK_END(L,0) |
1944 | 2207 | ||
1945 | tm.tm_year= year-1900; | 2208 | t.tm_year= year-1900; |
1946 | tm.tm_mon= month-1; // 0..11 | 2209 | t.tm_mon= month-1; // 0..11 |
1947 | tm.tm_mday= day; // 1..31 | 2210 | t.tm_mday= day; // 1..31 |
1948 | tm.tm_hour= hour; // 0..23 | 2211 | t.tm_hour= hour; // 0..23 |
1949 | tm.tm_min= min; // 0..59 | 2212 | t.tm_min= min; // 0..59 |
1950 | tm.tm_sec= sec; // 0..60 | 2213 | t.tm_sec= sec; // 0..60 |
1951 | tm.tm_isdst= isdst; // 0/1/negative | 2214 | t.tm_isdst= isdst; // 0/1/negative |
1952 | 2215 | ||
1953 | lua_pushnumber( L, (double) mktime( &tm ) ); // ms=0 | 2216 | lua_pushnumber( L, (double) mktime( &t)); // ms=0 |
1954 | return 1; | 2217 | return 1; |
1955 | } | 2218 | } |
1956 | 2219 | ||
@@ -1968,7 +2231,7 @@ static const struct luaL_reg lanes_functions [] = { | |||
1968 | /* | 2231 | /* |
1969 | * One-time initializations | 2232 | * One-time initializations |
1970 | */ | 2233 | */ |
1971 | static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ref, int const nbKeepers) | 2234 | static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_ref, int const nbKeepers, lua_CFunction _on_state_create) |
1972 | { | 2235 | { |
1973 | const char *err; | 2236 | const char *err; |
1974 | 2237 | ||
@@ -1994,7 +2257,7 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ | |||
1994 | // Selfdestruct chain handling | 2257 | // Selfdestruct chain handling |
1995 | // | 2258 | // |
1996 | MUTEX_INIT( &selfdestruct_cs ); | 2259 | MUTEX_INIT( &selfdestruct_cs ); |
1997 | atexit( selfdestruct_atexit ); | 2260 | //atexit( selfdestruct_atexit ); |
1998 | 2261 | ||
1999 | //--- | 2262 | //--- |
2000 | // Linux needs SCHED_RR to change thread priorities, and that is only | 2263 | // Linux needs SCHED_RR to change thread priorities, and that is only |
@@ -2019,7 +2282,7 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ | |||
2019 | } | 2282 | } |
2020 | #endif | 2283 | #endif |
2021 | #endif | 2284 | #endif |
2022 | err= init_keepers( nbKeepers); | 2285 | err = init_keepers( nbKeepers, _on_state_create); |
2023 | if (err) | 2286 | if (err) |
2024 | { | 2287 | { |
2025 | luaL_error( L, "Unable to initialize: %s", err ); | 2288 | luaL_error( L, "Unable to initialize: %s", err ); |
@@ -2044,7 +2307,17 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ | |||
2044 | 2307 | ||
2045 | // The host Lua state must always have a reference to this Linda object in order for our 'timer_deep_ref' to be valid. | 2308 | // The host Lua state must always have a reference to this Linda object in order for our 'timer_deep_ref' to be valid. |
2046 | // So store a reference that we will never actually use. | 2309 | // So store a reference that we will never actually use. |
2047 | lua_pushlightuserdata(L, (void *)init_once_LOCKED); | 2310 | // at the same time, use this object as a 'desinit' marker: |
2311 | // when the main lua State is closed, this object will be GC'ed | ||
2312 | { | ||
2313 | lua_newuserdata( L, 1); | ||
2314 | lua_newtable( L); | ||
2315 | lua_pushcfunction( L, selfdestruct_atexit); | ||
2316 | lua_setfield( L, -2, "__gc"); | ||
2317 | lua_pushliteral( L, "AtExit"); | ||
2318 | lua_setfield( L, -2, "__metatable"); | ||
2319 | lua_setmetatable( L, -2); | ||
2320 | } | ||
2048 | lua_insert(L, -2); // Swap key with the Linda object | 2321 | lua_insert(L, -2); // Swap key with the Linda object |
2049 | lua_rawset(L, LUA_REGISTRYINDEX); | 2322 | lua_rawset(L, LUA_REGISTRYINDEX); |
2050 | 2323 | ||
@@ -2052,15 +2325,15 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ | |||
2052 | STACK_END(L,0) | 2325 | STACK_END(L,0) |
2053 | } | 2326 | } |
2054 | 2327 | ||
2055 | int | 2328 | static volatile long s_initCount = 0; |
2056 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | 2329 | |
2057 | __declspec(dllexport) | 2330 | LUAG_FUNC( configure ) |
2058 | #endif | ||
2059 | luaopen_lanes( lua_State *L ) | ||
2060 | { | 2331 | { |
2061 | static volatile int /*bool*/ go_ahead; // = 0 | 2332 | char const* name = luaL_checkstring( L, lua_upvalueindex( 1)); |
2062 | int const nbKeepers = luaL_optint( L, 2, 1); | 2333 | int const nbKeepers = luaL_optint( L, 1, 1); |
2063 | luaL_argcheck( L, nbKeepers > 0, 2, "Number of keeper states must be > 0"); | 2334 | lua_CFunction on_state_create = lua_iscfunction( L, 2) ? lua_tocfunction( L, 2) : NULL; |
2335 | luaL_argcheck( L, nbKeepers > 0, 1, "Number of keeper states must be > 0"); | ||
2336 | luaL_argcheck( L, lua_iscfunction( L, 2) || lua_isnil( L, 2), 2, "on_state_create should be a C function"); | ||
2064 | /* | 2337 | /* |
2065 | * Making one-time initializations. | 2338 | * Making one-time initializations. |
2066 | * | 2339 | * |
@@ -2068,42 +2341,43 @@ luaopen_lanes( lua_State *L ) | |||
2068 | * there is no problem. But if the host is multithreaded, we need to lock around the | 2341 | * there is no problem. But if the host is multithreaded, we need to lock around the |
2069 | * initializations. | 2342 | * initializations. |
2070 | */ | 2343 | */ |
2071 | #ifdef PLATFORM_WIN32 | 2344 | #if THREADAPI == THREADAPI_WINDOWS |
2072 | { | 2345 | { |
2073 | // TBD: Someone please replace this with reliable Win32 API code. Problem is, | 2346 | static volatile int /*bool*/ go_ahead; // = 0 |
2074 | // there's no autoinitializing locks (s.a. PTHREAD_MUTEX_INITIALIZER) in | 2347 | if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0) |
2075 | // Windows so 'InterlockedIncrement' or something needs to be used. | 2348 | { |
2076 | // This is 99.9999% safe, though (and always safe if host is single-threaded) | 2349 | init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create); |
2077 | // -- AKa 24-Jun-2009 | ||
2078 | // | ||
2079 | static volatile unsigned my_number; // = 0 | ||
2080 | |||
2081 | if (my_number++ == 0) { // almost atomic | ||
2082 | init_once_LOCKED(L, &timer_deep, nbKeepers); | ||
2083 | go_ahead= 1; // let others pass | 2350 | go_ahead= 1; // let others pass |
2084 | } else { | 2351 | } |
2352 | else | ||
2353 | { | ||
2085 | while( !go_ahead ) { Sleep(1); } // changes threads | 2354 | while( !go_ahead ) { Sleep(1); } // changes threads |
2086 | } | 2355 | } |
2087 | } | 2356 | } |
2088 | #else | 2357 | #else // THREADAPI == THREADAPI_PTHREAD |
2089 | if (!go_ahead) { | 2358 | if( s_initCount == 0) |
2359 | { | ||
2090 | static pthread_mutex_t my_lock= PTHREAD_MUTEX_INITIALIZER; | 2360 | static pthread_mutex_t my_lock= PTHREAD_MUTEX_INITIALIZER; |
2091 | pthread_mutex_lock(&my_lock); | 2361 | pthread_mutex_lock( &my_lock); |
2092 | { | 2362 | { |
2093 | // Recheck now that we're within the lock | 2363 | // Recheck now that we're within the lock |
2094 | // | 2364 | // |
2095 | if (!go_ahead) { | 2365 | if( s_initCount == 0) |
2096 | init_once_LOCKED(L, &timer_deep, nbKeepers); | 2366 | { |
2097 | go_ahead= 1; | 2367 | init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create); |
2368 | s_initCount = 1; | ||
2098 | } | 2369 | } |
2099 | } | 2370 | } |
2100 | pthread_mutex_unlock(&my_lock); | 2371 | pthread_mutex_unlock(&my_lock); |
2101 | } | 2372 | } |
2102 | #endif | 2373 | #endif // THREADAPI == THREADAPI_PTHREAD |
2103 | assert( timer_deep != 0 ); | 2374 | assert( timer_deep != 0 ); |
2104 | 2375 | ||
2105 | // Create main module interface table | 2376 | // Create main module interface table |
2106 | lua_newtable(L); | 2377 | lua_pushvalue( L, lua_upvalueindex( 2)); |
2378 | // remove configure() (this function) from the module interface | ||
2379 | lua_pushnil( L); | ||
2380 | lua_setfield( L, -2, "configure"); | ||
2107 | luaL_register(L, NULL, lanes_functions); | 2381 | luaL_register(L, NULL, lanes_functions); |
2108 | 2382 | ||
2109 | // metatable for threads | 2383 | // metatable for threads |
@@ -2124,7 +2398,7 @@ luaopen_lanes( lua_State *L ) | |||
2124 | lua_setfield( L, -2, "join"); | 2398 | lua_setfield( L, -2, "join"); |
2125 | lua_pushcfunction( L, LG_thread_cancel); | 2399 | lua_pushcfunction( L, LG_thread_cancel); |
2126 | lua_setfield( L, -2, "cancel"); | 2400 | lua_setfield( L, -2, "cancel"); |
2127 | lua_pushboolean( L, 0); | 2401 | lua_pushliteral( L, "Lane"); |
2128 | lua_setfield( L, -2, "__metatable"); | 2402 | lua_setfield( L, -2, "__metatable"); |
2129 | 2403 | ||
2130 | lua_pushcclosure( L, LG_thread_new, 1 ); // metatable as closure param | 2404 | lua_pushcclosure( L, LG_thread_new, 1 ); // metatable as closure param |
@@ -2142,8 +2416,42 @@ luaopen_lanes( lua_State *L ) | |||
2142 | lua_pushlightuserdata( L, CANCEL_ERROR ); | 2416 | lua_pushlightuserdata( L, CANCEL_ERROR ); |
2143 | lua_setfield(L, -2, "cancel_error"); | 2417 | lua_setfield(L, -2, "cancel_error"); |
2144 | 2418 | ||
2145 | // Return the local module table | 2419 | // register all native functions found in that module in the transferable functions database |
2146 | return 1; | 2420 | // we process it before _G because we don't want to find the module when scanning _G (this would generate longer names) |
2421 | populate_func_lookup_table( L, -1, name); | ||
2422 | // record all existing C/JIT-fast functions | ||
2423 | populate_func_lookup_table( L, LUA_GLOBALSINDEX, NULL); | ||
2424 | // Return nothing | ||
2425 | lua_pop( L, 1); | ||
2426 | return 0; | ||
2427 | } | ||
2428 | |||
2429 | int | ||
2430 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | ||
2431 | __declspec(dllexport) | ||
2432 | #endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | ||
2433 | luaopen_lanes( lua_State *L ) | ||
2434 | { | ||
2435 | // Create main module interface table | ||
2436 | // we only have 1 closure, which must be called to configure Lanes | ||
2437 | STACK_GROW( L, 3); | ||
2438 | STACK_CHECK( L) | ||
2439 | lua_newtable(L); | ||
2440 | lua_pushvalue(L, 1); // module name | ||
2441 | lua_pushvalue(L, -2); // module table | ||
2442 | lua_pushcclosure( L, LG_configure, 2); | ||
2443 | if( s_initCount == 0) | ||
2444 | { | ||
2445 | lua_setfield( L, -2, "configure"); | ||
2446 | } | ||
2447 | else // already initialized: call it immediately and be done | ||
2448 | { | ||
2449 | lua_pushinteger( L, 666); // any value will do, it will be ignored | ||
2450 | lua_pushnil( L); // almost idem | ||
2451 | lua_call( L, 2, 0); | ||
2452 | } | ||
2453 | STACK_END( L, 1) | ||
2454 | return 1; | ||
2147 | } | 2455 | } |
2148 | 2456 | ||
2149 | 2457 | ||