aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenoit Germain <bnt.germain@gmail.com>2011-01-04 21:31:17 +0100
committerBenoit Germain <bnt.germain@gmail.com>2011-01-04 21:31:17 +0100
commit79e46938c5d8daf164ab2d934f668fa27b32e4cf (patch)
tree407761f25bbdc3d5b2066a705dcbcf8711690242 /src
parented07b457b6b45ece85d367dc8b89bf3c040abd9a (diff)
downloadlanes-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.lua4
-rw-r--r--src/lanes.c732
-rw-r--r--src/lanes.lua33
-rw-r--r--src/threading.c9
-rw-r--r--src/tools.c260
-rw-r--r--src/tools.h2
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
14Copyright (C) 2008 Asko Kauppi <akauppi@gmail.com> 14Copyright (C) 2008-10 Asko Kauppi <akauppi@gmail.com>
15 15
16Permission is hereby granted, free of charge, to any person obtaining a copy 16Permission is hereby granted, free of charge, to any person obtaining a copy
17of this software and associated documentation files (the "Software"), to deal 17of 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
60const char *VERSION= "2.0.3"; 67const char *VERSION= "2.0.10";
61 68
62/* 69/*
63=============================================================================== 70===============================================================================
64 71
65Copyright (C) 2007-08 Asko Kauppi <akauppi@gmail.com> 72Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com>
66 73
67Permission is hereby granted, free of charge, to any person obtaining a copy 74Permission is hereby granted, free of charge, to any person obtaining a copy
68of this software and associated documentation files (the "Software"), to deal 75of 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.
127static char keeper_chunk[]= 135static char keeper_chunk[]=
128#include "keeper.lch" 136#include "keeper.lch"
129 137
130struct s_lane; 138// NOTE: values to be changed by either thread, during execution, without
139// locking, are marked "volatile"
140//
141struct 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
131static bool_t cancel_test( lua_State *L ); 191static bool_t cancel_test( lua_State *L );
132static void cancel_error( lua_State *L ); 192static 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 }
458STACK_END(KL,0) 547STACK_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 ) {
535LUAG_FUNC( linda_set ) { 652LUAG_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 ) {
561LUAG_FUNC( linda_get ) { 681LUAG_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*/
581LUAG_FUNC( linda_limit ) { 704LUAG_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*/
605LUAG_FUNC( linda_deep ) { 731LUAG_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//
811struct 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
859static MUTEX_T selfdestruct_cs; 935static 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)
1247typedef 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
1256void 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//
1291LUAG_FUNC( thread_new ) 1415LUAG_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)
1323STACK_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
1336STACK_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 {
1350STACK_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
1353STACK_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 {
1361STACK_MID(L,0) 1485 lua_pushvalue( L, 1 );
1362 1486 luaG_inter_move( L,L2, 1 ); // L->L2
1363ASSERT_L( (uint_t)lua_gettop(L2) == 1+args ); 1487 STACK_MID(L,0)
1364ASSERT_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 );
1391STACK_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
1405STACK_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//
1431LUAG_FUNC( thread_gc ) { 1578LUAG_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
1451fprintf( 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
1457fprintf( 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*/
1637static
1638void 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/*
1748int 1865* One-time initializations
1749#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 1866*/
1750__declspec(dllexport) 1867static 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
1946int
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
18Copyright (C) 2007-08 Asko Kauppi <akauppi@gmail.com> 19Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com>
19 20
20Permission is hereby granted, free of charge, to any person obtaining a copy 21Permission is hereby granted, free of charge, to any person obtaining a copy
21of this software and associated documentation files (the "Software"), to deal 22of 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--
126local lane_mt= { 135local 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
13Copyright (C) 2007-08 Asko Kauppi <akauppi@gmail.com> 13Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com>
14 14
15Permission is hereby granted, free of charge, to any person obtaining a copy 15Permission is hereby granted, free of charge, to any person obtaining a copy
16of this software and associated documentation files (the "Software"), to deal 16of 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
10Copyright (C) 2002-08 Asko Kauppi <akauppi@gmail.com> 10Copyright (C) 2002-10 Asko Kauppi <akauppi@gmail.com>
11 11
12Permission is hereby granted, free of charge, to any person obtaining a copy 12Permission is hereby granted, free of charge, to any person obtaining a copy
13of this software and associated documentation files (the "Software"), to deal 13of 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
43static volatile lua_CFunction hijacked_tostring; // = NULL
44
45MUTEX_T deep_lock; 43MUTEX_T deep_lock;
46MUTEX_T mtid_lock; 44MUTEX_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 */
647static 645static bool_t push_cached_table( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i )
648bool_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 */
723static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ); 708static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i );
724 709
725static 710static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i )
726void 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