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 | ||