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/lanes.c | |
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/lanes.c')
-rw-r--r-- | src/lanes.c | 732 |
1 files changed, 459 insertions, 273 deletions
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 ); |