diff options
author | Benoit Germain <bnt.germain@gmail.com> | 2011-01-04 21:31:17 +0100 |
---|---|---|
committer | Benoit Germain <bnt.germain@gmail.com> | 2011-01-04 21:31:17 +0100 |
commit | 79e46938c5d8daf164ab2d934f668fa27b32e4cf (patch) | |
tree | 407761f25bbdc3d5b2066a705dcbcf8711690242 /src | |
parent | ed07b457b6b45ece85d367dc8b89bf3c040abd9a (diff) | |
download | lanes-79e46938c5d8daf164ab2d934f668fa27b32e4cf.tar.gz lanes-79e46938c5d8daf164ab2d934f668fa27b32e4cf.tar.bz2 lanes-79e46938c5d8daf164ab2d934f668fa27b32e4cf.zip |
Take all code from Asko Kauppi's SVN server, and push it here so that the github repository becomes the official Lanes source codebase.
Note that Asko's SVN server holds version 2.0.9, whereas this is version 2.0.10, but I don't see any real need to update SVN if it is to become deprecated.
Next steps:
- upgrade the rockspec to the latest version
- make the html help available online somewhere
Signed-off-by: Benoit Germain <bnt.germain@gmail.com>
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 | ||