diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/keeper.lua | 4 | ||||
| -rw-r--r-- | src/lanes.c | 732 | ||||
| -rw-r--r-- | src/lanes.lua | 33 | ||||
| -rw-r--r-- | src/threading.c | 9 | ||||
| -rw-r--r-- | src/tools.c | 260 | ||||
| -rw-r--r-- | src/tools.h | 2 |
6 files changed, 602 insertions, 438 deletions
diff --git a/src/keeper.lua b/src/keeper.lua index f76173b..9256a4b 100644 --- a/src/keeper.lua +++ b/src/keeper.lua | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | --[[ | 11 | --[[ |
| 12 | =============================================================================== | 12 | =============================================================================== |
| 13 | 13 | ||
| 14 | Copyright (C) 2008 Asko Kauppi <akauppi@gmail.com> | 14 | Copyright (C) 2008-10 Asko Kauppi <akauppi@gmail.com> |
| 15 | 15 | ||
| 16 | Permission is hereby granted, free of charge, to any person obtaining a copy | 16 | Permission is hereby granted, free of charge, to any person obtaining a copy |
| 17 | of this software and associated documentation files (the "Software"), to deal | 17 | of this software and associated documentation files (the "Software"), to deal |
| @@ -135,7 +135,7 @@ function send( ud, key, ... ) | |||
| 135 | local m= limits[key] | 135 | local m= limits[key] |
| 136 | 136 | ||
| 137 | if m and len+n > m then | 137 | if m and len+n > m then |
| 138 | return false -- would exceed the limit; try again later | 138 | return false -- would exceed the limit; try again later |
| 139 | end | 139 | end |
| 140 | 140 | ||
| 141 | for i=1,n do | 141 | for i=1,n do |
diff --git a/src/lanes.c b/src/lanes.c index 9b36e4d..ba9e59a 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
| @@ -4,6 +4,13 @@ | |||
| 4 | * Multithreading in Lua. | 4 | * Multithreading in Lua. |
| 5 | * | 5 | * |
| 6 | * History: | 6 | * History: |
| 7 | * 3-Jan-11 (2.0.10): linda_send bugfix, was waiting on the wrong signal | ||
| 8 | * 3-Dec-10 (2.0.9): Added support to generate a lane from a string | ||
| 9 | * 2-Dec-10 (2.0.8): Fix LuaJIT2 incompatibility (no 'tostring' hijack anymore) | ||
| 10 | * ???????? (2.0.7): Fixed 'memory leak' in some situations where a free running | ||
| 11 | * lane is collected before application shutdown | ||
| 12 | * 24-Aug-10 (2.0.6): Mem fixes, argument checking (lua_toLinda result), thread name | ||
| 13 | * 24-Jun-09 (2.0.4): Made friendly to already multithreaded host apps. | ||
| 7 | * 20-Oct-08 (2.0.2): Added closing of free-running threads, but it does | 14 | * 20-Oct-08 (2.0.2): Added closing of free-running threads, but it does |
| 8 | * not seem to eliminate the occasional segfaults at process | 15 | * not seem to eliminate the occasional segfaults at process |
| 9 | * exit. | 16 | * exit. |
| @@ -13,10 +20,9 @@ | |||
| 13 | * 18-Sep-06 AKa: Started the module. | 20 | * 18-Sep-06 AKa: Started the module. |
| 14 | * | 21 | * |
| 15 | * Platforms (tested internally): | 22 | * Platforms (tested internally): |
| 16 | * OS X (10.5.4 PowerPC/Intel) | 23 | * OS X (10.5.7 PowerPC/Intel) |
| 17 | * Linux x86 (Ubuntu 8.04) | 24 | * Linux x86 (Ubuntu 8.04) |
| 18 | * Win32 (Windows XP Home SP2, Visual C++ 2005/2008 Express) | 25 | * Win32 (Windows XP Home SP2, Visual C++ 2005/2008 Express) |
| 19 | * PocketPC (TBD) | ||
| 20 | * | 26 | * |
| 21 | * Platforms (tested externally): | 27 | * Platforms (tested externally): |
| 22 | * Win32 (MSYS) by Ross Berteig. | 28 | * Win32 (MSYS) by Ross Berteig. |
| @@ -54,15 +60,16 @@ | |||
| 54 | * | 60 | * |
| 55 | * To-do: | 61 | * To-do: |
| 56 | * | 62 | * |
| 63 | * Make waiting threads cancelable. | ||
| 57 | * ... | 64 | * ... |
| 58 | */ | 65 | */ |
| 59 | 66 | ||
| 60 | const char *VERSION= "2.0.3"; | 67 | const char *VERSION= "2.0.10"; |
| 61 | 68 | ||
| 62 | /* | 69 | /* |
| 63 | =============================================================================== | 70 | =============================================================================== |
| 64 | 71 | ||
| 65 | Copyright (C) 2007-08 Asko Kauppi <akauppi@gmail.com> | 72 | Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com> |
| 66 | 73 | ||
| 67 | Permission is hereby granted, free of charge, to any person obtaining a copy | 74 | Permission is hereby granted, free of charge, to any person obtaining a copy |
| 68 | of this software and associated documentation files (the "Software"), to deal | 75 | of this software and associated documentation files (the "Software"), to deal |
| @@ -84,10 +91,11 @@ THE SOFTWARE. | |||
| 84 | 91 | ||
| 85 | =============================================================================== | 92 | =============================================================================== |
| 86 | */ | 93 | */ |
| 94 | |||
| 87 | #include <string.h> | 95 | #include <string.h> |
| 88 | #include <stdio.h> | 96 | #include <stdio.h> |
| 89 | #include <ctype.h> | ||
| 90 | #include <stdlib.h> | 97 | #include <stdlib.h> |
| 98 | #include <ctype.h> | ||
| 91 | 99 | ||
| 92 | #include "lua.h" | 100 | #include "lua.h" |
| 93 | #include "lauxlib.h" | 101 | #include "lauxlib.h" |
| @@ -127,7 +135,59 @@ THE SOFTWARE. | |||
| 127 | static char keeper_chunk[]= | 135 | static char keeper_chunk[]= |
| 128 | #include "keeper.lch" | 136 | #include "keeper.lch" |
| 129 | 137 | ||
| 130 | struct s_lane; | 138 | // NOTE: values to be changed by either thread, during execution, without |
| 139 | // locking, are marked "volatile" | ||
| 140 | // | ||
| 141 | struct s_lane { | ||
| 142 | THREAD_T thread; | ||
| 143 | // | ||
| 144 | // M: sub-thread OS thread | ||
| 145 | // S: not used | ||
| 146 | |||
| 147 | char threadName[64]; //Optional, for debugging and such. owerflowable by a strcpy. | ||
| 148 | |||
| 149 | lua_State *L; | ||
| 150 | // | ||
| 151 | // M: prepares the state, and reads results | ||
| 152 | // S: while S is running, M must keep out of modifying the state | ||
| 153 | |||
| 154 | volatile enum e_status status; | ||
| 155 | // | ||
| 156 | // M: sets to PENDING (before launching) | ||
| 157 | // S: updates -> RUNNING/WAITING -> DONE/ERROR_ST/CANCELLED | ||
| 158 | |||
| 159 | volatile bool_t cancel_request; | ||
| 160 | // | ||
| 161 | // M: sets to FALSE, flags TRUE for cancel request | ||
| 162 | // S: reads to see if cancel is requested | ||
| 163 | |||
| 164 | #if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) ) | ||
| 165 | SIGNAL_T done_signal_; | ||
| 166 | // | ||
| 167 | // M: Waited upon at lane ending (if Posix with no PTHREAD_TIMEDJOIN) | ||
| 168 | // S: sets the signal once cancellation is noticed (avoids a kill) | ||
| 169 | |||
| 170 | MUTEX_T done_lock_; | ||
| 171 | // | ||
| 172 | // Lock required by 'done_signal' condition variable, protecting | ||
| 173 | // lane status changes to DONE/ERROR_ST/CANCELLED. | ||
| 174 | #endif | ||
| 175 | |||
| 176 | volatile enum { | ||
| 177 | NORMAL, // normal master side state | ||
| 178 | KILLED // issued an OS kill | ||
| 179 | } mstatus; | ||
| 180 | // | ||
| 181 | // M: sets to NORMAL, if issued a kill changes to KILLED | ||
| 182 | // S: not used | ||
| 183 | |||
| 184 | struct s_lane * volatile selfdestruct_next; | ||
| 185 | // | ||
| 186 | // M: sets to non-NULL if facing lane handle '__gc' cycle but the lane | ||
| 187 | // is still running | ||
| 188 | // S: cleans up after itself if non-NULL at lane exit | ||
| 189 | }; | ||
| 190 | |||
| 131 | static bool_t cancel_test( lua_State *L ); | 191 | static bool_t cancel_test( lua_State *L ); |
| 132 | static void cancel_error( lua_State *L ); | 192 | static void cancel_error( lua_State *L ); |
| 133 | 193 | ||
| @@ -360,10 +420,11 @@ int keeper_call( lua_State* K, const char *func_name, | |||
| 360 | int Ktos= lua_gettop(K); | 420 | int Ktos= lua_gettop(K); |
| 361 | int retvals; | 421 | int retvals; |
| 362 | 422 | ||
| 423 | STACK_GROW( K, 2 ); | ||
| 424 | |||
| 363 | lua_getglobal( K, func_name ); | 425 | lua_getglobal( K, func_name ); |
| 364 | ASSERT_L( lua_isfunction(K,-1) ); | 426 | ASSERT_L( lua_isfunction(K,-1) ); |
| 365 | 427 | ||
| 366 | STACK_GROW( K, 1 ); | ||
| 367 | lua_pushlightuserdata( K, linda ); | 428 | lua_pushlightuserdata( K, linda ); |
| 368 | 429 | ||
| 369 | luaG_inter_copy( L,K, args ); // L->K | 430 | luaG_inter_copy( L,K, args ); // L->K |
| @@ -408,6 +469,8 @@ LUAG_FUNC( linda_send ) { | |||
| 408 | struct s_Keeper *K; | 469 | struct s_Keeper *K; |
| 409 | time_d timeout= -1.0; | 470 | time_d timeout= -1.0; |
| 410 | uint_t key_i= 2; // index of first key, if timeout not there | 471 | uint_t key_i= 2; // index of first key, if timeout not there |
| 472 | |||
| 473 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | ||
| 411 | 474 | ||
| 412 | if (lua_isnumber(L,2)) { | 475 | if (lua_isnumber(L,2)) { |
| 413 | timeout= SIGNAL_TIMEOUT_PREPARE( lua_tonumber(L,2) ); | 476 | timeout= SIGNAL_TIMEOUT_PREPARE( lua_tonumber(L,2) ); |
| @@ -449,10 +512,36 @@ STACK_MID(KL,0) | |||
| 449 | cancel= cancel_test( L ); // testing here causes no delays | 512 | cancel= cancel_test( L ); // testing here causes no delays |
| 450 | if (cancel) break; | 513 | if (cancel) break; |
| 451 | 514 | ||
| 515 | // Bugfix by Benoit Germain Dec-2009: change status of lane to "waiting" | ||
| 516 | // | ||
| 517 | #if 1 | ||
| 518 | { | ||
| 519 | struct s_lane *s; | ||
| 520 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | ||
| 521 | STACK_GROW(L,1); | ||
| 522 | |||
| 523 | STACK_CHECK(L) | ||
| 524 | lua_pushlightuserdata( L, CANCEL_TEST_KEY ); | ||
| 525 | lua_rawget( L, LUA_REGISTRYINDEX ); | ||
| 526 | s= lua_touserdata( L, -1 ); // lightuserdata (true 's_lane' pointer) / nil | ||
| 527 | lua_pop(L,1); | ||
| 528 | STACK_END(L,0) | ||
| 529 | if (s) { | ||
| 530 | prev_status = s->status; | ||
| 531 | s->status = WAITING; | ||
| 532 | } | ||
| 533 | if (!SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout )) { | ||
| 534 | if (s) { s->status = prev_status; } | ||
| 535 | break; | ||
| 536 | } | ||
| 537 | if (s) s->status = prev_status; | ||
| 538 | } | ||
| 539 | #else | ||
| 452 | // K lock will be released for the duration of wait and re-acquired | 540 | // K lock will be released for the duration of wait and re-acquired |
| 453 | // | 541 | // |
| 454 | if (!SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout )) | 542 | if (!SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout )) |
| 455 | break; // timeout | 543 | break; // timeout |
| 544 | #endif | ||
| 456 | } | 545 | } |
| 457 | } | 546 | } |
| 458 | STACK_END(KL,0) | 547 | STACK_END(KL,0) |
| @@ -483,6 +572,8 @@ LUAG_FUNC( linda_receive ) { | |||
| 483 | time_d timeout= -1.0; | 572 | time_d timeout= -1.0; |
| 484 | uint_t key_i= 2; | 573 | uint_t key_i= 2; |
| 485 | 574 | ||
| 575 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | ||
| 576 | |||
| 486 | if (lua_isnumber(L,2)) { | 577 | if (lua_isnumber(L,2)) { |
| 487 | timeout= SIGNAL_TIMEOUT_PREPARE( lua_tonumber(L,2) ); | 578 | timeout= SIGNAL_TIMEOUT_PREPARE( lua_tonumber(L,2) ); |
| 488 | key_i++; | 579 | key_i++; |
| @@ -509,10 +600,36 @@ LUAG_FUNC( linda_receive ) { | |||
| 509 | cancel= cancel_test( L ); // testing here causes no delays | 600 | cancel= cancel_test( L ); // testing here causes no delays |
| 510 | if (cancel) break; | 601 | if (cancel) break; |
| 511 | 602 | ||
| 603 | // Bugfix by Benoit Germain Dec-2009: change status of lane to "waiting" | ||
| 604 | // | ||
| 605 | #if 1 | ||
| 606 | { | ||
| 607 | struct s_lane *s; | ||
| 608 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | ||
| 609 | STACK_GROW(L,1); | ||
| 610 | |||
| 611 | STACK_CHECK(L) | ||
| 612 | lua_pushlightuserdata( L, CANCEL_TEST_KEY ); | ||
| 613 | lua_rawget( L, LUA_REGISTRYINDEX ); | ||
| 614 | s= lua_touserdata( L, -1 ); // lightuserdata (true 's_lane' pointer) / nil | ||
| 615 | lua_pop(L,1); | ||
| 616 | STACK_END(L,0) | ||
| 617 | if (s) { | ||
| 618 | prev_status = s->status; | ||
| 619 | s->status = WAITING; | ||
| 620 | } | ||
| 621 | if (!SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout )) { | ||
| 622 | if (s) { s->status = prev_status; } | ||
| 623 | break; | ||
| 624 | } | ||
| 625 | if (s) s->status = prev_status; | ||
| 626 | } | ||
| 627 | #else | ||
| 512 | // Release the K lock for the duration of wait, and re-acquire | 628 | // Release the K lock for the duration of wait, and re-acquire |
| 513 | // | 629 | // |
| 514 | if (!SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout )) | 630 | if (!SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout )) |
| 515 | break; | 631 | break; |
| 632 | #endif | ||
| 516 | } | 633 | } |
| 517 | } | 634 | } |
| 518 | } | 635 | } |
| @@ -535,8 +652,11 @@ LUAG_FUNC( linda_receive ) { | |||
| 535 | LUAG_FUNC( linda_set ) { | 652 | LUAG_FUNC( linda_set ) { |
| 536 | struct s_Linda *linda= lua_toLinda( L, 1 ); | 653 | struct s_Linda *linda= lua_toLinda( L, 1 ); |
| 537 | bool_t has_value= !lua_isnil(L,3); | 654 | bool_t has_value= !lua_isnil(L,3); |
| 655 | struct s_Keeper *K; | ||
| 538 | 656 | ||
| 539 | struct s_Keeper *K= keeper_acquire( linda ); | 657 | luaL_argcheck( L, linda, 1, "expected a linda object!"); |
| 658 | |||
| 659 | K= keeper_acquire( linda ); | ||
| 540 | { | 660 | { |
| 541 | int pushed= keeper_call( K->L, "set", L, linda, 2 ); | 661 | int pushed= keeper_call( K->L, "set", L, linda, 2 ); |
| 542 | ASSERT_L( pushed==0 ); | 662 | ASSERT_L( pushed==0 ); |
| @@ -561,8 +681,11 @@ LUAG_FUNC( linda_set ) { | |||
| 561 | LUAG_FUNC( linda_get ) { | 681 | LUAG_FUNC( linda_get ) { |
| 562 | struct s_Linda *linda= lua_toLinda( L, 1 ); | 682 | struct s_Linda *linda= lua_toLinda( L, 1 ); |
| 563 | int pushed; | 683 | int pushed; |
| 684 | struct s_Keeper *K; | ||
| 564 | 685 | ||
| 565 | struct s_Keeper *K= keeper_acquire( linda ); | 686 | luaL_argcheck( L, linda, 1, "expected a linda object!"); |
| 687 | |||
| 688 | K= keeper_acquire( linda ); | ||
| 566 | { | 689 | { |
| 567 | pushed= keeper_call( K->L, "get", L, linda, 2 ); | 690 | pushed= keeper_call( K->L, "get", L, linda, 2 ); |
| 568 | ASSERT_L( pushed==0 || pushed==1 ); | 691 | ASSERT_L( pushed==0 || pushed==1 ); |
| @@ -580,8 +703,11 @@ LUAG_FUNC( linda_get ) { | |||
| 580 | */ | 703 | */ |
| 581 | LUAG_FUNC( linda_limit ) { | 704 | LUAG_FUNC( linda_limit ) { |
| 582 | struct s_Linda *linda= lua_toLinda( L, 1 ); | 705 | struct s_Linda *linda= lua_toLinda( L, 1 ); |
| 706 | struct s_Keeper *K; | ||
| 707 | |||
| 708 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | ||
| 583 | 709 | ||
| 584 | struct s_Keeper *K= keeper_acquire( linda ); | 710 | K= keeper_acquire( linda ); |
| 585 | { | 711 | { |
| 586 | int pushed= keeper_call( K->L, "limit", L, linda, 2 ); | 712 | int pushed= keeper_call( K->L, "limit", L, linda, 2 ); |
| 587 | ASSERT_L( pushed==0 ); | 713 | ASSERT_L( pushed==0 ); |
| @@ -604,6 +730,7 @@ LUAG_FUNC( linda_limit ) { | |||
| 604 | */ | 730 | */ |
| 605 | LUAG_FUNC( linda_deep ) { | 731 | LUAG_FUNC( linda_deep ) { |
| 606 | struct s_Linda *linda= lua_toLinda( L, 1 ); | 732 | struct s_Linda *linda= lua_toLinda( L, 1 ); |
| 733 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | ||
| 607 | lua_pushlightuserdata( L, linda ); // just the address | 734 | lua_pushlightuserdata( L, linda ); // just the address |
| 608 | return 1; | 735 | return 1; |
| 609 | } | 736 | } |
| @@ -761,13 +888,13 @@ static int run_finalizers( lua_State *L, int lua_rc ) | |||
| 761 | return 0; // no finalizers | 888 | return 0; // no finalizers |
| 762 | 889 | ||
| 763 | tbl_index= lua_gettop(L); | 890 | tbl_index= lua_gettop(L); |
| 764 | error_index= (lua_rc!=0) ? tbl_index-1 : 0; // absolute indices | 891 | error_index= (lua_rc!=0) ? tbl_index-2 : 0; // absolute indices |
| 765 | 892 | ||
| 766 | STACK_GROW(L,4); | 893 | STACK_GROW(L,4); |
| 767 | 894 | ||
| 768 | // [-1]: { func [, ...] } | 895 | // [-1]: { func [, ...] } |
| 769 | // | 896 | // |
| 770 | for( n= lua_objlen(L,-1); n>0; n-- ) { | 897 | for( n= (unsigned int)lua_objlen(L,-1); n>0; n-- ) { |
| 771 | unsigned args= 0; | 898 | unsigned args= 0; |
| 772 | lua_pushinteger( L,n ); | 899 | lua_pushinteger( L,n ); |
| 773 | lua_gettable( L, -2 ); | 900 | lua_gettable( L, -2 ); |
| @@ -805,57 +932,6 @@ static int run_finalizers( lua_State *L, int lua_rc ) | |||
| 805 | /*---=== Threads ===--- | 932 | /*---=== Threads ===--- |
| 806 | */ | 933 | */ |
| 807 | 934 | ||
| 808 | // NOTE: values to be changed by either thread, during execution, without | ||
| 809 | // locking, are marked "volatile" | ||
| 810 | // | ||
| 811 | struct s_lane { | ||
| 812 | THREAD_T thread; | ||
| 813 | // | ||
| 814 | // M: sub-thread OS thread | ||
| 815 | // S: not used | ||
| 816 | |||
| 817 | lua_State *L; | ||
| 818 | // | ||
| 819 | // M: prepares the state, and reads results | ||
| 820 | // S: while S is running, M must keep out of modifying the state | ||
| 821 | |||
| 822 | volatile enum e_status status; | ||
| 823 | // | ||
| 824 | // M: sets to PENDING (before launching) | ||
| 825 | // S: updates -> RUNNING/WAITING -> DONE/ERROR_ST/CANCELLED | ||
| 826 | |||
| 827 | volatile bool_t cancel_request; | ||
| 828 | // | ||
| 829 | // M: sets to FALSE, flags TRUE for cancel request | ||
| 830 | // S: reads to see if cancel is requested | ||
| 831 | |||
| 832 | #if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) ) | ||
| 833 | SIGNAL_T done_signal_; | ||
| 834 | // | ||
| 835 | // M: Waited upon at lane ending (if Posix with no PTHREAD_TIMEDJOIN) | ||
| 836 | // S: sets the signal once cancellation is noticed (avoids a kill) | ||
| 837 | |||
| 838 | MUTEX_T done_lock_; | ||
| 839 | // | ||
| 840 | // Lock required by 'done_signal' condition variable, protecting | ||
| 841 | // lane status changes to DONE/ERROR_ST/CANCELLED. | ||
| 842 | #endif | ||
| 843 | |||
| 844 | volatile enum { | ||
| 845 | NORMAL, // normal master side state | ||
| 846 | KILLED // issued an OS kill | ||
| 847 | } mstatus; | ||
| 848 | // | ||
| 849 | // M: sets to NORMAL, if issued a kill changes to KILLED | ||
| 850 | // S: not used | ||
| 851 | |||
| 852 | struct s_lane * volatile selfdestruct_next; | ||
| 853 | // | ||
| 854 | // M: sets to non-NULL if facing lane handle '__gc' cycle but the lane | ||
| 855 | // is still running | ||
| 856 | // S: cleans up after itself if non-NULL at lane exit | ||
| 857 | }; | ||
| 858 | |||
| 859 | static MUTEX_T selfdestruct_cs; | 935 | static MUTEX_T selfdestruct_cs; |
| 860 | // | 936 | // |
| 861 | // Protects modifying the selfdestruct chain | 937 | // Protects modifying the selfdestruct chain |
| @@ -985,11 +1061,13 @@ static void selfdestruct_atexit( void ) { | |||
| 985 | // Linux (at least 64-bit): CAUSES A SEGFAULT IF THIS BLOCK IS ENABLED | 1061 | // Linux (at least 64-bit): CAUSES A SEGFAULT IF THIS BLOCK IS ENABLED |
| 986 | // and works without the block (so let's leave those lanes running) | 1062 | // and works without the block (so let's leave those lanes running) |
| 987 | // | 1063 | // |
| 988 | #if 1 | 1064 | //we want to free memory and such when we exit. |
| 1065 | #if 0 | ||
| 989 | // 2.0.2: at least timer lane is still here | 1066 | // 2.0.2: at least timer lane is still here |
| 990 | // | 1067 | // |
| 991 | //fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n ); | 1068 | DEBUGEXEC(fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n )); |
| 992 | #else | 1069 | #else |
| 1070 | n=0; | ||
| 993 | MUTEX_LOCK( &selfdestruct_cs ); | 1071 | MUTEX_LOCK( &selfdestruct_cs ); |
| 994 | { | 1072 | { |
| 995 | struct s_lane *s= selfdestruct_first; | 1073 | struct s_lane *s= selfdestruct_first; |
| @@ -998,6 +1076,8 @@ static void selfdestruct_atexit( void ) { | |||
| 998 | s->selfdestruct_next= NULL; // detach from selfdestruct chain | 1076 | s->selfdestruct_next= NULL; // detach from selfdestruct chain |
| 999 | 1077 | ||
| 1000 | THREAD_KILL( &s->thread ); | 1078 | THREAD_KILL( &s->thread ); |
| 1079 | lua_close(s->L); | ||
| 1080 | free(s); | ||
| 1001 | s= next_s; | 1081 | s= next_s; |
| 1002 | n++; | 1082 | n++; |
| 1003 | } | 1083 | } |
| @@ -1005,9 +1085,16 @@ static void selfdestruct_atexit( void ) { | |||
| 1005 | } | 1085 | } |
| 1006 | MUTEX_UNLOCK( &selfdestruct_cs ); | 1086 | MUTEX_UNLOCK( &selfdestruct_cs ); |
| 1007 | 1087 | ||
| 1008 | fprintf( stderr, "Killed %d lane(s) at process end.\n", n ); | 1088 | DEBUGEXEC(fprintf( stderr, "Killed %d lane(s) at process end.\n", n )); |
| 1009 | #endif | 1089 | #endif |
| 1010 | } | 1090 | } |
| 1091 | { | ||
| 1092 | int i; | ||
| 1093 | for(i=0;i<KEEPER_STATES_N;i++){ | ||
| 1094 | lua_close(keeper[i].L); | ||
| 1095 | keeper[i].L = 0; | ||
| 1096 | } | ||
| 1097 | } | ||
| 1011 | } | 1098 | } |
| 1012 | 1099 | ||
| 1013 | 1100 | ||
| @@ -1153,6 +1240,38 @@ static int lane_error( lua_State *L ) { | |||
| 1153 | } | 1240 | } |
| 1154 | #endif | 1241 | #endif |
| 1155 | 1242 | ||
| 1243 | #if defined PLATFORM_WIN32 && !defined __GNUC__ | ||
| 1244 | //see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx | ||
| 1245 | #define MS_VC_EXCEPTION 0x406D1388 | ||
| 1246 | #pragma pack(push,8) | ||
| 1247 | typedef struct tagTHREADNAME_INFO | ||
| 1248 | { | ||
| 1249 | DWORD dwType; // Must be 0x1000. | ||
| 1250 | LPCSTR szName; // Pointer to name (in user addr space). | ||
| 1251 | DWORD dwThreadID; // Thread ID (-1=caller thread). | ||
| 1252 | DWORD dwFlags; // Reserved for future use, must be zero. | ||
| 1253 | } THREADNAME_INFO; | ||
| 1254 | #pragma pack(pop) | ||
| 1255 | |||
| 1256 | void SetThreadName( DWORD dwThreadID, char* threadName) | ||
| 1257 | { | ||
| 1258 | THREADNAME_INFO info; | ||
| 1259 | Sleep(10); | ||
| 1260 | info.dwType = 0x1000; | ||
| 1261 | info.szName = threadName; | ||
| 1262 | info.dwThreadID = dwThreadID; | ||
| 1263 | info.dwFlags = 0; | ||
| 1264 | |||
| 1265 | __try | ||
| 1266 | { | ||
| 1267 | RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); | ||
| 1268 | } | ||
| 1269 | __except(EXCEPTION_EXECUTE_HANDLER) | ||
| 1270 | { | ||
| 1271 | } | ||
| 1272 | } | ||
| 1273 | #endif | ||
| 1274 | |||
| 1156 | 1275 | ||
| 1157 | //--- | 1276 | //--- |
| 1158 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | 1277 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) |
| @@ -1165,7 +1284,12 @@ static int lane_error( lua_State *L ) { | |||
| 1165 | int rc, rc2; | 1284 | int rc, rc2; |
| 1166 | lua_State *L= s->L; | 1285 | lua_State *L= s->L; |
| 1167 | 1286 | ||
| 1168 | s->status= RUNNING; // PENDING -> RUNNING | 1287 | |
| 1288 | #if defined PLATFORM_WIN32 && !defined __GNUC__ | ||
| 1289 | SetThreadName(-1, s->threadName); | ||
| 1290 | #endif | ||
| 1291 | |||
| 1292 | s->status= RUNNING; // PENDING -> RUNNING | ||
| 1169 | 1293 | ||
| 1170 | // Tie "set_finalizer()" to the state | 1294 | // Tie "set_finalizer()" to the state |
| 1171 | // | 1295 | // |
| @@ -1243,7 +1367,7 @@ static int lane_error( lua_State *L ) { | |||
| 1243 | // We're a free-running thread and no-one's there to clean us up. | 1367 | // We're a free-running thread and no-one's there to clean us up. |
| 1244 | // | 1368 | // |
| 1245 | lua_close( s->L ); | 1369 | lua_close( s->L ); |
| 1246 | L= 0; | 1370 | s->L = L = 0; |
| 1247 | 1371 | ||
| 1248 | #if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) ) | 1372 | #if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) ) |
| 1249 | SIGNAL_FREE( &s->done_signal_ ); | 1373 | SIGNAL_FREE( &s->done_signal_ ); |
| @@ -1290,121 +1414,144 @@ static int lane_error( lua_State *L ) { | |||
| 1290 | // | 1414 | // |
| 1291 | LUAG_FUNC( thread_new ) | 1415 | LUAG_FUNC( thread_new ) |
| 1292 | { | 1416 | { |
| 1293 | lua_State *L2; | 1417 | lua_State *L2; |
| 1294 | struct s_lane *s; | 1418 | struct s_lane *s; |
| 1295 | struct s_lane **ud; | 1419 | struct s_lane **ud; |
| 1296 | 1420 | const char *threadName = 0; | |
| 1297 | const char *libs= lua_tostring( L, 2 ); | 1421 | |
| 1298 | uint_t cs= luaG_optunsigned( L, 3,0); | 1422 | const char *libs= lua_tostring( L, 2 ); |
| 1299 | int prio= luaL_optinteger( L, 4,0); | 1423 | uint_t cs= luaG_optunsigned( L, 3,0); |
| 1300 | uint_t glob= luaG_isany(L,5) ? 5:0; | 1424 | int prio= (int)luaL_optinteger( L, 4,0); |
| 1301 | 1425 | uint_t glob= luaG_isany(L,5) ? 5:0; | |
| 1302 | #define FIXED_ARGS (5) | 1426 | |
| 1303 | uint_t args= lua_gettop(L) - FIXED_ARGS; | 1427 | #define FIXED_ARGS (5) |
| 1304 | 1428 | uint_t args= lua_gettop(L) - FIXED_ARGS; | |
| 1305 | if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) { | 1429 | |
| 1306 | luaL_error( L, "Priority out of range: %d..+%d (%d)", | 1430 | if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) |
| 1307 | THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio ); | 1431 | { |
| 1308 | } | 1432 | luaL_error( L, "Priority out of range: %d..+%d (%d)", |
| 1309 | 1433 | THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio ); | |
| 1310 | /* --- Create and prepare the sub state --- */ | 1434 | } |
| 1311 | 1435 | ||
| 1312 | L2 = luaL_newstate(); // uses standard 'realloc()'-based allocator, | 1436 | /* --- Create and prepare the sub state --- */ |
| 1313 | // sets the panic callback | 1437 | |
| 1314 | 1438 | L2 = luaL_newstate(); // uses standard 'realloc()'-based allocator, | |
| 1315 | if (!L2) luaL_error( L, "'luaL_newstate()' failed; out of memory" ); | 1439 | // sets the panic callback |
| 1316 | 1440 | ||
| 1317 | STACK_GROW( L,2 ); | 1441 | if (!L2) luaL_error( L, "'luaL_newstate()' failed; out of memory" ); |
| 1318 | 1442 | ||
| 1319 | // Setting the globals table (needs to be done before loading stdlibs, | 1443 | STACK_GROW( L,2 ); |
| 1320 | // and the lane function) | 1444 | |
| 1321 | // | 1445 | // Setting the globals table (needs to be done before loading stdlibs, |
| 1322 | if (glob!=0) { | 1446 | // and the lane function) |
| 1323 | STACK_CHECK(L) | 1447 | // |
| 1324 | if (!lua_istable(L,glob)) | 1448 | if (glob!=0) |
| 1325 | luaL_error( L, "Expected table, got %s", luaG_typename(L,glob) ); | 1449 | { |
| 1326 | 1450 | STACK_CHECK(L) | |
| 1327 | lua_pushvalue( L, glob ); | 1451 | if (!lua_istable(L,glob)) |
| 1328 | luaG_inter_move( L,L2, 1 ); // moves the table to L2 | 1452 | luaL_error( L, "Expected table, got %s", luaG_typename(L,glob) ); |
| 1329 | 1453 | ||
| 1330 | // L2 [-1]: table of globals | 1454 | lua_pushvalue( L, glob ); |
| 1331 | 1455 | lua_pushstring( L, "threadName"); | |
| 1332 | // "You can change the global environment of a Lua thread using lua_replace" | 1456 | lua_gettable( L, -2); |
| 1333 | // (refman-5.0.pdf p. 30) | 1457 | threadName = lua_tostring( L, -1); |
| 1334 | // | 1458 | lua_pop( L, 1); |
| 1335 | lua_replace( L2, LUA_GLOBALSINDEX ); | 1459 | luaG_inter_move( L,L2, 1 ); // moves the table to L2 |
| 1336 | STACK_END(L,0) | 1460 | |
| 1337 | } | 1461 | // L2 [-1]: table of globals |
| 1338 | 1462 | ||
| 1339 | // Selected libraries | 1463 | // "You can change the global environment of a Lua thread using lua_replace" |
| 1340 | // | 1464 | // (refman-5.0.pdf p. 30) |
| 1341 | if (libs) { | 1465 | // |
| 1342 | const char *err= luaG_openlibs( L2, libs ); | 1466 | lua_replace( L2, LUA_GLOBALSINDEX ); |
| 1343 | ASSERT_L( !err ); // bad libs should have been noticed by 'lanes.lua' | 1467 | STACK_END(L,0) |
| 1344 | 1468 | } | |
| 1345 | serialize_require( L2 ); | 1469 | |
| 1346 | } | 1470 | // Selected libraries |
| 1347 | 1471 | // | |
| 1348 | // Lane main function | 1472 | if (libs) |
| 1349 | // | 1473 | { |
| 1350 | STACK_CHECK(L) | 1474 | const char *err= luaG_openlibs( L2, libs ); |
| 1351 | lua_pushvalue( L, 1 ); | 1475 | ASSERT_L( !err ); // bad libs should have been noticed by 'lanes.lua' |
| 1352 | luaG_inter_move( L,L2, 1 ); // L->L2 | 1476 | |
| 1353 | STACK_MID(L,0) | 1477 | serialize_require( L2 ); |
| 1354 | 1478 | } | |
| 1355 | ASSERT_L( lua_gettop(L2) == 1 ); | 1479 | |
| 1356 | ASSERT_L( lua_isfunction(L2,1) ); | 1480 | // Lane main function |
| 1357 | 1481 | // | |
| 1358 | // revive arguments | 1482 | STACK_CHECK(L) |
| 1359 | // | 1483 | if( lua_type(L, 1) == LUA_TFUNCTION) |
| 1360 | if (args) luaG_inter_copy( L,L2, args ); // L->L2 | 1484 | { |
| 1361 | STACK_MID(L,0) | 1485 | lua_pushvalue( L, 1 ); |
| 1362 | 1486 | luaG_inter_move( L,L2, 1 ); // L->L2 | |
| 1363 | ASSERT_L( (uint_t)lua_gettop(L2) == 1+args ); | 1487 | STACK_MID(L,0) |
| 1364 | ASSERT_L( lua_isfunction(L2,1) ); | 1488 | } |
| 1365 | 1489 | else if( lua_type(L, 1) == LUA_TSTRING) | |
| 1366 | // 's' is allocated from heap, not Lua, since its life span may surpass | 1490 | { |
| 1367 | // the handle's (if free running thread) | 1491 | // compile the string |
| 1368 | // | 1492 | if( luaL_loadstring( L2, lua_tostring( L, 1)) != 0) |
| 1369 | ud= lua_newuserdata( L, sizeof(struct s_lane*) ); | 1493 | { |
| 1370 | ASSERT_L(ud); | 1494 | luaL_error( L, "error when parsing lane function code"); |
| 1371 | 1495 | } | |
| 1372 | s= *ud= malloc( sizeof(struct s_lane) ); | 1496 | } |
| 1373 | ASSERT_L(s); | 1497 | |
| 1374 | 1498 | ASSERT_L( lua_gettop(L2) == 1 ); | |
| 1375 | //memset( s, 0, sizeof(struct s_lane) ); | 1499 | ASSERT_L( lua_isfunction(L2,1) ); |
| 1376 | s->L= L2; | 1500 | |
| 1377 | s->status= PENDING; | 1501 | // revive arguments |
| 1378 | s->cancel_request= FALSE; | 1502 | // |
| 1503 | if (args) luaG_inter_copy( L,L2, args ); // L->L2 | ||
| 1504 | STACK_MID(L,0) | ||
| 1505 | |||
| 1506 | ASSERT_L( (uint_t)lua_gettop(L2) == 1+args ); | ||
| 1507 | ASSERT_L( lua_isfunction(L2,1) ); | ||
| 1508 | |||
| 1509 | // 's' is allocated from heap, not Lua, since its life span may surpass | ||
| 1510 | // the handle's (if free running thread) | ||
| 1511 | // | ||
| 1512 | ud= lua_newuserdata( L, sizeof(struct s_lane*) ); | ||
| 1513 | ASSERT_L(ud); | ||
| 1514 | |||
| 1515 | s= *ud= malloc( sizeof(struct s_lane) ); | ||
| 1516 | ASSERT_L(s); | ||
| 1517 | |||
| 1518 | //memset( s, 0, sizeof(struct s_lane) ); | ||
| 1519 | s->L= L2; | ||
| 1520 | s->status= PENDING; | ||
| 1521 | s->cancel_request= FALSE; | ||
| 1522 | |||
| 1523 | threadName = threadName ? threadName : "<unnamed thread>"; | ||
| 1524 | strcpy(s->threadName, threadName); | ||
| 1379 | 1525 | ||
| 1380 | #if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) ) | 1526 | #if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) ) |
| 1381 | MUTEX_INIT( &s->done_lock_ ); | 1527 | MUTEX_INIT( &s->done_lock_ ); |
| 1382 | SIGNAL_INIT( &s->done_signal_ ); | 1528 | SIGNAL_INIT( &s->done_signal_ ); |
| 1383 | #endif | 1529 | #endif |
| 1384 | s->mstatus= NORMAL; | 1530 | s->mstatus= NORMAL; |
| 1385 | s->selfdestruct_next= NULL; | 1531 | s->selfdestruct_next= NULL; |
| 1386 | 1532 | ||
| 1387 | // Set metatable for the userdata | 1533 | // Set metatable for the userdata |
| 1388 | // | 1534 | // |
| 1389 | lua_pushvalue( L, lua_upvalueindex(1) ); | 1535 | lua_pushvalue( L, lua_upvalueindex(1) ); |
| 1390 | lua_setmetatable( L, -2 ); | 1536 | lua_setmetatable( L, -2 ); |
| 1391 | STACK_MID(L,1) | 1537 | STACK_MID(L,1) |
| 1392 | 1538 | ||
| 1393 | // Place 's' to registry, for 'cancel_test()' (even if 'cs'==0 we still | 1539 | // Place 's' to registry, for 'cancel_test()' (even if 'cs'==0 we still |
| 1394 | // do cancel tests at pending send/receive). | 1540 | // do cancel tests at pending send/receive). |
| 1395 | // | 1541 | // |
| 1396 | lua_pushlightuserdata( L2, CANCEL_TEST_KEY ); | 1542 | lua_pushlightuserdata( L2, CANCEL_TEST_KEY ); |
| 1397 | lua_pushlightuserdata( L2, s ); | 1543 | lua_pushlightuserdata( L2, s ); |
| 1398 | lua_rawset( L2, LUA_REGISTRYINDEX ); | 1544 | lua_rawset( L2, LUA_REGISTRYINDEX ); |
| 1399 | 1545 | ||
| 1400 | if (cs) { | 1546 | if (cs) |
| 1401 | lua_sethook( L2, cancel_hook, LUA_MASKCOUNT, cs ); | 1547 | { |
| 1402 | } | 1548 | lua_sethook( L2, cancel_hook, LUA_MASKCOUNT, cs ); |
| 1403 | 1549 | } | |
| 1404 | THREAD_CREATE( &s->thread, lane_main, s, prio ); | 1550 | |
| 1405 | STACK_END(L,1) | 1551 | THREAD_CREATE( &s->thread, lane_main, s, prio ); |
| 1406 | 1552 | STACK_END(L,1) | |
| 1407 | return 1; | 1553 | |
| 1554 | return 1; | ||
| 1408 | } | 1555 | } |
| 1409 | 1556 | ||
| 1410 | 1557 | ||
| @@ -1428,45 +1575,56 @@ STACK_END(L,1) | |||
| 1428 | // | 1575 | // |
| 1429 | // Todo: Maybe we should have a clear #define for selecting either behaviour. | 1576 | // Todo: Maybe we should have a clear #define for selecting either behaviour. |
| 1430 | // | 1577 | // |
| 1431 | LUAG_FUNC( thread_gc ) { | 1578 | LUAG_FUNC( thread_gc ) |
| 1432 | struct s_lane *s= lua_toLane(L,1); | 1579 | { |
| 1433 | 1580 | struct s_lane *s= lua_toLane(L,1); | |
| 1434 | // We can read 's->status' without locks, but not wait for it | 1581 | |
| 1435 | // | 1582 | // We can read 's->status' without locks, but not wait for it |
| 1436 | if (s->status < DONE) { | 1583 | // |
| 1437 | // | 1584 | if (s->status < DONE) |
| 1438 | selfdestruct_add(s); | 1585 | { |
| 1439 | assert( s->selfdestruct_next ); | 1586 | // |
| 1440 | return 0; | 1587 | selfdestruct_add(s); |
| 1441 | 1588 | assert( s->selfdestruct_next ); | |
| 1442 | } else if (s->mstatus==KILLED) { | 1589 | return 0; |
| 1443 | // Make sure a kill has proceeded, before cleaning up the data structure. | 1590 | |
| 1444 | // | 1591 | } |
| 1445 | // If not doing 'THREAD_WAIT()' we should close the Lua state here | 1592 | else if (s->mstatus==KILLED) |
| 1446 | // (can it be out of order, since we killed the lane abruptly?) | 1593 | { |
| 1447 | // | 1594 | // Make sure a kill has proceeded, before cleaning up the data structure. |
| 1595 | // | ||
| 1596 | // If not doing 'THREAD_WAIT()' we should close the Lua state here | ||
| 1597 | // (can it be out of order, since we killed the lane abruptly?) | ||
| 1598 | // | ||
| 1448 | #if 0 | 1599 | #if 0 |
| 1449 | lua_close( s->L ); | 1600 | lua_close( s->L ); |
| 1601 | s->L = 0; | ||
| 1450 | #else | 1602 | #else |
| 1451 | fprintf( stderr, "** Joining with a killed thread (needs testing) **" ); | 1603 | DEBUGEXEC(fprintf( stderr, "** Joining with a killed thread (needs testing) **" )); |
| 1452 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) | 1604 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) |
| 1453 | THREAD_WAIT( &s->thread, -1 ); | 1605 | THREAD_WAIT( &s->thread, -1 ); |
| 1454 | #else | 1606 | #else |
| 1455 | THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, -1 ); | 1607 | THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, -1 ); |
| 1456 | #endif | 1608 | #endif |
| 1457 | fprintf( stderr, "** Joined ok **" ); | 1609 | DEBUGEXEC(fprintf( stderr, "** Joined ok **" )); |
| 1458 | #endif | 1610 | #endif |
| 1459 | } | 1611 | } |
| 1460 | 1612 | else if( s->L) | |
| 1461 | // Clean up after a (finished) thread | 1613 | { |
| 1462 | // | 1614 | lua_close( s->L); |
| 1615 | s->L = 0; | ||
| 1616 | } | ||
| 1617 | |||
| 1618 | // Clean up after a (finished) thread | ||
| 1619 | // | ||
| 1463 | #if (! ((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN))) | 1620 | #if (! ((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN))) |
| 1464 | SIGNAL_FREE( &s->done_signal_ ); | 1621 | SIGNAL_FREE( &s->done_signal_ ); |
| 1465 | MUTEX_FREE( &s->done_lock_ ); | 1622 | MUTEX_FREE( &s->done_lock_ ); |
| 1466 | free(s); | ||
| 1467 | #endif | 1623 | #endif |
| 1468 | 1624 | ||
| 1469 | return 0; | 1625 | free(s); |
| 1626 | |||
| 1627 | return 0; | ||
| 1470 | } | 1628 | } |
| 1471 | 1629 | ||
| 1472 | 1630 | ||
| @@ -1614,10 +1772,11 @@ LUAG_FUNC( thread_join ) | |||
| 1614 | break; | 1772 | break; |
| 1615 | 1773 | ||
| 1616 | default: | 1774 | default: |
| 1617 | fprintf( stderr, "Status: %d\n", s->status ); | 1775 | DEBUGEXEC(fprintf( stderr, "Status: %d\n", s->status )); |
| 1618 | ASSERT_L( FALSE ); ret= 0; | 1776 | ASSERT_L( FALSE ); ret= 0; |
| 1619 | } | 1777 | } |
| 1620 | lua_close(L2); | 1778 | lua_close(L2); |
| 1779 | s->L = L2 = 0; | ||
| 1621 | 1780 | ||
| 1622 | return ret; | 1781 | return ret; |
| 1623 | } | 1782 | } |
| @@ -1627,48 +1786,6 @@ LUAG_FUNC( thread_join ) | |||
| 1627 | */ | 1786 | */ |
| 1628 | 1787 | ||
| 1629 | /* | 1788 | /* |
| 1630 | * Push a timer gateway Linda object; only one deep userdata is | ||
| 1631 | * created for this, each lane will get its own proxy. | ||
| 1632 | * | ||
| 1633 | * Note: this needs to be done on the C side; Lua wouldn't be able | ||
| 1634 | * to even see, when we've been initialized for the very first | ||
| 1635 | * time (with us, they will be). | ||
| 1636 | */ | ||
| 1637 | static | ||
| 1638 | void push_timer_gateway( lua_State *L ) { | ||
| 1639 | |||
| 1640 | /* No need to lock; 'static' is just fine | ||
| 1641 | */ | ||
| 1642 | static DEEP_PRELUDE *p; // = NULL | ||
| 1643 | |||
| 1644 | STACK_CHECK(L) | ||
| 1645 | if (!p) { | ||
| 1646 | // Create the Linda (only on first time) | ||
| 1647 | // | ||
| 1648 | // proxy_ud= deep_userdata( idfunc ) | ||
| 1649 | // | ||
| 1650 | lua_pushcfunction( L, luaG_deep_userdata ); | ||
| 1651 | lua_pushcfunction( L, LG_linda_id ); | ||
| 1652 | lua_call( L, 1 /*args*/, 1 /*retvals*/ ); | ||
| 1653 | |||
| 1654 | ASSERT_L( lua_isuserdata(L,-1) ); | ||
| 1655 | |||
| 1656 | // Proxy userdata contents is only a 'DEEP_PRELUDE*' pointer | ||
| 1657 | // | ||
| 1658 | p= * (DEEP_PRELUDE**) lua_touserdata( L, -1 ); | ||
| 1659 | ASSERT_L(p && p->refcount==1 && p->deep); | ||
| 1660 | |||
| 1661 | // [-1]: proxy for accessing the Linda | ||
| 1662 | |||
| 1663 | } else { | ||
| 1664 | /* Push a proxy based on the deep userdata we stored. | ||
| 1665 | */ | ||
| 1666 | luaG_push_proxy( L, LG_linda_id, p ); | ||
| 1667 | } | ||
| 1668 | STACK_END(L,1) | ||
| 1669 | } | ||
| 1670 | |||
| 1671 | /* | ||
| 1672 | * secs= now_secs() | 1789 | * secs= now_secs() |
| 1673 | * | 1790 | * |
| 1674 | * Returns the current time, as seconds (millisecond resolution). | 1791 | * Returns the current time, as seconds (millisecond resolution). |
| @@ -1697,12 +1814,12 @@ LUAG_FUNC( wakeup_conv ) | |||
| 1697 | // .isdst (daylight saving on/off) | 1814 | // .isdst (daylight saving on/off) |
| 1698 | 1815 | ||
| 1699 | STACK_CHECK(L) | 1816 | STACK_CHECK(L) |
| 1700 | lua_getfield( L, 1, "year" ); year= lua_tointeger(L,-1); lua_pop(L,1); | 1817 | lua_getfield( L, 1, "year" ); year= (int)lua_tointeger(L,-1); lua_pop(L,1); |
| 1701 | lua_getfield( L, 1, "month" ); month= lua_tointeger(L,-1); lua_pop(L,1); | 1818 | lua_getfield( L, 1, "month" ); month= (int)lua_tointeger(L,-1); lua_pop(L,1); |
| 1702 | lua_getfield( L, 1, "day" ); day= lua_tointeger(L,-1); lua_pop(L,1); | 1819 | lua_getfield( L, 1, "day" ); day= (int)lua_tointeger(L,-1); lua_pop(L,1); |
| 1703 | lua_getfield( L, 1, "hour" ); hour= lua_tointeger(L,-1); lua_pop(L,1); | 1820 | lua_getfield( L, 1, "hour" ); hour= (int)lua_tointeger(L,-1); lua_pop(L,1); |
| 1704 | lua_getfield( L, 1, "min" ); min= lua_tointeger(L,-1); lua_pop(L,1); | 1821 | lua_getfield( L, 1, "min" ); min= (int)lua_tointeger(L,-1); lua_pop(L,1); |
| 1705 | lua_getfield( L, 1, "sec" ); sec= lua_tointeger(L,-1); lua_pop(L,1); | 1822 | lua_getfield( L, 1, "sec" ); sec= (int)lua_tointeger(L,-1); lua_pop(L,1); |
| 1706 | 1823 | ||
| 1707 | // If Lua table has '.isdst' we trust that. If it does not, we'll let | 1824 | // If Lua table has '.isdst' we trust that. If it does not, we'll let |
| 1708 | // 'mktime' decide on whether the time is within DST or not (value -1). | 1825 | // 'mktime' decide on whether the time is within DST or not (value -1). |
| @@ -1744,19 +1861,11 @@ LUAG_FUNC( wakeup_conv ) | |||
| 1744 | lua_pushinteger( L, val ); \ | 1861 | lua_pushinteger( L, val ); \ |
| 1745 | lua_setglobal( L, #name ) | 1862 | lua_setglobal( L, #name ) |
| 1746 | 1863 | ||
| 1747 | 1864 | /* | |
| 1748 | int | 1865 | * One-time initializations |
| 1749 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | 1866 | */ |
| 1750 | __declspec(dllexport) | 1867 | static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ref ) { |
| 1751 | #endif | ||
| 1752 | luaopen_lanes( lua_State *L ) { | ||
| 1753 | const char *err; | 1868 | const char *err; |
| 1754 | static volatile char been_here; // =0 | ||
| 1755 | |||
| 1756 | // One time initializations: | ||
| 1757 | // | ||
| 1758 | if (!been_here) { | ||
| 1759 | been_here= TRUE; | ||
| 1760 | 1869 | ||
| 1761 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | 1870 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) |
| 1762 | now_secs(); // initialize 'now_secs()' internal offset | 1871 | now_secs(); // initialize 'now_secs()' internal offset |
| @@ -1806,10 +1915,87 @@ __declspec(dllexport) | |||
| 1806 | #endif | 1915 | #endif |
| 1807 | #endif | 1916 | #endif |
| 1808 | err= init_keepers(); | 1917 | err= init_keepers(); |
| 1809 | if (err) | 1918 | if (err) { |
| 1810 | luaL_error( L, "Unable to initialize: %s", err ); | 1919 | luaL_error( L, "Unable to initialize: %s", err ); |
| 1811 | } | 1920 | } |
| 1812 | 1921 | ||
| 1922 | // Initialize 'timer_deep'; a common Linda object shared by all states | ||
| 1923 | // | ||
| 1924 | ASSERT_L( timer_deep_ref && (!(*timer_deep_ref)) ); | ||
| 1925 | |||
| 1926 | STACK_CHECK(L) | ||
| 1927 | { | ||
| 1928 | // proxy_ud= deep_userdata( idfunc ) | ||
| 1929 | // | ||
| 1930 | lua_pushcfunction( L, luaG_deep_userdata ); | ||
| 1931 | lua_pushcfunction( L, LG_linda_id ); | ||
| 1932 | lua_call( L, 1 /*args*/, 1 /*retvals*/ ); | ||
| 1933 | |||
| 1934 | ASSERT_L( lua_isuserdata(L,-1) ); | ||
| 1935 | |||
| 1936 | // Proxy userdata contents is only a 'DEEP_PRELUDE*' pointer | ||
| 1937 | // | ||
| 1938 | *timer_deep_ref= * (DEEP_PRELUDE**) lua_touserdata( L, -1 ); | ||
| 1939 | ASSERT_L( (*timer_deep_ref) && (*timer_deep_ref)->refcount==1 && (*timer_deep_ref)->deep ); | ||
| 1940 | |||
| 1941 | lua_pop(L,1); // we don't need the proxy | ||
| 1942 | } | ||
| 1943 | STACK_END(L,0) | ||
| 1944 | } | ||
| 1945 | |||
| 1946 | int | ||
| 1947 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | ||
| 1948 | __declspec(dllexport) | ||
| 1949 | #endif | ||
| 1950 | luaopen_lanes( lua_State *L ) { | ||
| 1951 | |||
| 1952 | // Initialized by 'init_once_LOCKED()': the deep userdata Linda object | ||
| 1953 | // used for timers (each lane will get a proxy to this) | ||
| 1954 | // | ||
| 1955 | static volatile DEEP_PRELUDE *timer_deep; // = NULL | ||
| 1956 | |||
| 1957 | /* | ||
| 1958 | * Making one-time initializations. | ||
| 1959 | * | ||
| 1960 | * When the host application is single-threaded (and all threading happens via Lanes) | ||
| 1961 | * there is no problem. But if the host is multithreaded, we need to lock around the | ||
| 1962 | * initializations. | ||
| 1963 | */ | ||
| 1964 | static volatile int /*bool*/ go_ahead; // = 0 | ||
| 1965 | #ifdef PLATFORM_WIN32 | ||
| 1966 | { | ||
| 1967 | // TBD: Someone please replace this with reliable Win32 API code. Problem is, | ||
| 1968 | // there's no autoinitializing locks (s.a. PTHREAD_MUTEX_INITIALIZER) in | ||
| 1969 | // Windows so 'InterlockedIncrement' or something needs to be used. | ||
| 1970 | // This is 99.9999% safe, though (and always safe if host is single-threaded) | ||
| 1971 | // -- AKa 24-Jun-2009 | ||
| 1972 | // | ||
| 1973 | static volatile unsigned my_number; // = 0 | ||
| 1974 | |||
| 1975 | if (my_number++ == 0) { // almost atomic | ||
| 1976 | init_once_LOCKED(L, &timer_deep); | ||
| 1977 | go_ahead= 1; // let others pass | ||
| 1978 | } else { | ||
| 1979 | while( !go_ahead ) { Sleep(1); } // changes threads | ||
| 1980 | } | ||
| 1981 | } | ||
| 1982 | #else | ||
| 1983 | if (!go_ahead) { | ||
| 1984 | static pthread_mutex_t my_lock= PTHREAD_MUTEX_INITIALIZER; | ||
| 1985 | pthread_mutex_lock(&my_lock); | ||
| 1986 | { | ||
| 1987 | // Recheck now that we're within the lock | ||
| 1988 | // | ||
| 1989 | if (!go_ahead) { | ||
| 1990 | init_once_LOCKED(L, &timer_deep); | ||
| 1991 | go_ahead= 1; | ||
| 1992 | } | ||
| 1993 | } | ||
| 1994 | pthread_mutex_unlock(&my_lock); | ||
| 1995 | } | ||
| 1996 | #endif | ||
| 1997 | assert( timer_deep != 0 ); | ||
| 1998 | |||
| 1813 | // Linda identity function | 1999 | // Linda identity function |
| 1814 | // | 2000 | // |
| 1815 | REG_FUNC( linda_id ); | 2001 | REG_FUNC( linda_id ); |
| @@ -1835,7 +2021,7 @@ __declspec(dllexport) | |||
| 1835 | REG_FUNC( now_secs ); | 2021 | REG_FUNC( now_secs ); |
| 1836 | REG_FUNC( wakeup_conv ); | 2022 | REG_FUNC( wakeup_conv ); |
| 1837 | 2023 | ||
| 1838 | push_timer_gateway(L); | 2024 | luaG_push_proxy( L, LG_linda_id, (DEEP_PRELUDE *) timer_deep ); |
| 1839 | lua_setglobal( L, "timer_gateway" ); | 2025 | lua_setglobal( L, "timer_gateway" ); |
| 1840 | 2026 | ||
| 1841 | REG_INT2( max_prio, THREAD_PRIO_MAX ); | 2027 | REG_INT2( max_prio, THREAD_PRIO_MAX ); |
diff --git a/src/lanes.lua b/src/lanes.lua index c68506d..7ec8c76 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
| @@ -6,7 +6,8 @@ | |||
| 6 | -- Author: Asko Kauppi <akauppi@gmail.com> | 6 | -- Author: Asko Kauppi <akauppi@gmail.com> |
| 7 | -- | 7 | -- |
| 8 | -- History: | 8 | -- History: |
| 9 | -- Jun-08 AKa: major revise | 9 | -- 3-Dec-10 BGe: Added support to generate a lane from a string |
| 10 | -- Jun-08 AKa: major revise | ||
| 10 | -- 15-May-07 AKa: pthread_join():less version, some speedup & ability to | 11 | -- 15-May-07 AKa: pthread_join():less version, some speedup & ability to |
| 11 | -- handle more threads (~ 8000-9000, up from ~ 5000) | 12 | -- handle more threads (~ 8000-9000, up from ~ 5000) |
| 12 | -- 26-Feb-07 AKa: serialization working (C side) | 13 | -- 26-Feb-07 AKa: serialization working (C side) |
| @@ -15,7 +16,7 @@ | |||
| 15 | --[[ | 16 | --[[ |
| 16 | =============================================================================== | 17 | =============================================================================== |
| 17 | 18 | ||
| 18 | Copyright (C) 2007-08 Asko Kauppi <akauppi@gmail.com> | 19 | Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com> |
| 19 | 20 | ||
| 20 | Permission is hereby granted, free of charge, to any person obtaining a copy | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy |
| 21 | of this software and associated documentation files (the "Software"), to deal | 22 | of this software and associated documentation files (the "Software"), to deal |
| @@ -89,7 +90,7 @@ ABOUT= | |||
| 89 | author= "Asko Kauppi <akauppi@gmail.com>", | 90 | author= "Asko Kauppi <akauppi@gmail.com>", |
| 90 | description= "Running multiple Lua states in parallel", | 91 | description= "Running multiple Lua states in parallel", |
| 91 | license= "MIT/X11", | 92 | license= "MIT/X11", |
| 92 | copyright= "Copyright (c) 2007-08, Asko Kauppi", | 93 | copyright= "Copyright (c) 2007-10, Asko Kauppi", |
| 93 | version= _version, | 94 | version= _version, |
| 94 | } | 95 | } |
| 95 | 96 | ||
| @@ -123,6 +124,14 @@ end | |||
| 123 | -- | 124 | -- |
| 124 | -- lane_h.state: "pending"/"running"/"waiting"/"done"/"error"/"cancelled" | 125 | -- lane_h.state: "pending"/"running"/"waiting"/"done"/"error"/"cancelled" |
| 125 | -- | 126 | -- |
| 127 | -- Note: Would be great to be able to have '__ipairs' metamethod, that gets | ||
| 128 | -- called by 'ipairs()' function to custom iterate objects. We'd use it | ||
| 129 | -- for making sure a lane has ended (results are available); not requiring | ||
| 130 | -- the user to precede a loop by explicit 'h[0]' or 'h:join()'. | ||
| 131 | -- | ||
| 132 | -- Or, even better, 'ipairs()' should start valuing '__index' instead | ||
| 133 | -- of using raw reads that bypass it. | ||
| 134 | -- | ||
| 126 | local lane_mt= { | 135 | local lane_mt= { |
| 127 | __index= function( me, k ) | 136 | __index= function( me, k ) |
| 128 | if type(k) == "number" then | 137 | if type(k) == "number" then |
| @@ -260,8 +269,9 @@ function gen( ... ) | |||
| 260 | end | 269 | end |
| 261 | 270 | ||
| 262 | local func= select(n,...) | 271 | local func= select(n,...) |
| 263 | if type(func)~="function" then | 272 | local functype = type(func) |
| 264 | error( "Last parameter not function: "..tostring(func) ) | 273 | if functype ~= "function" and functype ~= "string" then |
| 274 | error( "Last parameter not function or string: "..tostring(func)) | ||
| 265 | end | 275 | end |
| 266 | 276 | ||
| 267 | -- Check 'libs' already here, so the error goes in the right place | 277 | -- Check 'libs' already here, so the error goes in the right place |
| @@ -302,9 +312,10 @@ lane_proxy= function( ud ) | |||
| 302 | local proxy= { | 312 | local proxy= { |
| 303 | _ud= ud, | 313 | _ud= ud, |
| 304 | 314 | ||
| 305 | -- void= me:cancel() | 315 | -- true|false= me:cancel() |
| 306 | -- | 316 | -- |
| 307 | cancel= function(me) thread_cancel(me._ud) end, | 317 | cancel= function(me, time, force) return thread_cancel(me._ud, time, force) end, |
| 318 | |||
| 308 | 319 | ||
| 309 | -- [...] | [nil,err,stack_tbl]= me:join( [wait_secs=-1] ) | 320 | -- [...] | [nil,err,stack_tbl]= me:join( [wait_secs=-1] ) |
| 310 | -- | 321 | -- |
| @@ -495,14 +506,18 @@ if first_time then | |||
| 495 | -- We let the timer lane be a "free running" thread; no handle to it | 506 | -- We let the timer lane be a "free running" thread; no handle to it |
| 496 | -- remains. | 507 | -- remains. |
| 497 | -- | 508 | -- |
| 498 | gen( "io", { priority=max_prio }, function() | 509 | gen( "io", { priority=max_prio, globals={threadName="LanesTimer"} }, function() |
| 499 | 510 | ||
| 500 | while true do | 511 | while true do |
| 501 | local next_wakeup= check_timers() | 512 | local next_wakeup= check_timers() |
| 502 | 513 | ||
| 503 | -- Sleep until next timer to wake up, or a set/clear command | 514 | -- Sleep until next timer to wake up, or a set/clear command |
| 504 | -- | 515 | -- |
| 505 | local secs= next_wakeup and (next_wakeup - now_secs()) or nil | 516 | local secs |
| 517 | if next_wakeup then | ||
| 518 | secs = next_wakeup - now_secs() | ||
| 519 | if secs < 0 then secs = 0 end | ||
| 520 | end | ||
| 506 | local linda= timer_gateway:receive( secs, TGW_KEY ) | 521 | local linda= timer_gateway:receive( secs, TGW_KEY ) |
| 507 | 522 | ||
| 508 | if linda then | 523 | if linda then |
diff --git a/src/threading.c b/src/threading.c index 68d1e41..00be243 100644 --- a/src/threading.c +++ b/src/threading.c | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * THREADING.C Copyright (c) 2007-08, Asko Kauppi | 2 | * THREADING.C Copyright (c) 2007-10, Asko Kauppi |
| 3 | * | 3 | * |
| 4 | * Lua Lanes OS threading specific code. | 4 | * Lua Lanes OS threading specific code. |
| 5 | * | 5 | * |
| @@ -10,7 +10,7 @@ | |||
| 10 | /* | 10 | /* |
| 11 | =============================================================================== | 11 | =============================================================================== |
| 12 | 12 | ||
| 13 | Copyright (C) 2007-08 Asko Kauppi <akauppi@gmail.com> | 13 | Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com> |
| 14 | 14 | ||
| 15 | Permission is hereby granted, free of charge, to any person obtaining a copy | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy |
| 16 | of this software and associated documentation files (the "Software"), to deal | 16 | of this software and associated documentation files (the "Software"), to deal |
| @@ -178,6 +178,11 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) { | |||
| 178 | 178 | ||
| 179 | ts->tv_sec= floor( abs_secs ); | 179 | ts->tv_sec= floor( abs_secs ); |
| 180 | ts->tv_nsec= ((long)((abs_secs - ts->tv_sec) * 1000.0 +0.5)) * 1000000UL; // 1ms = 1000000ns | 180 | ts->tv_nsec= ((long)((abs_secs - ts->tv_sec) * 1000.0 +0.5)) * 1000000UL; // 1ms = 1000000ns |
| 181 | if (ts->tv_nsec == 1000000000UL) | ||
| 182 | { | ||
| 183 | ts->tv_nsec = 0; | ||
| 184 | ts->tv_sec = ts->tv_sec + 1; | ||
| 185 | } | ||
| 181 | } | 186 | } |
| 182 | #endif | 187 | #endif |
| 183 | 188 | ||
diff --git a/src/tools.c b/src/tools.c index a2ec517..2f3140d 100644 --- a/src/tools.c +++ b/src/tools.c | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * TOOLS.C Copyright (c) 2002-08, Asko Kauppi | 2 | * TOOLS.C Copyright (c) 2002-10, Asko Kauppi |
| 3 | * | 3 | * |
| 4 | * Lua tools to support Lanes. | 4 | * Lua tools to support Lanes. |
| 5 | */ | 5 | */ |
| @@ -7,7 +7,7 @@ | |||
| 7 | /* | 7 | /* |
| 8 | =============================================================================== | 8 | =============================================================================== |
| 9 | 9 | ||
| 10 | Copyright (C) 2002-08 Asko Kauppi <akauppi@gmail.com> | 10 | Copyright (C) 2002-10 Asko Kauppi <akauppi@gmail.com> |
| 11 | 11 | ||
| 12 | Permission is hereby granted, free of charge, to any person obtaining a copy | 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 | 13 | of this software and associated documentation files (the "Software"), to deal |
| @@ -40,8 +40,6 @@ THE SOFTWARE. | |||
| 40 | #include <ctype.h> | 40 | #include <ctype.h> |
| 41 | #include <stdlib.h> | 41 | #include <stdlib.h> |
| 42 | 42 | ||
| 43 | static volatile lua_CFunction hijacked_tostring; // = NULL | ||
| 44 | |||
| 45 | MUTEX_T deep_lock; | 43 | MUTEX_T deep_lock; |
| 46 | MUTEX_T mtid_lock; | 44 | MUTEX_T mtid_lock; |
| 47 | 45 | ||
| @@ -600,7 +598,7 @@ uint_t get_mt_id( lua_State *L, int i ) { | |||
| 600 | // [-2]: reg[REG_MTID] | 598 | // [-2]: reg[REG_MTID] |
| 601 | // [-1]: nil/uint | 599 | // [-1]: nil/uint |
| 602 | 600 | ||
| 603 | id= lua_tointeger(L,-1); // 0 for nil | 601 | id= (uint_t)lua_tointeger(L,-1); // 0 for nil |
| 604 | lua_pop(L,1); | 602 | lua_pop(L,1); |
| 605 | STACK_MID(L,1) | 603 | STACK_MID(L,1) |
| 606 | 604 | ||
| @@ -644,73 +642,60 @@ static int buf_writer( lua_State *L, const void* b, size_t n, void* B ) { | |||
| 644 | * Returns TRUE if the table was cached (no need to fill it!); FALSE if | 642 | * Returns TRUE if the table was cached (no need to fill it!); FALSE if |
| 645 | * it's a virgin. | 643 | * it's a virgin. |
| 646 | */ | 644 | */ |
| 647 | static | 645 | static bool_t push_cached_table( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) |
| 648 | bool_t push_cached_table( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) { | 646 | { |
| 649 | bool_t ret; | 647 | bool_t ret; |
| 650 | |||
| 651 | ASSERT_L( hijacked_tostring ); | ||
| 652 | ASSERT_L( L2_cache_i != 0 ); | ||
| 653 | |||
| 654 | STACK_GROW(L,2); | ||
| 655 | STACK_GROW(L2,3); | ||
| 656 | 648 | ||
| 657 | // Create an identity string for table at [i]; it should stay unique at | 649 | ASSERT_L( L2_cache_i != 0 ); |
| 658 | // least during copying of the data (then we can clear the caches). | ||
| 659 | // | ||
| 660 | STACK_CHECK(L) | ||
| 661 | lua_pushcfunction( L, hijacked_tostring ); | ||
| 662 | lua_pushvalue( L, i ); | ||
| 663 | lua_call( L, 1 /*args*/, 1 /*retvals*/ ); | ||
| 664 | // | ||
| 665 | // [-1]: "table: 0x...." | ||
| 666 | 650 | ||
| 667 | STACK_END(L,1) | 651 | STACK_GROW(L2,3); |
| 668 | ASSERT_L( lua_type(L,-1) == LUA_TSTRING ); | ||
| 669 | 652 | ||
| 670 | // L2_cache[id_str]= [{...}] | 653 | // L2_cache[id_str]= [{...}] |
| 671 | // | 654 | // |
| 672 | STACK_CHECK(L2) | 655 | STACK_CHECK(L2) |
| 673 | 656 | ||
| 674 | // We don't need to use the from state ('L') in ID since the life span | 657 | // We don't need to use the from state ('L') in ID since the life span |
| 675 | // is only for the duration of a copy (both states are locked). | 658 | // is only for the duration of a copy (both states are locked). |
| 676 | // | 659 | // |
| 677 | lua_pushstring( L2, lua_tostring(L,-1) ); | 660 | lua_pushlightuserdata( L2, (void*)lua_topointer( L, i )); // push a light userdata uniquely representing the table |
| 678 | lua_pop(L,1); // remove the 'tostring(tbl)' value (in L!) | ||
| 679 | 661 | ||
| 680 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); | 662 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); |
| 681 | 663 | ||
| 682 | lua_pushvalue( L2, -1 ); | 664 | lua_pushvalue( L2, -1 ); |
| 683 | lua_rawget( L2, L2_cache_i ); | 665 | lua_rawget( L2, L2_cache_i ); |
| 684 | // | 666 | // |
| 685 | // [-2]: identity string ("table: 0x...") | 667 | // [-2]: identity table pointer lightuserdata |
| 686 | // [-1]: table|nil | 668 | // [-1]: table|nil |
| 687 | |||
| 688 | if (lua_isnil(L2,-1)) { | ||
| 689 | lua_pop(L2,1); | ||
| 690 | lua_newtable(L2); | ||
| 691 | lua_pushvalue(L2,-1); | ||
| 692 | lua_insert(L2,-3); | ||
| 693 | // | ||
| 694 | // [-3]: new table (2nd ref) | ||
| 695 | // [-2]: identity string | ||
| 696 | // [-1]: new table | ||
| 697 | 669 | ||
| 698 | lua_rawset(L2, L2_cache_i); | 670 | if (lua_isnil(L2,-1)) |
| 699 | // | 671 | { |
| 700 | // [-1]: new table (tied to 'L2_cache' table') | 672 | lua_pop(L2,1); |
| 701 | 673 | lua_newtable(L2); | |
| 702 | ret= FALSE; // brand new | 674 | lua_pushvalue(L2,-1); |
| 703 | 675 | lua_insert(L2,-3); | |
| 704 | } else { | 676 | // |
| 705 | lua_remove(L2,-2); | 677 | // [-3]: new table (2nd ref) |
| 706 | ret= TRUE; // from cache | 678 | // [-2]: identity table pointer lightuserdata |
| 707 | } | 679 | // [-1]: new table |
| 708 | STACK_END(L2,1) | ||
| 709 | // | ||
| 710 | // L2 [-1]: table to use as destination | ||
| 711 | 680 | ||
| 712 | ASSERT_L( lua_istable(L2,-1) ); | 681 | lua_rawset(L2, L2_cache_i); |
| 713 | return ret; | 682 | // |
| 683 | // [-1]: new table (tied to 'L2_cache' table') | ||
| 684 | |||
| 685 | ret= FALSE; // brand new | ||
| 686 | |||
| 687 | } | ||
| 688 | else | ||
| 689 | { | ||
| 690 | lua_remove(L2,-2); | ||
| 691 | ret= TRUE; // from cache | ||
| 692 | } | ||
| 693 | STACK_END(L2,1) | ||
| 694 | // | ||
| 695 | // L2 [-1]: table to use as destination | ||
| 696 | |||
| 697 | ASSERT_L( lua_istable(L2,-1) ); | ||
| 698 | return ret; | ||
| 714 | } | 699 | } |
| 715 | 700 | ||
| 716 | 701 | ||
| @@ -722,82 +707,76 @@ bool_t push_cached_table( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t | |||
| 722 | */ | 707 | */ |
| 723 | static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ); | 708 | static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ); |
| 724 | 709 | ||
| 725 | static | 710 | static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) |
| 726 | void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) { | 711 | { |
| 727 | // TBD: Merge this and same code for tables | 712 | // TBD: Merge this and same code for tables |
| 728 | 713 | ||
| 729 | ASSERT_L( hijacked_tostring ); | 714 | ASSERT_L( L2_cache_i != 0 ); |
| 730 | ASSERT_L( L2_cache_i != 0 ); | ||
| 731 | 715 | ||
| 732 | STACK_GROW(L,2); | 716 | STACK_GROW(L2,3); |
| 733 | STACK_GROW(L2,3); | ||
| 734 | 717 | ||
| 735 | STACK_CHECK(L) | 718 | // L2_cache[id_str]= function |
| 736 | lua_pushcfunction( L, hijacked_tostring ); | 719 | // |
| 737 | lua_pushvalue( L, i ); | 720 | STACK_CHECK(L2) |
| 738 | lua_call( L, 1 /*args*/, 1 /*retvals*/ ); | ||
| 739 | // | ||
| 740 | // [-1]: "function: 0x...." | ||
| 741 | 721 | ||
| 742 | STACK_END(L,1) | 722 | // We don't need to use the from state ('L') in ID since the life span |
| 743 | ASSERT_L( lua_type(L,-1) == LUA_TSTRING ); | 723 | // is only for the duration of a copy (both states are locked). |
| 744 | 724 | // | |
| 745 | // L2_cache[id_str]= function | 725 | lua_pushlightuserdata( L2, (void*)lua_topointer( L, i )); // push a light userdata uniquely representing the function |
| 746 | // | ||
| 747 | STACK_CHECK(L2) | ||
| 748 | 726 | ||
| 749 | // We don't need to use the from state ('L') in ID since the life span | 727 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); |
| 750 | // is only for the duration of a copy (both states are locked). | ||
| 751 | // | ||
| 752 | lua_pushstring( L2, lua_tostring(L,-1) ); | ||
| 753 | lua_pop(L,1); // remove the 'tostring(tbl)' value (in L!) | ||
| 754 | 728 | ||
| 755 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); | 729 | lua_pushvalue( L2, -1 ); |
| 730 | lua_rawget( L2, L2_cache_i ); | ||
| 731 | // | ||
| 732 | // [-2]: identity lightuserdata function pointer | ||
| 733 | // [-1]: function|nil|true (true means: we're working on it; recursive) | ||
| 756 | 734 | ||
| 757 | lua_pushvalue( L2, -1 ); | 735 | if (lua_isnil(L2,-1)) |
| 758 | lua_rawget( L2, L2_cache_i ); | 736 | { |
| 759 | // | 737 | lua_pop(L2,1); |
| 760 | // [-2]: identity string ("function: 0x...") | ||
| 761 | // [-1]: function|nil|true (true means: we're working on it; recursive) | ||
| 762 | 738 | ||
| 763 | if (lua_isnil(L2,-1)) { | 739 | // Set to 'true' for the duration of creation; need to find self-references |
| 764 | lua_pop(L2,1); | 740 | // via upvalues |
| 765 | 741 | // | |
| 766 | // Set to 'true' for the duration of creation; need to find self-references | 742 | lua_pushvalue( L2, -1); |
| 767 | // via upvalues | 743 | lua_pushboolean(L2,TRUE); |
| 768 | // | 744 | lua_rawset( L2, L2_cache_i); |
| 769 | lua_pushboolean(L2,TRUE); | ||
| 770 | lua_setfield( L2, L2_cache_i, lua_tostring(L2,-2) ); | ||
| 771 | |||
| 772 | inter_copy_func( L2, L2_cache_i, L, i ); // pushes a copy of the func | ||
| 773 | 745 | ||
| 774 | lua_pushvalue(L2,-1); | 746 | inter_copy_func( L2, L2_cache_i, L, i ); // pushes a copy of the func |
| 775 | lua_insert(L2,-3); | ||
| 776 | // | ||
| 777 | // [-3]: function (2nd ref) | ||
| 778 | // [-2]: identity string | ||
| 779 | // [-1]: function | ||
| 780 | 747 | ||
| 781 | lua_rawset(L2,L2_cache_i); | 748 | lua_pushvalue(L2,-1); |
| 782 | // | 749 | lua_insert(L2,-3); |
| 783 | // [-1]: function (tied to 'L2_cache' table') | 750 | // |
| 784 | 751 | // [-3]: function (2nd ref) | |
| 785 | } else if (lua_isboolean(L2,-1)) { | 752 | // [-2]: identity lightuserdata function pointer |
| 786 | // Loop in preparing upvalues; either direct or via a table | 753 | // [-1]: function |
| 787 | // | ||
| 788 | // Note: This excludes the case where a function directly addresses | ||
| 789 | // itself as an upvalue (recursive lane creation). | ||
| 790 | // | ||
| 791 | luaL_error( L, "Recursive use of upvalues; cannot copy the function" ); | ||
| 792 | |||
| 793 | } else { | ||
| 794 | lua_remove(L2,-2); | ||
| 795 | } | ||
| 796 | STACK_END(L2,1) | ||
| 797 | // | ||
| 798 | // L2 [-1]: function | ||
| 799 | 754 | ||
| 800 | ASSERT_L( lua_isfunction(L2,-1) ); | 755 | lua_rawset(L2,L2_cache_i); |
| 756 | // | ||
| 757 | // [-1]: function (tied to 'L2_cache' table') | ||
| 758 | |||
| 759 | } | ||
| 760 | else if (lua_isboolean(L2,-1)) | ||
| 761 | { | ||
| 762 | // Loop in preparing upvalues; either direct or via a table | ||
| 763 | // | ||
| 764 | // Note: This excludes the case where a function directly addresses | ||
| 765 | // itself as an upvalue (recursive lane creation). | ||
| 766 | // | ||
| 767 | STACK_GROW(L,1); | ||
| 768 | luaL_error( L, "Recursive use of upvalues; cannot copy the function" ); | ||
| 769 | |||
| 770 | } | ||
| 771 | else | ||
| 772 | { | ||
| 773 | lua_remove(L2,-2); | ||
| 774 | } | ||
| 775 | STACK_END(L2,1) | ||
| 776 | // | ||
| 777 | // L2 [-1]: function | ||
| 778 | |||
| 779 | ASSERT_L( lua_isfunction(L2,-1) ); | ||
| 801 | } | 780 | } |
| 802 | 781 | ||
| 803 | 782 | ||
| @@ -1137,29 +1116,6 @@ void luaG_inter_copy( lua_State* L, lua_State *L2, uint_t n ) | |||
| 1137 | uint_t top_L2= lua_gettop(L2); | 1116 | uint_t top_L2= lua_gettop(L2); |
| 1138 | uint_t i; | 1117 | uint_t i; |
| 1139 | 1118 | ||
| 1140 | /* steal Lua library's 'luaB_tostring()' from the first call. Other calls | ||
| 1141 | * don't have to have access to it. | ||
| 1142 | * | ||
| 1143 | * Note: multiple threads won't come here at once; this function will | ||
| 1144 | * be called before there can be multiple threads (no locking needed). | ||
| 1145 | */ | ||
| 1146 | if (!hijacked_tostring) { | ||
| 1147 | STACK_GROW( L,1 ); | ||
| 1148 | |||
| 1149 | STACK_CHECK(L) | ||
| 1150 | lua_getglobal( L, "tostring" ); | ||
| 1151 | // | ||
| 1152 | // [-1]: function|nil | ||
| 1153 | |||
| 1154 | hijacked_tostring= lua_tocfunction( L, -1 ); | ||
| 1155 | lua_pop(L,1); | ||
| 1156 | STACK_END(L,0) | ||
| 1157 | |||
| 1158 | if (!hijacked_tostring) { | ||
| 1159 | luaL_error( L, "Need to see 'tostring()' once" ); | ||
| 1160 | } | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | if (n > top_L) | 1119 | if (n > top_L) |
| 1164 | luaL_error( L, "Not enough values: %d < %d", top_L, n ); | 1120 | luaL_error( L, "Not enough values: %d < %d", top_L, n ); |
| 1165 | 1121 | ||
diff --git a/src/tools.h b/src/tools.h index d155c65..aad26df 100644 --- a/src/tools.h +++ b/src/tools.h | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #define STACK_END(L,c) /*nothing*/ | 22 | #define STACK_END(L,c) /*nothing*/ |
| 23 | #define STACK_DUMP(L) /*nothing*/ | 23 | #define STACK_DUMP(L) /*nothing*/ |
| 24 | #define DEBUG() /*nothing*/ | 24 | #define DEBUG() /*nothing*/ |
| 25 | #define DEBUGEXEC(_code) {} /*nothing*/ | ||
| 25 | #else | 26 | #else |
| 26 | #define _ASSERT_L(lua,c) { if (!(c)) luaL_error( lua, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #c ); } | 27 | #define _ASSERT_L(lua,c) { if (!(c)) luaL_error( lua, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #c ); } |
| 27 | // | 28 | // |
| @@ -32,6 +33,7 @@ | |||
| 32 | 33 | ||
| 33 | #define STACK_DUMP(L) luaG_dump(L); | 34 | #define STACK_DUMP(L) luaG_dump(L); |
| 34 | #define DEBUG() fprintf( stderr, "<<%s %d>>\n", __FILE__, __LINE__ ); | 35 | #define DEBUG() fprintf( stderr, "<<%s %d>>\n", __FILE__, __LINE__ ); |
| 36 | #define DEBUGEXEC(_code) {_code;} /*nothing*/ | ||
| 35 | #endif | 37 | #endif |
| 36 | #define ASSERT_L(c) _ASSERT_L(L,c) | 38 | #define ASSERT_L(c) _ASSERT_L(L,c) |
| 37 | 39 | ||
