aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ABOUT4
-rw-r--r--CHANGES38
-rw-r--r--COPYRIGHT1
-rw-r--r--Makefile2
-rw-r--r--docs/index.html117
-rw-r--r--src/keeper.c112
-rw-r--r--src/keeper.h1
-rw-r--r--src/lanes.c311
-rw-r--r--src/lanes.lua79
-rw-r--r--src/threading.c1
-rw-r--r--src/tools.c649
-rw-r--r--src/tools.h9
-rw-r--r--tests/appendud.lua3
-rw-r--r--tests/atexit.lua3
-rw-r--r--tests/atomic.lua3
-rw-r--r--tests/basic.lua3
-rw-r--r--tests/cyclic.lua3
-rw-r--r--tests/ehynes.lua3
-rw-r--r--tests/errhangtest.lua3
-rw-r--r--tests/error.lua3
-rw-r--r--tests/fibonacci.lua24
-rw-r--r--tests/fifo.lua11
-rw-r--r--tests/finalizer.lua3
-rw-r--r--tests/func_is_string.lua3
-rw-r--r--tests/hangtest.lua3
-rw-r--r--tests/irayo_closure.lua5
-rw-r--r--tests/irayo_recursive.lua3
-rw-r--r--tests/keeper.lua3
-rw-r--r--tests/launchtest.lua3
-rw-r--r--tests/linda_perf.lua3
-rw-r--r--tests/objects.lua3
-rw-r--r--tests/perftest.lua3
-rw-r--r--tests/protectproxy.lua3
-rw-r--r--tests/recursive.lua4
-rw-r--r--tests/require.lua3
-rw-r--r--tests/timer.lua3
36 files changed, 1092 insertions, 336 deletions
diff --git a/ABOUT b/ABOUT
index 81cf640..260fb5c 100644
--- a/ABOUT
+++ b/ABOUT
@@ -14,4 +14,6 @@ in the manual).
14 14
15Lua Lanes has been optimized for performance, and provides around 50-60% 15Lua Lanes has been optimized for performance, and provides around 50-60%
16speed increase when running heavily threaded applications on dual core 16speed increase when running heavily threaded applications on dual core
17processors (compared to running a non-threaded plain Lua implementation). 17processors (compared to running a non-threaded plain Lua implementation).
18
19Starting with version 3.0, Lanes is compatible with LuaJIT 2. \ No newline at end of file
diff --git a/CHANGES b/CHANGES
index f7ab99f..c2f3b81 100644
--- a/CHANGES
+++ b/CHANGES
@@ -3,21 +3,39 @@ CHANGES:
3 3
4CHANGE X: 4CHANGE X:
5 5
6CHANGE 33 BGe 5-Nov-2011: Lanes version 3.0-beta
7 * process exit change: close everything at GC when main state closes, not when atexit() handlers are processed
8 * Lua 5.2-style module:
9 * module() is no longer used to implement lanes.lua
10 * a global "lanes" variable is no longer created when the module is required
11 * the Lanes module table is returned instead
12 * Lanes must be initialized before used:
13 * the first occurence of 'require "lanes"' produces a minimal interface that only contains a configure() function
14 * the remainder of the interface is made available once this function is called
15 * subsequent calls to configure() do nothing
16 * configure() controls the number of keeper states and the startup of timers
17 * LuaJIT 2 compatibility
18 * non-Lua functions are no longer copied by creating a C closure from a C pointer, but through 2-way lookup tables
19 * this means that if a lane function body pulls non-Lua functions, the lane generator description must contain the list of libraries and modules that exports them
20 * introduces a change in configuration .globals management: contents are copied *after* std libs are loaded
21 * new .required configuration entry to list modules that must be require()'ed before lane body is transferred
22 * lane:cancel() wakes up waiting lindas like what is done at lane shutdown
23
6CHANGE 32 BGe 14-May-2011 24CHANGE 32 BGe 14-May-2011
7* raise an error when linda:send() has nothing to send 25 * raise an error when linda:send() has nothing to send
8 26
9CHANGE 31 BGe 17-Apr-2011 27CHANGE 31 BGe 17-Apr-2011
10* linda uses a fast FIFO implementation to speed up data exchanges 28 * linda uses a fast FIFO implementation to speed up data exchanges
11* new linda:count() method 29 * new linda:count() method
12* new linda batched data read mode 30 * new linda batched data read mode
13* proper key type check in all linda methods 31 * proper key type check in all linda methods
14* fix setup-vc.cmd to support Visual Studio 2010 and Windows 7 64 bits 32 * fix setup-vc.cmd to support Visual Studio 2010 and Windows 7 64 bits
15* bugfix: release keeper state mutex at desinit 33 * bugfix: release keeper state mutex at desinit
16 34
17CHANGE 30 BGe 30-Mar-2011 35CHANGE 30 BGe 30-Mar-2011
18* linda honors __tostring and __concat 36 * linda honors __tostring and __concat
19* new accessor linda:keys(), to retrieve the list of keys with pending data inside a linda 37 * new accessor linda:keys(), to retrieve the list of keys with pending data inside a linda
20* new lanes options packagepath and packagecpath, in case one needs to set them differently than the default 38 * new lanes options packagepath and packagecpath, in case one needs to set them differently than the default
21 39
22CHANGE 29 BGe 1-Mar-2011 40CHANGE 29 BGe 1-Mar-2011
23 fixed potential crash at application shutdown when calling lua_close() on a killed thread's VM. 41 fixed potential crash at application shutdown when calling lua_close() on a killed thread's VM.
diff --git a/COPYRIGHT b/COPYRIGHT
index d6b0008..2fdb982 100644
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -7,6 +7,7 @@ For details and rationale, see http://www.lua.org/license.html
7=============================================================================== 7===============================================================================
8 8
9Copyright (C) 2007-11 Asko Kauppi, <akauppi@gmail.com> 9Copyright (C) 2007-11 Asko Kauppi, <akauppi@gmail.com>
10Copyright (C) 2010-11 Benoit Germain, <bnt.germain@gmail.com>
10 11
11Permission 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
12of this software and associated documentation files (the "Software"), to deal 13of this software and associated documentation files (the "Software"), to deal
diff --git a/Makefile b/Makefile
index 23bec03..3f1f0e1 100644
--- a/Makefile
+++ b/Makefile
@@ -36,7 +36,7 @@ else
36 LUAC=$(word 1,$(shell which luac5.1) $(shell which luac51) luac) 36 LUAC=$(word 1,$(shell which luac5.1) $(shell which luac51) luac)
37endif 37endif
38 38
39_PREFIX=LUA_CPATH=./src/?$(_SO) LUA_PATH="src/?.lua;./tests/?.lua" 39_PREFIX=LUA_CPATH="./src/?$(_SO)" LUA_PATH="./src/?.lua;./tests/?.lua"
40 40
41#--- 41#---
42all: $(_TARGET_SO) 42all: $(_TARGET_SO)
diff --git a/docs/index.html b/docs/index.html
index 3c4fe59..3c9abfe 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -54,9 +54,9 @@
54 <a href="#changes">Change log</a> 54 <a href="#changes">Change log</a>
55 <!-- ... --> 55 <!-- ... -->
56 56
57<p><br/><font size="-1"><i>Copyright &copy; 2007-11 Asko Kauppi. All rights reserved.</i> 57<p><br/><font size="-1"><i>Copyright &copy; 2007-12 Asko Kauppi, Benoit Germain. All rights reserved.</i>
58 <br>Lua Lanes is published under the same <A HREF="http://en.wikipedia.org/wiki/MIT_License">MIT license</A> as Lua 5.1. 58 <br>Lua Lanes is published under the same <A HREF="http://en.wikipedia.org/wiki/MIT_License">MIT license</A> as Lua 5.1.
59 </p><p>This document was revised on 1-Mar-11, and applies to version 2.1.0. 59 </p><p>This document was revised on 5-Nov-11, and applies to version 3.0-beta.
60</font></p> 60</font></p>
61 61
62</center> 62</center>
@@ -162,7 +162,51 @@ Or use <A HREF="http://www.luarocks.org" TARGET="_blank">Lua Rocks</A> package m
162 162
163<!-- launching +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 163<!-- launching +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
164<hr/> 164<hr/>
165<h2 id="creation">Creation</h2> 165
166 <h2 id="initialization">Initialization</h2>
167
168 <p>
169 The following sample shows how to initialize the Lanes module.
170 </p>
171
172 <table border="1" bgcolor="#FFFFE0" width="500">
173 <tr>
174 <td>
175 <pre>
176 local lanes = require "lanes"
177 lanes.configure( 1)
178 </pre>
179 </table>
180
181 <p>
182 Starting with version 3.0-beta, requiring the module follows Lua 5.2 rules:
183 the module is not available under the global name "lanes", but has to be accessed
184 through require's return value.
185 After lanes is required, it is necessary to call lanes.configure(), which is the
186 only function exposed by the module at this point. Calling configure() will
187 perform one-time initializations and make the rest of the API available.
188 At the same time, configure() itself will be replaced by another function that
189 raises an error if called with differing arguments.
190 </p>
191
192 <p>
193 <table border="1" bgcolor="#E0E0FF" cellpadding="10">
194 <tr>
195 <td>
196 <code>
197 lanes.configure( [nb_keepers] [, "NO_TIMERS"])
198 </code>
199 </table>
200 </p>
201 <p>
202 <tt>lanes.configure</tt> accepts 2 arguments. The first one controls the number
203 of keeper states used internally by lindas to transfer data between lanes. (see below).
204 Default is 1.
205 </p>
206 <p>If the second argument is equal to <tt>"NO_TIMERS"</tt>, Lanes doesn't start the timer service,
207 and the associated API will be absent from the interface (see below).
208 </p>
209 <h2 id="creation">Creation</h2>
166 210
167<p>The following sample shows preparing a function for parallel calling, and 211<p>The following sample shows preparing a function for parallel calling, and
168calling it with varying arguments. Each of the two results is calculated in 212calling it with varying arguments. Each of the two results is calculated in
@@ -172,7 +216,8 @@ joins the threads, waiting for any results not already there.
172 216
173<table border=1 bgcolor="#FFFFE0" width=500><tr><td> 217<table border=1 bgcolor="#FFFFE0" width=500><tr><td>
174<pre> 218<pre>
175 require "lanes" 219 local lanes = require "lanes"
220 lanes.configure( 1)
176 221
177 f= lanes.gen( function(n) return 2*n end ) 222 f= lanes.gen( function(n) return 2*n end )
178 a= f(1) 223 a= f(1)
@@ -189,7 +234,7 @@ joins the threads, waiting for any results not already there.
189 lane_h= func( ... )</code> 234 lane_h= func( ... )</code>
190</table> 235</table>
191</p> 236</p>
192</p><p> 237<p>
193 The function returned by <tt>lanes.gen()</tt> is a "generator" for 238 The function returned by <tt>lanes.gen()</tt> is a "generator" for
194 launching any number of lanes. They will share code, options, initial globals, 239 launching any number of lanes. They will share code, options, initial globals,
195 but the particular arguments may vary. Only calling the generator function 240 but the particular arguments may vary. Only calling the generator function
@@ -256,6 +301,23 @@ also in the new lanes.
256 modifying one will only affect the particular lane. 301 modifying one will only affect the particular lane.
257 </td></tr> 302 </td></tr>
258 303
304 <tr valign="top">
305 <td/>
306 <td>
307 <code>.required</code> <br/>modules_tbl
308 </td>
309 <td>
310 Lists modules that have to be required in order to be able to trasnfer
311 functions they exposed. Starting with Lanes 3.0-beta, non-Lua functions are
312 no longer copied by recreating a C closure from a C pointer, but are searched
313 in lookup tables. These tables are built from the modules listed here. <tt>required</tt>
314 must be a list of strings, each one being the name of a module to be required.
315 <br>
316 The global values of different lanes are in no manner connected;
317 modifying one will only affect the particular lane.
318 </td>
319 </tr>
320
259 <tr valign=top><td width=40><td> 321 <tr valign=top><td width=40><td>
260 <code>.priority</code> <br/><nobr>-2..+2</nobr></td> 322 <code>.priority</code> <br/><nobr>-2..+2</nobr></td>
261 <td>The priority of lanes generated. -2 is lowest, +2 is highest. 323 <td>The priority of lanes generated. -2 is lowest, +2 is highest.
@@ -273,6 +335,7 @@ also in the new lanes.
273 <td> 335 <td>
274 <code>package.path</code> and <code>package.cpath</code> overrides, if needed. 336 <code>package.path</code> and <code>package.cpath</code> overrides, if needed.
275 Specifying these when <code>libs_str</code> doesn't cause the <code>package</code> library to be loaded will generate an error. 337 Specifying these when <code>libs_str</code> doesn't cause the <code>package</code> library to be loaded will generate an error.
338 If not specified, the created lane will receive the current values of <tt>package.path</tt> and <tt>package.cpath</tt>.
276 <br> 339 <br>
277 </td> 340 </td>
278 </tr> 341 </tr>
@@ -407,8 +470,9 @@ OS thread running the lane is forcefully killed. This means no GC, and should
407generally be the last resort. 470generally be the last resort.
408</p> 471</p>
409<p>Cancellation is tested <u>before</u> going to sleep in <tt>receive()</tt> or <tt>send()</tt> calls 472<p>Cancellation is tested <u>before</u> going to sleep in <tt>receive()</tt> or <tt>send()</tt> calls
410and after executing <tt>cancelstep</tt> Lua statements. A currently pending <tt>receive()</tt> 473and after executing <tt>cancelstep</tt> Lua statements. Starting with version 3.0-beta, a pending <tt>receive()</tt>
411or <tt>send()</tt> call is currently not awakened, and may be a reason for a non-detected cancel. 474or <tt>send()</tt> call is awakened. This means the execution of the lane will resume although the operation has
475not completed, to give the lane a chance to detect cancellation. The code should be able to handle this situation appropriately if required.
412It is also possible to manually test for cancel requests with <tt>cancel_test()</tt>. 476It is also possible to manually test for cancel requests with <tt>cancel_test()</tt>.
413</p> 477</p>
414 478
@@ -619,6 +683,8 @@ you want to use several?
619 <li>Performance. Changing any slot in a Linda causes all pending threads 683 <li>Performance. Changing any slot in a Linda causes all pending threads
620 for that Linda to be momentarily awakened (at least in the C level). 684 for that Linda to be momentarily awakened (at least in the C level).
621 This can degrade performance due to unnecessary OS level context switches. 685 This can degrade performance due to unnecessary OS level context switches.
686 The more keeper states you declared with <tt>lanes.configure()</tt> the less
687 this should be a problem.
622 </li> 688 </li>
623</ul> 689</ul>
624 690
@@ -639,6 +705,10 @@ events to a common Linda, but... :).</font>
639 <code>= lanes.timer( linda_h, key, date_tbl|first_secs [,period_secs] )</code> 705 <code>= lanes.timer( linda_h, key, date_tbl|first_secs [,period_secs] )</code>
640</table> 706</table>
641 707
708 <p>
709 Timers are implemented as a lane. They can be disabled by passing <tt>"NO_TIMERS"</tt>
710 to <tt>lanes.configure()</tt>.
711 </p>
642<p> 712<p>
643Timers can be run once, or in a reoccurring fashion (<tt>period_secs > 0</tt>). 713Timers can be run once, or in a reoccurring fashion (<tt>period_secs > 0</tt>).
644The first occurrence can be given either as a date or as a relative delay in seconds. 714The first occurrence can be given either as a date or as a relative delay in seconds.
@@ -655,7 +725,8 @@ A timer can be stopped simply by <tt>first_secs=0</tt> and no period.
655 725
656<table border=1 bgcolor="#FFFFE0" width=500><tr><td> 726<table border=1 bgcolor="#FFFFE0" width=500><tr><td>
657<pre> 727<pre>
658 require "lanes" 728 local lanes = require "lanes"
729 lanes.configure( 1)
659 730
660 local linda= lanes.linda() 731 local linda= lanes.linda()
661 732
@@ -991,11 +1062,37 @@ its actual value.
991 1062
992<p> 1063<p>
993 1064
1065 Nov-2011
1066 <ul>
1067 <li>process exit change: close everything at GC when main state closes, not when atexit() handlers are processed</li>
1068 <li>Lua 5.2-style module:</li>
1069 <ul>
1070 <li>module() is no longer used to implement lanes.lua</li>
1071 <li>a global "lanes" variable is no longer created when the module is required</li>
1072 <li>the Lanes module table is returned instead</li>
1073 </ul>
1074 <li>Lanes must be initialized before used:</li>
1075 <ul>
1076 <li>the first occurence of 'require "lanes"' produces a minimal interface that only contains a configure() function</li>
1077 <li>the remainder of the interface is made available once this function is called</li>
1078 <li>subsequent calls to configure() do nothing</li>
1079 <li>configure() controls the number of keeper states and the startup of timers</li>
1080 </ul>
1081 <li>* LuaJIT 2 compatibility</li>
1082 <ul>
1083 <li>non-Lua functions are no longer copied by creating a C closure from a C pointer, but through 2-way lookup tables</li>
1084 <li>this means that if a lane function body pulls non-Lua functions, the lane generator description must contain the list of libraries and modules that exports them</li>
1085 <li>introduces a change in configuration .globals management: contents are copied *after* std libs are loaded</li>
1086 <li>new .required configuration entry to list modules that must be require()'ed before lane body is transferred</li>
1087 </ul>
1088 <li>lane:cancel() wakes up waiting lindas like what is done at lane shutdown</li>
1089 </ul>
1090
994 Mar-2011 (not yet versioned) 1091 Mar-2011 (not yet versioned)
995 <ul> 1092 <ul>
996 <li>linda honors __tostring and __concat</li> 1093 <li>linda honors __tostring and __concat</li>
997 <li>new accessor linda:keys(), to retrieve the list of keys with pending data inside a linda</li> 1094 <li>new accessor linda:count(), to get info about data stored inside a linda.</li>
998 <li>new lanes options packagepath and packagecpath, in case one needs to set them differently than the default</li> 1095 <li>new lanes options packagepath and packagecpath, in case one needs to set them differently than the default.</li>
999 </ul> 1096 </ul>
1000 1097
1001 Mar-2011 (2.1.0) 1098 Mar-2011 (2.1.0)
diff --git a/src/keeper.c b/src/keeper.c
index 5b355cb..1f69d40 100644
--- a/src/keeper.c
+++ b/src/keeper.c
@@ -90,61 +90,119 @@ char const *init_keepers( int const _nbKeepers)
90 // Initialize Keeper states with bare minimum of libs (those required 90 // Initialize Keeper states with bare minimum of libs (those required
91 // by 'keeper.lua') 91 // by 'keeper.lua')
92 // 92 //
93 lua_State *L= luaL_newstate(); 93 lua_State *K = luaL_newstate();
94 if (!L) 94 if (!K)
95 return "out of memory"; 95 return "out of memory";
96 96
97 // to see VM name in Decoda debugger 97 // to see VM name in Decoda debugger
98 lua_pushliteral( L, "Keeper #"); 98 lua_pushliteral( K, "Keeper #");
99 lua_pushinteger( L, i + 1); 99 lua_pushinteger( K, i + 1);
100 lua_concat( L, 2); 100 lua_concat( K, 2);
101 lua_setglobal( L, "decoda_name"); 101 lua_setglobal( K, "decoda_name");
102
103 luaG_openlibs( L, "io,table,package" ); // 'io' for debugging messages, package because we need to require modules exporting idfuncs
104 serialize_require( L);
105 102
103 // 'io' for debugging messages, 'package' because we need to require modules exporting idfuncs
104 // the others because they export functions that we may store in a keeper for transfer between lanes
105 luaG_openlibs( K, "*");
106 serialize_require( K);
106 107
107 // Read in the preloaded chunk (and run it) 108 // Read in the preloaded chunk (and run it)
108 // 109 //
109 if (luaL_loadbuffer( L, keeper_chunk, sizeof(keeper_chunk), "@keeper.lua")) 110 if( luaL_loadbuffer( K, keeper_chunk, sizeof(keeper_chunk), "@keeper.lua"))
110 return "luaL_loadbuffer() failed"; // LUA_ERRMEM 111 return "luaL_loadbuffer() failed"; // LUA_ERRMEM
111 112
112 if (lua_pcall( L, 0 /*args*/, 0 /*results*/, 0 /*errfunc*/ )) 113 if( lua_pcall( K, 0 /*args*/, 0 /*results*/, 0 /*errfunc*/))
113 { 114 {
114 // LUA_ERRRUN / LUA_ERRMEM / LUA_ERRERR 115 // LUA_ERRRUN / LUA_ERRMEM / LUA_ERRERR
115 // 116 //
116 const char *err= lua_tostring(L,-1); 117 const char *err = lua_tostring(K, -1);
117 assert(err); 118 assert( err);
118 return err; 119 return err;
119 } 120 }
120 121
121 MUTEX_INIT( &GKeepers[i].lock_ ); 122 MUTEX_INIT( &GKeepers[i].lock_);
122 GKeepers[i].L= L; 123 GKeepers[i].L = K;
123 //GKeepers[i].count = 0; 124 //GKeepers[i].count = 0;
124 } 125 }
125 return NULL; // ok 126 return NULL; // ok
126} 127}
127 128
129// cause each keeper state to populate its database of transferable functions with those from the specified module
130void populate_keepers( lua_State *L)
131{
132 size_t name_len;
133 char const *name = luaL_checklstring( L, -1, &name_len);
134 size_t package_path_len;
135 char const *package_path;
136 size_t package_cpath_len;
137 char const *package_cpath;
138 int i;
139
140 // we need to make sure that package.path & package.cpath are the same in the keepers
141// than what is currently in use when the module is required in the caller's Lua state
142 STACK_CHECK(L)
143 STACK_GROW( L, 3);
144 lua_getglobal( L, "package");
145 lua_getfield( L, -1, "path");
146 package_path = luaL_checklstring( L, -1, &package_path_len);
147 lua_getfield( L, -2, "cpath");
148 package_cpath = luaL_checklstring( L, -1, &package_cpath_len);
149
150 for( i = 0; i < GNbKeepers; ++ i)
151 {
152 lua_State *K = GKeepers[i].L;
153 int res;
154 MUTEX_LOCK( &GKeepers[i].lock_);
155 STACK_CHECK(K)
156 STACK_GROW( K, 2);
157 lua_getglobal( K, "package");
158 lua_pushlstring( K, package_path, package_path_len);
159 lua_setfield( K, -2, "path");
160 lua_pushlstring( K, package_cpath, package_cpath_len);
161 lua_setfield( K, -2, "cpath");
162 lua_pop( K, 1);
163 lua_getglobal( K, "require");
164 lua_pushlstring( K, name, name_len);
165 res = lua_pcall( K, 1, 0, 0);
166 if( res != 0)
167 {
168 char const *err = luaL_checkstring( K, -1);
169 luaL_error( L, "error requiring '%s' in keeper state: %s", name, err);
170 }
171 STACK_END(K, 0)
172 MUTEX_UNLOCK( &GKeepers[i].lock_);
173 }
174 lua_pop( L, 3);
175 STACK_END(L, 0)
176}
177
128struct s_Keeper *keeper_acquire( const void *ptr) 178struct s_Keeper *keeper_acquire( const void *ptr)
129{ 179{
130 /* 180 // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers)
131 * Any hashing will do that maps pointers to 0..GNbKeepers-1 181 if( GNbKeepers == 0)
132 * consistently. 182 {
133 * 183 return NULL;
134 * Pointers are often aligned by 8 or so - ignore the low order bits 184 }
135 */ 185 else
136 unsigned int i= ((unsigned long)(ptr) >> 3) % GNbKeepers; 186 {
137 struct s_Keeper *K= &GKeepers[i]; 187 /*
188 * Any hashing will do that maps pointers to 0..GNbKeepers-1
189 * consistently.
190 *
191 * Pointers are often aligned by 8 or so - ignore the low order bits
192 */
193 unsigned int i= ((unsigned long)(ptr) >> 3) % GNbKeepers;
194 struct s_Keeper *K= &GKeepers[i];
138 195
139 MUTEX_LOCK( &K->lock_); 196 MUTEX_LOCK( &K->lock_);
140 //++ K->count; 197 //++ K->count;
141 return K; 198 return K;
199 }
142} 200}
143 201
144void keeper_release( struct s_Keeper *K) 202void keeper_release( struct s_Keeper *K)
145{ 203{
146 //-- K->count; 204 //-- K->count;
147 MUTEX_UNLOCK( &K->lock_); 205 if( K) MUTEX_UNLOCK( &K->lock_);
148} 206}
149 207
150void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, int _nil_to_sentinel) 208void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, int _nil_to_sentinel)
diff --git a/src/keeper.h b/src/keeper.h
index 27a0f68..1bcb36b 100644
--- a/src/keeper.h
+++ b/src/keeper.h
@@ -9,6 +9,7 @@ struct s_Keeper
9}; 9};
10 10
11const char *init_keepers( int const _nbKeepers); 11const char *init_keepers( int const _nbKeepers);
12void populate_keepers( lua_State *L);
12struct s_Keeper *keeper_acquire( const void *ptr); 13struct s_Keeper *keeper_acquire( const void *ptr);
13void keeper_release( struct s_Keeper *K); 14void keeper_release( struct s_Keeper *K);
14void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, int _nil_to_sentinel); 15void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, int _nil_to_sentinel);
diff --git a/src/lanes.c b/src/lanes.c
index ed54b0f..44db625 100644
--- a/src/lanes.c
+++ b/src/lanes.c
@@ -51,7 +51,7 @@
51 * ... 51 * ...
52 */ 52 */
53 53
54const char *VERSION= "2.2.0"; 54const char *VERSION= "3.0-beta";
55 55
56/* 56/*
57=============================================================================== 57===============================================================================
@@ -787,10 +787,11 @@ static void linda_id( lua_State *L, char const * const which)
787 /* Clean associated structures in the keeper state. 787 /* Clean associated structures in the keeper state.
788 */ 788 */
789 K= keeper_acquire(s); 789 K= keeper_acquire(s);
790 if( K) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup)
790 { 791 {
791 keeper_call( K->L, "clear", L, s, 0 ); 792 keeper_call( K->L, "clear", L, s, 0 );
793 keeper_release(K);
792 } 794 }
793 keeper_release(K);
794 795
795 /* There aren't any lanes waiting on these lindas, since all proxies 796 /* There aren't any lanes waiting on these lindas, since all proxies
796 * have been gc'ed. Right? 797 * have been gc'ed. Right?
@@ -1028,9 +1029,9 @@ volatile DEEP_PRELUDE *timer_deep; // = NULL
1028/* 1029/*
1029* Process end; cancel any still free-running threads 1030* Process end; cancel any still free-running threads
1030*/ 1031*/
1031static void selfdestruct_atexit( void ) 1032static int selfdestruct_atexit( lua_State *L)
1032{ 1033{
1033 if (selfdestruct_first == SELFDESTRUCT_END) return; // no free-running threads 1034 if (selfdestruct_first == SELFDESTRUCT_END) return 0; // no free-running threads
1034 1035
1035 // Signal _all_ still running threads to exit (including the timer thread) 1036 // Signal _all_ still running threads to exit (including the timer thread)
1036 // 1037 //
@@ -1047,7 +1048,7 @@ static void selfdestruct_atexit( void )
1047 // signal the linda the wake up the thread so that it can react to the cancel query 1048 // signal the linda the wake up the thread so that it can react to the cancel query
1048 // let us hope we never land here with a pointer on a linda that has been destroyed... 1049 // let us hope we never land here with a pointer on a linda that has been destroyed...
1049 SIGNAL_T *waiting_on = s->waiting_on; 1050 SIGNAL_T *waiting_on = s->waiting_on;
1050 s->waiting_on = NULL; 1051 //s->waiting_on = NULL; // useful, or not?
1051 SIGNAL_ALL( waiting_on); 1052 SIGNAL_ALL( waiting_on);
1052 } 1053 }
1053 s = s->selfdestruct_next; 1054 s = s->selfdestruct_next;
@@ -1117,6 +1118,7 @@ static void selfdestruct_atexit( void )
1117 // 1118 //
1118 if ( selfdestruct_first != SELFDESTRUCT_END ) { 1119 if ( selfdestruct_first != SELFDESTRUCT_END ) {
1119 unsigned n=0; 1120 unsigned n=0;
1121#if 0
1120 MUTEX_LOCK( &selfdestruct_cs ); 1122 MUTEX_LOCK( &selfdestruct_cs );
1121 { 1123 {
1122 struct s_lane *s= selfdestruct_first; 1124 struct s_lane *s= selfdestruct_first;
@@ -1131,15 +1133,14 @@ static void selfdestruct_atexit( void )
1131 // and works without the block (so let's leave those lanes running) 1133 // and works without the block (so let's leave those lanes running)
1132 // 1134 //
1133//we want to free memory and such when we exit. 1135//we want to free memory and such when we exit.
1134#if 0
1135 // 2.0.2: at least timer lane is still here 1136 // 2.0.2: at least timer lane is still here
1136 // 1137 //
1137 DEBUGEXEC(fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n )); 1138 DEBUGEXEC(fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n ));
1139 n=0;
1138#else 1140#else
1139 // first thing we did was to raise the linda signals the threads were waiting on (if any) 1141 // first thing we did was to raise the linda signals the threads were waiting on (if any)
1140 // therefore, any well-behaved thread should be in CANCELLED state 1142 // therefore, any well-behaved thread should be in CANCELLED state
1141 // these are not running, and the state can be closed 1143 // these are not running, and the state can be closed
1142 n=0;
1143 MUTEX_LOCK( &selfdestruct_cs ); 1144 MUTEX_LOCK( &selfdestruct_cs );
1144 { 1145 {
1145 struct s_lane *s= selfdestruct_first; 1146 struct s_lane *s= selfdestruct_first;
@@ -1147,7 +1148,8 @@ static void selfdestruct_atexit( void )
1147 { 1148 {
1148 struct s_lane *next_s= s->selfdestruct_next; 1149 struct s_lane *next_s= s->selfdestruct_next;
1149 s->selfdestruct_next= NULL; // detach from selfdestruct chain 1150 s->selfdestruct_next= NULL; // detach from selfdestruct chain
1150 THREAD_KILL( &s->thread); 1151 if( s->thread) // can be NULL if previous 'soft' termination succeeded
1152 THREAD_KILL( &s->thread);
1151 // NO lua_close() in this case because we don't know where execution of the state was interrupted 1153 // NO lua_close() in this case because we don't know where execution of the state was interrupted
1152 free( s); 1154 free( s);
1153 s = next_s; 1155 s = next_s;
@@ -1161,6 +1163,7 @@ static void selfdestruct_atexit( void )
1161#endif 1163#endif
1162 } 1164 }
1163 close_keepers(); 1165 close_keepers();
1166 return 0;
1164} 1167}
1165 1168
1166 1169
@@ -1508,10 +1511,36 @@ LUAG_FUNC( set_debug_threadname)
1508// [prio_int=0], 1511// [prio_int=0],
1509// [globals_tbl], 1512// [globals_tbl],
1510// [packagepath], 1513// [packagepath],
1514// [required],
1511// [... args ...] ) 1515// [... args ...] )
1512// 1516//
1513// Upvalues: metatable to use for 'lane_ud' 1517// Upvalues: metatable to use for 'lane_ud'
1514// 1518//
1519
1520// helper function to require a module in the keeper states and in the target state
1521// source state contains module name at the top of the stack
1522static void require_one_module( lua_State *L, lua_State *L2, bool_t _fatal)
1523{
1524 size_t len;
1525 char const *name = lua_tolstring( L, -1, &len);
1526 // require the module in the target lane
1527 STACK_GROW( L2, 2);
1528 lua_getglobal( L2, "require");
1529 if( lua_isnil( L2, -1))
1530 {
1531 lua_pop( L2, 1);
1532 if( _fatal)
1533 luaL_error( L, "cannot pre-require modules without loading package library first");
1534 }
1535 else
1536 {
1537 lua_pushlstring( L2, name, len);
1538 lua_pcall( L2, 1, 0, 0);
1539 // we need to require this module in the keeper states as well
1540 populate_keepers( L);
1541 }
1542}
1543
1515LUAG_FUNC( thread_new ) 1544LUAG_FUNC( thread_new )
1516{ 1545{
1517 lua_State *L2; 1546 lua_State *L2;
@@ -1524,8 +1553,9 @@ LUAG_FUNC( thread_new )
1524 uint_t glob= luaG_isany(L,5) ? 5:0; 1553 uint_t glob= luaG_isany(L,5) ? 5:0;
1525 uint_t ppath = luaG_isany(L,6) ? 6:0; 1554 uint_t ppath = luaG_isany(L,6) ? 6:0;
1526 uint_t pcpath = luaG_isany(L,7) ? 7:0; 1555 uint_t pcpath = luaG_isany(L,7) ? 7:0;
1556 uint_t required = luaG_isany(L,8) ? 8:0;
1527 1557
1528#define FIXED_ARGS (7) 1558#define FIXED_ARGS (8)
1529 uint_t args= lua_gettop(L) - FIXED_ARGS; 1559 uint_t args= lua_gettop(L) - FIXED_ARGS;
1530 1560
1531 if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) 1561 if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX)
@@ -1541,40 +1571,20 @@ LUAG_FUNC( thread_new )
1541 1571
1542 if (!L2) luaL_error( L, "'luaL_newstate()' failed; out of memory" ); 1572 if (!L2) luaL_error( L, "'luaL_newstate()' failed; out of memory" );
1543 1573
1544 STACK_GROW( L,2 ); 1574 STACK_GROW( L, 2);
1545
1546 // Setting the globals table (needs to be done before loading stdlibs,
1547 // and the lane function)
1548 //
1549 if (glob!=0)
1550 {
1551 STACK_CHECK(L)
1552 if (!lua_istable(L,glob))
1553 luaL_error( L, "Expected table, got %s", luaG_typename(L,glob) );
1554
1555 lua_pushvalue( L, glob );
1556
1557 luaG_inter_move( L, L2, 1); // moves the table to L2
1558
1559 // L2 [-1]: table of globals
1560
1561 // "You can change the global environment of a Lua thread using lua_replace"
1562 // (refman-5.0.pdf p. 30)
1563 //
1564 lua_replace( L2, LUA_GLOBALSINDEX );
1565 STACK_END(L,0)
1566 }
1567 1575
1568 // Selected libraries 1576 // Selected libraries
1569 // 1577 //
1570 if (libs) 1578 if (libs)
1571 { 1579 {
1572 const char *err= luaG_openlibs( L2, libs ); 1580 const char *err= luaG_openlibs( L2, libs);
1573 ASSERT_L( !err ); // bad libs should have been noticed by 'lanes.lua' 1581 ASSERT_L( !err ); // bad libs should have been noticed by 'lanes.lua'
1574 1582
1575 serialize_require( L2 ); 1583 serialize_require( L2);
1576 } 1584 }
1577 1585
1586 ASSERT_L( lua_gettop(L2) == 0);
1587
1578 // package.path 1588 // package.path
1579 STACK_CHECK(L2) 1589 STACK_CHECK(L2)
1580 if( ppath) 1590 if( ppath)
@@ -1582,15 +1592,17 @@ LUAG_FUNC( thread_new )
1582 if (lua_type(L,ppath) != LUA_TSTRING) 1592 if (lua_type(L,ppath) != LUA_TSTRING)
1583 luaL_error( L, "expected packagepath as string, got %s", luaG_typename(L,ppath)); 1593 luaL_error( L, "expected packagepath as string, got %s", luaG_typename(L,ppath));
1584 lua_getglobal( L2, "package"); 1594 lua_getglobal( L2, "package");
1585 if( lua_isnil( L2, -1)) 1595 if( lua_isnil( L2, -1)) // package library not loaded: do nothing
1586 { 1596 {
1587 lua_pop( L2, 1); 1597 lua_pop( L2, 1);
1588 luaL_error( L, "specifying a new path for packages, but lane doesn't load package library");
1589 } 1598 }
1590 lua_pushvalue( L, ppath); 1599 else
1591 luaG_inter_move( L, L2, 1); // moves the new path to L2 1600 {
1592 lua_setfield( L2, -2, "path"); // set package.path 1601 lua_pushvalue( L, ppath);
1593 lua_pop( L2, 1); 1602 luaG_inter_move( L, L2, 1); // moves the new path to L2
1603 lua_setfield( L2, -2, "path"); // set package.path
1604 lua_pop( L2, 1);
1605 }
1594 } 1606 }
1595 STACK_END(L2,0) 1607 STACK_END(L2,0)
1596 1608
@@ -1601,18 +1613,83 @@ LUAG_FUNC( thread_new )
1601 if (lua_type(L,pcpath) != LUA_TSTRING) 1613 if (lua_type(L,pcpath) != LUA_TSTRING)
1602 luaL_error( L, "expected packagecpath as string, got %s", luaG_typename(L,pcpath)); 1614 luaL_error( L, "expected packagecpath as string, got %s", luaG_typename(L,pcpath));
1603 lua_getglobal( L2, "package"); 1615 lua_getglobal( L2, "package");
1604 if( lua_isnil( L2, -1)) 1616 if( lua_isnil( L2, -1)) // // package library not loaded: do nothing
1605 { 1617 {
1606 lua_pop( L2, 1); 1618 lua_pop( L2, 1);
1607 luaL_error( L, "specifying a new cpath for packages, but lane doesn't load package library");
1608 } 1619 }
1609 lua_pushvalue( L, pcpath); 1620 else
1610 luaG_inter_move( L, L2, 1); // moves the new cpath to L2 1621 {
1611 lua_setfield( L2, -2, "cpath"); // set package.cpath 1622 lua_pushvalue( L, pcpath);
1612 lua_pop( L2, 1); 1623 luaG_inter_move( L, L2, 1); // moves the new cpath to L2
1624 lua_setfield( L2, -2, "cpath"); // set package.cpath
1625 lua_pop( L2, 1);
1626 }
1613 } 1627 }
1614 STACK_END(L2,0) 1628 STACK_END(L2,0)
1615 1629
1630 // modules to require in the target lane *before* the function is transfered!
1631
1632 //start by requiring lua51-lanes, since it is a bit special
1633 // it not fatal if 'require' isn't loaded, just ignore (may cause function transfer errors later on if the lane pulls the lanes module itself)
1634 STACK_CHECK(L)
1635 STACK_CHECK(L2)
1636 lua_pushliteral( L, "lua51-lanes");
1637 require_one_module( L, L2, FALSE);
1638 lua_pop( L, 1);
1639 STACK_END(L2,0)
1640 STACK_END(L,0)
1641
1642 STACK_CHECK(L)
1643 STACK_CHECK(L2)
1644 if( required)
1645 {
1646 int nbRequired = 1;
1647 // should not happen, was checked in lanes.lua before calling thread_new()
1648 if (lua_type(L, required) != LUA_TTABLE)
1649 luaL_error( L, "expected required module list as a table, got %s", luaG_typename( L, required));
1650 lua_pushnil( L);
1651 while( lua_next( L, required) != 0)
1652 {
1653 if (lua_type(L,-1) != LUA_TSTRING || lua_type(L,-2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired)
1654 {
1655 luaL_error( L, "required module list should be a list of strings.");
1656 }
1657 else
1658 {
1659 require_one_module( L, L2, TRUE);
1660 }
1661 lua_pop( L, 1);
1662 ++ nbRequired;
1663 }
1664 }
1665 STACK_END(L2,0)
1666 STACK_END(L,0)
1667
1668 // Appending the specified globals to the global environment
1669 // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed...
1670 //
1671 if (glob!=0)
1672 {
1673 STACK_CHECK(L)
1674 STACK_CHECK(L2)
1675 if (!lua_istable(L,glob))
1676 luaL_error( L, "Expected table, got %s", luaG_typename(L,glob));
1677
1678 lua_pushnil( L);
1679 while( lua_next( L, glob))
1680 {
1681 luaG_inter_copy( L, L2, 2); // moves the key/value pair to the L2 stack
1682 // assign it in the globals table
1683 lua_rawset( L2, LUA_GLOBALSINDEX);
1684 lua_pop( L, 1);
1685 }
1686
1687 STACK_END(L2, 0)
1688 STACK_END(L, 0)
1689 }
1690
1691 ASSERT_L( lua_gettop(L2) == 0);
1692
1616 // Lane main function 1693 // Lane main function
1617 // 1694 //
1618 STACK_CHECK(L) 1695 STACK_CHECK(L)
@@ -1677,7 +1754,7 @@ LUAG_FUNC( thread_new )
1677 lua_newtable( L); 1754 lua_newtable( L);
1678 lua_setfenv( L, -2); 1755 lua_setfenv( L, -2);
1679 1756
1680 // Place 's' to registry, for 'cancel_test()' (even if 'cs'==0 we still 1757 // Place 's' in registry, for 'cancel_test()' (even if 'cs'==0 we still
1681 // do cancel tests at pending send/receive). 1758 // do cancel tests at pending send/receive).
1682 // 1759 //
1683 lua_pushlightuserdata( L2, CANCEL_TEST_KEY ); 1760 lua_pushlightuserdata( L2, CANCEL_TEST_KEY );
@@ -1792,6 +1869,17 @@ static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force)
1792 // 1869 //
1793 if( s->status < DONE) 1870 if( s->status < DONE)
1794 { 1871 {
1872 // signal the linda the wake up the thread so that it can react to the cancel query
1873 // let us hope we never land here with a pointer on a linda that has been destroyed...
1874 //MUTEX_LOCK( &selfdestruct_cs );
1875 {
1876 SIGNAL_T *waiting_on = s->waiting_on;
1877 if( s->status == WAITING && waiting_on != NULL)
1878 {
1879 SIGNAL_ALL( waiting_on);
1880 }
1881 }
1882 //MUTEX_UNLOCK( &selfdestruct_cs );
1795 s->cancel_request = TRUE; // it's now signalled to stop 1883 s->cancel_request = TRUE; // it's now signalled to stop
1796 done= 1884 done=
1797#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) 1885#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN)
@@ -1816,23 +1904,32 @@ static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force)
1816 1904
1817LUAG_FUNC( thread_cancel) 1905LUAG_FUNC( thread_cancel)
1818{ 1906{
1819 struct s_lane *s= lua_toLane(L,1); 1907 if( lua_gettop( L) != 1 || lua_type( L, 1) != LUA_TUSERDATA)
1820 double secs= 0.0; 1908 {
1821 uint_t force_i=2; 1909 return luaL_error( L, "invalid argument #1, did you use ':' as you should?");
1822 bool_t force, done= TRUE; 1910 }
1911 else
1912 {
1913 struct s_lane *s = lua_toLane( L, 1);
1914 double secs = 0.0;
1915 uint_t force_i = 2;
1916 bool_t force, done= TRUE;
1823 1917
1824 if (lua_isnumber(L,2)) { 1918 if( lua_isnumber( L, 2))
1825 secs= lua_tonumber(L,2); 1919 {
1826 force_i++; 1920 secs = lua_tonumber( L, 2);
1827 } else if (lua_isnil(L,2)) 1921 ++ force_i;
1828 force_i++; 1922 }
1923 else if( lua_isnil( L, 2))
1924 ++ force_i;
1829 1925
1830 force= lua_toboolean(L,force_i); // FALSE if nothing there 1926 force = lua_toboolean( L, force_i); // FALSE if nothing there
1831 1927
1832 done = thread_cancel( s, secs, force); 1928 done = thread_cancel( s, secs, force);
1833 1929
1834 lua_pushboolean( L, done); 1930 lua_pushboolean( L, done);
1835 return 1; 1931 return 1;
1932 }
1836} 1933}
1837 1934
1838//--- 1935//---
@@ -2183,7 +2280,7 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_
2183 // Selfdestruct chain handling 2280 // Selfdestruct chain handling
2184 // 2281 //
2185 MUTEX_INIT( &selfdestruct_cs ); 2282 MUTEX_INIT( &selfdestruct_cs );
2186 atexit( selfdestruct_atexit ); 2283 //atexit( selfdestruct_atexit );
2187 2284
2188 //--- 2285 //---
2189 // Linux needs SCHED_RR to change thread priorities, and that is only 2286 // Linux needs SCHED_RR to change thread priorities, and that is only
@@ -2233,7 +2330,17 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_
2233 2330
2234 // The host Lua state must always have a reference to this Linda object in order for our 'timer_deep_ref' to be valid. 2331 // The host Lua state must always have a reference to this Linda object in order for our 'timer_deep_ref' to be valid.
2235 // So store a reference that we will never actually use. 2332 // So store a reference that we will never actually use.
2236 lua_pushlightuserdata(L, (void *)init_once_LOCKED); 2333 // at the same time, use this object as a 'desinit' marker:
2334 // when the main lua State is closed, this object will be GC'ed
2335 {
2336 lua_newuserdata( L, 1);
2337 lua_newtable( L);
2338 lua_pushcfunction( L, selfdestruct_atexit);
2339 lua_setfield( L, -2, "__gc");
2340 lua_pushliteral( L, "AtExit");
2341 lua_setfield( L, -2, "__metatable");
2342 lua_setmetatable( L, -2);
2343 }
2237 lua_insert(L, -2); // Swap key with the Linda object 2344 lua_insert(L, -2); // Swap key with the Linda object
2238 lua_rawset(L, LUA_REGISTRYINDEX); 2345 lua_rawset(L, LUA_REGISTRYINDEX);
2239 2346
@@ -2241,14 +2348,12 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_
2241 STACK_END(L,0) 2348 STACK_END(L,0)
2242} 2349}
2243 2350
2244int 2351static volatile long s_initCount = 0;
2245#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 2352
2246__declspec(dllexport) 2353LUAG_FUNC( configure )
2247#endif
2248luaopen_lanes( lua_State *L )
2249{ 2354{
2250 static volatile int /*bool*/ go_ahead; // = 0 2355 char const *name = luaL_checkstring( L, lua_upvalueindex( 1));
2251 int const nbKeepers = luaL_optint( L, 2, 1); 2356 int const nbKeepers = luaL_optint( L, 1, 1);
2252 luaL_argcheck( L, nbKeepers > 0, 2, "Number of keeper states must be > 0"); 2357 luaL_argcheck( L, nbKeepers > 0, 2, "Number of keeper states must be > 0");
2253 /* 2358 /*
2254 * Making one-time initializations. 2359 * Making one-time initializations.
@@ -2259,31 +2364,29 @@ luaopen_lanes( lua_State *L )
2259 */ 2364 */
2260#ifdef PLATFORM_WIN32 2365#ifdef PLATFORM_WIN32
2261 { 2366 {
2262 // TBD: Someone please replace this with reliable Win32 API code. Problem is, 2367 static volatile int /*bool*/ go_ahead; // = 0
2263 // there's no autoinitializing locks (s.a. PTHREAD_MUTEX_INITIALIZER) in 2368 if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0)
2264 // Windows so 'InterlockedIncrement' or something needs to be used. 2369 {
2265 // This is 99.9999% safe, though (and always safe if host is single-threaded)
2266 // -- AKa 24-Jun-2009
2267 //
2268 static volatile unsigned my_number; // = 0
2269
2270 if (my_number++ == 0) { // almost atomic
2271 init_once_LOCKED(L, &timer_deep, nbKeepers); 2370 init_once_LOCKED(L, &timer_deep, nbKeepers);
2272 go_ahead= 1; // let others pass 2371 go_ahead= 1; // let others pass
2273 } else { 2372 }
2373 else
2374 {
2274 while( !go_ahead ) { Sleep(1); } // changes threads 2375 while( !go_ahead ) { Sleep(1); } // changes threads
2275 } 2376 }
2276 } 2377 }
2277#else 2378#else
2278 if (!go_ahead) { 2379 if( s_initCount == 0)
2380 {
2279 static pthread_mutex_t my_lock= PTHREAD_MUTEX_INITIALIZER; 2381 static pthread_mutex_t my_lock= PTHREAD_MUTEX_INITIALIZER;
2280 pthread_mutex_lock(&my_lock); 2382 pthread_mutex_lock(&my_lock);
2281 { 2383 {
2282 // Recheck now that we're within the lock 2384 // Recheck now that we're within the lock
2283 // 2385 //
2284 if (!go_ahead) { 2386 if (s_initCount == 0)
2387 {
2285 init_once_LOCKED(L, &timer_deep, nbKeepers); 2388 init_once_LOCKED(L, &timer_deep, nbKeepers);
2286 go_ahead= 1; 2389 s_initCount = 1;
2287 } 2390 }
2288 } 2391 }
2289 pthread_mutex_unlock(&my_lock); 2392 pthread_mutex_unlock(&my_lock);
@@ -2292,7 +2395,10 @@ luaopen_lanes( lua_State *L )
2292 assert( timer_deep != 0 ); 2395 assert( timer_deep != 0 );
2293 2396
2294 // Create main module interface table 2397 // Create main module interface table
2295 lua_newtable(L); 2398 lua_pushvalue( L, lua_upvalueindex( 2));
2399 // remove configure() (this function) from the module interface
2400 lua_pushnil( L);
2401 lua_setfield( L, -2, "configure");
2296 luaL_register(L, NULL, lanes_functions); 2402 luaL_register(L, NULL, lanes_functions);
2297 2403
2298 // metatable for threads 2404 // metatable for threads
@@ -2313,7 +2419,7 @@ luaopen_lanes( lua_State *L )
2313 lua_setfield( L, -2, "join"); 2419 lua_setfield( L, -2, "join");
2314 lua_pushcfunction( L, LG_thread_cancel); 2420 lua_pushcfunction( L, LG_thread_cancel);
2315 lua_setfield( L, -2, "cancel"); 2421 lua_setfield( L, -2, "cancel");
2316 lua_pushboolean( L, 0); 2422 lua_pushliteral( L, "Lane");
2317 lua_setfield( L, -2, "__metatable"); 2423 lua_setfield( L, -2, "__metatable");
2318 2424
2319 lua_pushcclosure( L, LG_thread_new, 1 ); // metatable as closure param 2425 lua_pushcclosure( L, LG_thread_new, 1 ); // metatable as closure param
@@ -2331,8 +2437,41 @@ luaopen_lanes( lua_State *L )
2331 lua_pushlightuserdata( L, CANCEL_ERROR ); 2437 lua_pushlightuserdata( L, CANCEL_ERROR );
2332 lua_setfield(L, -2, "cancel_error"); 2438 lua_setfield(L, -2, "cancel_error");
2333 2439
2334 // Return the local module table 2440 // register all native functions found in that module in the transferable functions database
2335 return 1; 2441 // we process it before _G because we don't want to find the module when scanning _G (this would generate longer names)
2442 populate_func_lookup_table( L, -1, name);
2443 // record all existing C/JIT-fast functions
2444 populate_func_lookup_table( L, LUA_GLOBALSINDEX, NULL);
2445 // Return nothing
2446 lua_pop( L, 1);
2447 return 0;
2448}
2449
2450int
2451#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
2452__declspec(dllexport)
2453#endif
2454luaopen_lanes( lua_State *L )
2455{
2456 // Create main module interface table
2457 // we only have 1 closure, which must be called to configure Lanes
2458 STACK_GROW( L, 3);
2459 STACK_CHECK( L)
2460 lua_newtable(L);
2461 lua_pushvalue(L, 1); // module name
2462 lua_pushvalue(L, -2); // module table
2463 lua_pushcclosure( L, LG_configure, 2);
2464 if( s_initCount == 0)
2465 {
2466 lua_setfield( L, -2, "configure");
2467 }
2468 else // already initialized: call it mmediately and be done
2469 {
2470 lua_pushinteger( L, 666); // any value will do, it will be ignored
2471 lua_call( L, 1, 0);
2472 }
2473 STACK_END( L, 1)
2474 return 1;
2336} 2475}
2337 2476
2338 2477
diff --git a/src/lanes.lua b/src/lanes.lua
index 252d151..8837e4b 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -39,11 +39,19 @@ THE SOFTWARE.
39=============================================================================== 39===============================================================================
40]]-- 40]]--
41 41
42module( "lanes", package.seeall ) 42-- Lua 5.1: module() creates a global variable
43-- Lua 5.2: module() might go away
44-- almost everything module() does is done by require()
45-- -> simply create a table, populate it, return it, and be done
46local lanes = {}
47
48lanes.configure = function( _nb_keepers, _timers)
43 49
44local mm = require "lua51-lanes" 50local mm = require "lua51-lanes"
45assert( type(mm)=="table" ) 51assert( type(mm)=="table" )
46 52
53-- configure() is available only the first time lua51-lanes is required process-wide, and we *must* call it to have the other functions in the interface
54if mm.configure then mm.configure( _nb_keepers) end
47 55
48local thread_new = assert(mm.thread_new) 56local thread_new = assert(mm.thread_new)
49 57
@@ -140,6 +148,7 @@ end
140-- 148--
141-- .globals: table of globals to set for a new thread (passed by value) 149-- .globals: table of globals to set for a new thread (passed by value)
142-- 150--
151-- .required: table of packages to require
143-- ... (more options may be introduced later) ... 152-- ... (more options may be introduced later) ...
144-- 153--
145-- Calling with a function parameter ('lane_func') ends the string/table 154-- Calling with a function parameter ('lane_func') ends the string/table
@@ -161,7 +170,8 @@ local valid_libs= {
161 ["*"]= true 170 ["*"]= true
162} 171}
163 172
164function gen( ... ) 173-- PUBLIC LANES API
174local function gen( ... )
165 local opt= {} 175 local opt= {}
166 local libs= nil 176 local libs= nil
167 local lev= 2 -- level for errors 177 local lev= 2 -- level for errors
@@ -204,27 +214,34 @@ function gen( ... )
204 end 214 end
205 end 215 end
206 216
207 local prio, cs, g_tbl, packagepath, packagecpath 217 local prio, cs, g_tbl, packagepath, packagecpath, required
208 218
209 for k,v in pairs(opt) do 219 for k,v in pairs(opt) do
210 if k=="priority" then prio= v 220 if k=="priority" then prio= v
211 elseif k=="cancelstep" then cs= (v==true) and 100 or 221 elseif k=="cancelstep" then
212 (v==false) and 0 or 222 cs = (v==true) and 100 or
213 type(v)=="number" and v or 223 (v==false) and 0 or
214 error( "Bad cancelstep: "..tostring(v), lev ) 224 type(v)=="number" and v or
225 error( "Bad cancelstep: "..tostring(v), lev )
215 elseif k=="globals" then g_tbl= v 226 elseif k=="globals" then g_tbl= v
216 elseif k=="packagepath" then packagepath= v 227 elseif k=="packagepath" then
217 elseif k=="packagecpath" then packagecpath= v 228 packagepath = (type( v) == "string") and v or error( "Bad packagepath: " .. tostring( v), lev)
229 elseif k=="packagecpath" then
230 packagecpath = (type( v) == "string") and v or error( "Bad packagecpath: " .. tostring( v), lev)
231 elseif k=="required" then
232 required= (type( v) == "table") and v or error( "Bad required: " .. tostring( v), lev)
218 --.. 233 --..
219 elseif k==1 then error( "unkeyed option: ".. tostring(v), lev ) 234 elseif k==1 then error( "unkeyed option: ".. tostring(v), lev )
220 else error( "Bad option: ".. tostring(k), lev ) 235 else error( "Bad option: ".. tostring(k), lev )
221 end 236 end
222 end 237 end
223 238
239 if not packagepath then packagepath = package.path end
240 if not packagecpath then packagecpath = package.cpath end
224 -- Lane generator 241 -- Lane generator
225 -- 242 --
226 return function(...) 243 return function(...)
227 return thread_new( func, libs, cs, prio, g_tbl, packagepath, packagecpath, ...) -- args 244 return thread_new( func, libs, cs, prio, g_tbl, packagepath, packagecpath, required, ...) -- args
228 end 245 end
229end 246end
230 247
@@ -235,12 +252,16 @@ end
235----- 252-----
236-- lanes.linda() -> linda_ud 253-- lanes.linda() -> linda_ud
237-- 254--
238linda = mm.linda 255-- PUBLIC LANES API
256local linda = mm.linda
239 257
240 258
241---=== Timers ===--- 259---=== Timers ===---
242local want_timers = true 260
243if want_timers then 261-- PUBLIC LANES API
262local timer = function() error "timers are not active" end
263
264if _timers ~= "NO_TIMERS" then
244 265
245local timer_gateway= assert( mm.timer_gateway ) 266local timer_gateway= assert( mm.timer_gateway )
246-- 267--
@@ -424,6 +445,8 @@ if first_time then
424 assert( key and wakeup_at and period ) 445 assert( key and wakeup_at and period )
425 446
426 set_timer( linda, key, wakeup_at, period>0 and period or nil ) 447 set_timer( linda, key, wakeup_at, period>0 and period or nil )
448 elseif secs == 0 then -- got no value while block-waiting?
449 WR( "timer lane: no linda, aborted?")
427 end 450 end
428 end 451 end
429 end )() 452 end )()
@@ -432,7 +455,8 @@ end
432----- 455-----
433-- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] ) 456-- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] )
434-- 457--
435function timer( linda, key, a, period ) 458-- PUBLIC LANES API
459timer = function( linda, key, a, period )
436 460
437 if a==0.0 then 461 if a==0.0 then
438 -- Caller expects to get current time stamp in Linda, on return 462 -- Caller expects to get current time stamp in Linda, on return
@@ -456,7 +480,7 @@ function timer( linda, key, a, period )
456 timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period ) 480 timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period )
457end 481end
458 482
459end -- want_timers 483end -- _timers
460 484
461---=== Lock & atomic generators ===--- 485---=== Lock & atomic generators ===---
462 486
@@ -473,7 +497,8 @@ end -- want_timers
473-- Returns an access function that allows 'N' simultaneous entries between 497-- Returns an access function that allows 'N' simultaneous entries between
474-- acquire (+M) and release (-M). For binary locks, use M==1. 498-- acquire (+M) and release (-M). For binary locks, use M==1.
475-- 499--
476function genlock( linda, key, N ) 500-- PUBLIC LANES API
501local function genlock( linda, key, N )
477 linda:limit(key,N) 502 linda:limit(key,N)
478 linda:set(key,nil) -- clears existing data 503 linda:set(key,nil) -- clears existing data
479 504
@@ -506,7 +531,8 @@ end
506-- Returns an access function that allows atomic increment/decrement of the 531-- Returns an access function that allows atomic increment/decrement of the
507-- number in 'key'. 532-- number in 'key'.
508-- 533--
509function genatomic( linda, key, initial_val ) 534-- PUBLIC LANES API
535local function genatomic( linda, key, initial_val )
510 linda:limit(key,2) -- value [,true] 536 linda:limit(key,2) -- value [,true]
511 linda:set(key,initial_val or 0.0) -- clears existing data (also queue) 537 linda:set(key,initial_val or 0.0) -- clears existing data (also queue)
512 538
@@ -522,4 +548,23 @@ end
522 548
523-- newuserdata = mm.newuserdata 549-- newuserdata = mm.newuserdata
524 550
551 -- activate full interface
552 lanes.gen = gen
553 lanes.linda = mm.linda
554 lanes.timer = timer
555 lanes.genlock = genlock
556 lanes.genatomic = genatomic
557 -- from now on, calling configure does nothing but checking that we don't call it with parameters that changed compared to the first invocation
558 lanes.configure = function( _nk, _t)
559 if _nk ~= _nb_keepers then
560 error( "mismatched configuration: " .. tostring( _nk) .. " keepers instead of " .. tostring( _nb_keepers))
561 end
562 if _t ~= _timers then
563 error( "mismatched configuration: " .. tostring( _t) .. " timer activity instead of " .. tostring( _timers))
564 end
565 end
566end -- lanes.configure
567
525--the end 568--the end
569return lanes
570
diff --git a/src/threading.c b/src/threading.c
index 00be243..0a07d47 100644
--- a/src/threading.c
+++ b/src/threading.c
@@ -74,6 +74,7 @@ THE SOFTWARE.
74#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 74#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
75static void FAIL( const char *funcname, int rc ) { 75static void FAIL( const char *funcname, int rc ) {
76 fprintf( stderr, "%s() failed! (%d)\n", funcname, rc ); 76 fprintf( stderr, "%s() failed! (%d)\n", funcname, rc );
77 __debugbreak(); // give a chance to the debugger!
77 abort(); 78 abort();
78} 79}
79#endif 80#endif
diff --git a/src/tools.c b/src/tools.c
index f9854db..b412e84 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -31,6 +31,7 @@ THE SOFTWARE.
31*/ 31*/
32 32
33#include "tools.h" 33#include "tools.h"
34#include "keeper.h"
34 35
35#include "lualib.h" 36#include "lualib.h"
36#include "lauxlib.h" 37#include "lauxlib.h"
@@ -66,7 +67,7 @@ void luaG_dump( lua_State* L ) {
66 // enable it for more debugging. 67 // enable it for more debugging.
67 // 68 //
68 STACK_CHECK(L) 69 STACK_CHECK(L)
69 STACK_GROW( L, 2 ) 70 STACK_GROW( L, 2);
70 71
71 lua_getglobal( L, "tostring" ); 72 lua_getglobal( L, "tostring" );
72 // 73 //
@@ -108,21 +109,302 @@ static const luaL_Reg libs[] = {
108 109
109static bool_t openlib( lua_State *L, const char *name, size_t len ) { 110static bool_t openlib( lua_State *L, const char *name, size_t len ) {
110 111
111 unsigned i; 112 unsigned i;
112 bool_t all= strncmp( name, "*", len ) == 0; 113 bool_t all= strncmp( name, "*", len ) == 0;
113 114
114 for( i=0; libs[i].name; i++ ) { 115 for( i=0; libs[i].name; i++ )
115 if (all || (strncmp(name, libs[i].name, len) ==0)) { 116 {
116 if (libs[i].func) { 117 if (all || (strncmp(name, libs[i].name, len) ==0))
117 STACK_GROW(L,2); 118 {
118 lua_pushcfunction( L, libs[i].func ); 119 if (libs[i].func)
119 lua_pushstring( L, libs[i].name ); 120 {
120 lua_call( L, 1, 0 ); 121 STACK_GROW(L,1);
121 } 122 STACK_CHECK(L)
122 if (!all) return TRUE; 123 lua_pushcfunction( L, libs[i].func);
123 } 124 // pushes the module table on the stack
124 } 125 lua_call( L, 0, 1);
125 return all; 126 populate_func_lookup_table( L, -1, libs[i].name);
127 // remove the module when we are done
128 lua_pop( L, 1);
129 STACK_END(L, 0)
130 }
131 if (!all) return TRUE;
132 }
133 }
134 return all;
135}
136
137static int dummy_writer(lua_State *L, const void* p, size_t sz, void* ud)
138{
139 return 666;
140}
141
142
143/*
144 * differentiation between C, bytecode and JIT-fast functions
145 *
146 *
147 * +----------+------------+----------+
148 * | bytecode | C function | JIT-fast |
149 * +-----------------+----------+------------+----------+
150 * | lua_topointer | | | |
151 * +-----------------+----------+------------+----------+
152 * | lua_tocfunction | NULL | | NULL |
153 * +-----------------+----------+------------+----------+
154 * | lua_dump | 666 | 1 | 1 |
155 * +-----------------+----------+------------+----------+
156 */
157
158typedef enum
159{
160 FST_Bytecode,
161 FST_Native,
162 FST_FastJIT
163} FuncSubType;
164
165FuncSubType luaG_getfuncsubtype( lua_State *L, int _i)
166{
167 if( lua_tocfunction( L, _i))
168 {
169 return FST_Native;
170 }
171 {
172 int mustpush = 0, dumpres;
173 if( STACK_ABS( L, _i) != lua_gettop( L))
174 {
175 lua_pushvalue( L, _i);
176 mustpush = 1;
177 }
178 // the provided writer fails with code 666
179 // therefore, anytime we get 666, this means that lua_dump() attempted a dump
180 // all other cases mean this is either a C or LuaJIT-fast function
181 dumpres = lua_dump( L, dummy_writer, NULL);
182 lua_pop( L, mustpush);
183 if( dumpres == 666)
184 {
185 return FST_Bytecode;
186 }
187 }
188 return FST_FastJIT;
189}
190
191static lua_CFunction luaG_tocfunction( lua_State *L, int _i, FuncSubType *_out)
192{
193 lua_CFunction p = lua_tocfunction( L, _i);
194 *_out = luaG_getfuncsubtype( L, _i);
195 return p;
196}
197
198
199#define LOOKUP_KEY "ddea37aa-50c7-4d3f-8e0b-fb7a9d62bac5"
200#define LOOKUP_KEY_CACHE "d1059270-4976-4193-a55b-c952db5ab7cd"
201
202
203// inspired from tconcat() in ltablib.c
204static char const * luaG_pushFQN(lua_State *L, int t, int last)
205{
206 int i = 1;
207 luaL_Buffer b;
208 STACK_CHECK( L)
209 luaL_buffinit(L, &b);
210 for( ; i < last; i++)
211 {
212 lua_rawgeti( L, t, i);
213 luaL_addvalue( &b);
214 luaL_addlstring(&b, ".", 1);
215 }
216 if (i == last) /* add last value (if interval was not empty) */
217 {
218 lua_rawgeti( L, t, i);
219 luaL_addvalue( &b);
220 }
221 luaL_pushresult( &b);
222 STACK_END( L, 1)
223 return lua_tostring( L, -1);
224}
225
226
227static void populate_func_lookup_table_recur( lua_State *L, int _ctx_base, int _i, int _depth)
228{
229 lua_Integer visit_count;
230 // slot 1 in the stack contains the table that receives everything we found
231 int const dest = _ctx_base;
232 // slot 2 contains a table that, when concatenated, produces the fully qualified name of scanned elements in the table provided at slot _i
233 int const fqn = _ctx_base + 1;
234 // slot 3 contains a cache that stores all already visited tables to avoid infinite recursion loops
235 int const cache = _ctx_base + 2;
236 // we need to remember subtables to process them after functions encountered at the current depth (breadth-first search)
237 int const breadth_first_cache = lua_gettop( L) + 1;
238
239 STACK_GROW( L, 6);
240 // slot _i contains a table where we search for functions
241 STACK_CHECK( L) // ... {_i}
242
243 // if table is already visited, we are done
244 lua_pushvalue( L, _i); // ... {_i} {}
245 lua_rawget( L, cache); // ... {_i} nil|n
246 visit_count = lua_tointeger( L, -1); // 0 if nil, else n
247 lua_pop( L, 1); // ... {_i}
248 STACK_MID( L, 0)
249 if( visit_count > 0)
250 {
251 return;
252 }
253
254 // remember we visited this table (1-visit count)
255 lua_pushvalue( L, _i); // ... {_i} {}
256 lua_pushinteger( L, visit_count + 1); // ... {_i} {} 1
257 lua_rawset( L, cache); // ... {_i}
258 STACK_MID( L, 0)
259
260 // this table is at breadth_first_cache index
261 lua_newtable( L); // ... {_i} {bfc}
262 ASSERT_L( lua_gettop( L) == breadth_first_cache);
263 // iterate over all entries in the processed table
264 lua_pushnil( L); // ... {_i} {bfc} nil
265 while( lua_next( L, _i) != 0) // ... {_i} {bfc} k v
266 {
267 // just for debug, not actually needed
268 //char const * key = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : "not a string";
269 // subtable: process it recursively
270 if( lua_istable( L, -1)) // ... {_i} {bfc} k {}
271 {
272 // increment visit count to make sure we will actually scan it at this recursive level
273 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {}
274 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {} {}
275 lua_rawget( L, cache); // ... {_i} {bfc} k {} {} n?
276 visit_count = lua_tointeger( L, -1) + 1; // 1 if we got nil, else n+1
277 lua_pop( L, 1); // ... {_i} {bfc} k {} {}
278 lua_pushinteger( L, visit_count); // ... {_i} {bfc} k {} {} n
279 lua_rawset( L, cache); // ... {_i} {bfc} k {}
280 // store the table in the breadth-first cache
281 lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k
282 lua_insert( L, -2); // ... {_i} {bfc} k k {}
283 lua_rawset( L, breadth_first_cache); // ... {_i} {bfc} k
284 STACK_MID( L, 2)
285 }
286 else if( lua_isfunction( L, -1)) // ... {_i} {bfc} k func
287 {
288 if( luaG_getfuncsubtype( L, -1) != FST_Bytecode)
289 {
290 char const *fqnString;
291 bool_t not_registered;
292 // first, skip everything if the function is already known
293 lua_pushvalue( L, -1); // ... {_i} {bfc} k func func
294 lua_rawget( L, dest); // ... {_i} {bfc} k func name?
295 not_registered = lua_isnil( L, -1);
296 lua_pop( L, 1); // ... {_i} {bfc} k func
297 if( not_registered)
298 {
299 ++ _depth;
300 // push function name in fqn stack (note that concatenation will crash if name is a not string!)
301 lua_pushvalue( L, -2); // ... {_i} {bfc} k func k
302 lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} k func
303 // generate name
304 fqnString = luaG_pushFQN( L, fqn, _depth); // ... {_i} {bfc} k func "f.q.n"
305 //puts( fqnString);
306 // prepare the stack for database feed
307 lua_pushvalue( L, -1); // ... {_i} {bfc} k func "f.q.n" "f.q.n"
308 lua_pushvalue( L, -3); // ... {_i} {bfc} k func "f.q.n" "f.q.n" func
309 // t["f.q.n"] = func
310 lua_rawset( L, dest); // ... {_i} {bfc} k func "f.q.n"
311 // t[func] = "f.q.n"
312 lua_rawset( L, dest); // ... {_i} {bfc} k
313 // remove table name from fqn stack
314 lua_pushnil( L); // ... {_i} {bfc} k nil
315 lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} k
316 -- _depth;
317 }
318 else
319 {
320 lua_pop( L, 1); // ... {_i} {bfc} k
321 }
322 }
323 else
324 {
325 lua_pop( L, 1); // ... {_i} {bfc} k
326 }
327 }
328 else
329 {
330 lua_pop( L, 1); // ... {_i} {bfc} k
331 }
332 STACK_MID( L, 2)
333 }
334 // now process the tables we encountered at that depth
335 ++ _depth;
336 lua_pushnil( L); // ... {_i} {bfc} nil
337 while( lua_next( L, breadth_first_cache) != 0) // ... {_i} {bfc} k {}
338 {
339 // un-visit this table in case we do need to process it
340 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {}
341 lua_rawget( L, cache); // ... {_i} {bfc} k {} n
342 ASSERT_L( lua_type( L, -1) == LUA_TNUMBER);
343 visit_count = lua_tointeger( L, -1) - 1;
344 lua_pop( L, 1); // ... {_i} {bfc} k {}
345 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {}
346 if( visit_count > 0)
347 {
348 lua_pushinteger( L, visit_count); // ... {_i} {bfc} k {} {} n
349 }
350 else
351 {
352 lua_pushnil( L); // ... {_i} {bfc} k {} {} nil
353 }
354 lua_rawset( L, cache); // ... {_i} {bfc} k {}
355 // push table name in fqn stack (note that concatenation will crash if name is a not string!)
356 lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k
357 lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} k {}
358 populate_func_lookup_table_recur( L, _ctx_base, lua_gettop( L), _depth); // ... {_i} {bfc} k {}
359 lua_pop( L, 1); // ... {_i} {bfc} k
360 STACK_MID( L, 2)
361 }
362 // remove table name from fqn stack
363 lua_pushnil( L); // ... {_i} {bfc} nil
364 lua_rawseti( L, fqn, _depth); // ... {_i} {bfc}
365 -- _depth;
366 // we are done with our cache
367 lua_pop( L, 1); // ... {_i}
368 STACK_END( L, 0)
369 // we are done // ... {_i} {bfc}
370}
371
372/*
373 * create a "fully.qualified.name" <-> function equivalence dabase
374 */
375void populate_func_lookup_table( lua_State *L, int _i, char const *_name)
376{
377 int const ctx_base = lua_gettop( L) + 1;
378 int const in_base = STACK_ABS( L, _i);
379 int const start_depth = _name ? 1 : 0;
380 //printf( "%p: populate_func_lookup_table('%s')\n", L, _name ? _name : "NULL");
381 STACK_GROW( L, 3);
382 STACK_CHECK( L)
383 lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // {}?
384 if( lua_isnil( L, -1)) // nil
385 {
386 lua_pop( L, 1); //
387 lua_newtable( L); // {}
388 lua_pushvalue( L, -1); // {} {}
389 lua_setfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // {}
390 }
391 lua_newtable( L); // {} {fqn}
392 if( _name)
393 {
394 lua_pushstring( L, _name); // {} {fqn} "name"
395 lua_rawseti( L, -2, start_depth); // {} {fqn}
396 }
397 lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY_CACHE); // {} {fqn} {cache}?
398 if( lua_isnil( L, -1))
399 {
400 lua_pop( L, 1); // {} {fqn}
401 lua_newtable( L); // {} {fqn} {cache}
402 lua_pushvalue( L, -1); // {} {fqn} {cache} {cache}
403 lua_setfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY_CACHE); // {} {fqn} {cache}
404 }
405 populate_func_lookup_table_recur( L, ctx_base, in_base, start_depth); // {...} {fqn} {cache}
406 lua_pop( L, 3);
407 STACK_END( L, 0)
126} 408}
127 409
128/* 410/*
@@ -139,33 +421,39 @@ static bool_t openlib( lua_State *L, const char *name, size_t len ) {
139*/ 421*/
140#define is_name_char(c) (isalpha(c) || (c)=='*') 422#define is_name_char(c) (isalpha(c) || (c)=='*')
141 423
142const char *luaG_openlibs( lua_State *L, const char *libs ) { 424const char *luaG_openlibs( lua_State *L, const char *libs)
143 const char *p; 425{
144 unsigned len; 426 const char *p;
427 unsigned len;
145 428
146 if (!libs) return NULL; // no libs, not even 'base' 429 if (!libs) return NULL; // no libs, not even 'base'
147 430
148 // 'lua.c' stops GC during initialization so perhaps its a good idea. :) 431 // 'lua.c' stops GC during initialization so perhaps its a good idea. :)
149 // 432 //
150 lua_gc(L, LUA_GCSTOP, 0); 433 lua_gc( L, LUA_GCSTOP, 0);
151 434
152 // Anything causes 'base' to be taken in 435 // Anything causes 'base' to be taken in
153 // 436 //
154 STACK_GROW(L,2); 437 STACK_GROW(L,2);
155 lua_pushcfunction( L, luaopen_base ); 438 STACK_CHECK(L)
156 lua_pushliteral( L, "" ); 439 lua_pushcfunction( L, luaopen_base);
157 lua_call( L, 1, 0 ); 440 lua_call( L, 0, 1);
158 441 // after opening base, register the functions they exported in our name<->function database
159 for( p= libs; *p; p+=len ) { 442 populate_func_lookup_table( L, LUA_GLOBALSINDEX, NULL);
160 len=0; 443 lua_pop( L, 1);
161 while (*p && !is_name_char(*p)) p++; // bypass delimiters 444 STACK_MID( L, 0);
162 while (is_name_char(p[len])) len++; // bypass name 445 for( p= libs; *p; p+=len )
163 if (len && (!openlib( L, p, len ))) 446 {
164 break; 447 len=0;
165 } 448 while (*p && !is_name_char(*p)) p++; // bypass delimiters
166 lua_gc(L, LUA_GCRESTART, 0); 449 while (is_name_char(p[len])) len++; // bypass name
450 if (len && (!openlib( L, p, len )))
451 break;
452 }
453 STACK_END(L,0)
454 lua_gc(L, LUA_GCRESTART, 0);
167 455
168 return *p ? p : NULL; 456 return *p ? p : NULL;
169} 457}
170 458
171 459
@@ -284,7 +572,7 @@ luaG_IdFunction get_idfunc( lua_State *L, int index )
284{ 572{
285 luaG_IdFunction ret; 573 luaG_IdFunction ret;
286 574
287 index= STACK_ABS(L,index); 575 index = STACK_ABS( L, index);
288 576
289 STACK_GROW(L,1); 577 STACK_GROW(L,1);
290 578
@@ -696,7 +984,7 @@ uint_t get_mt_id( lua_State *L, int i ) {
696 static uint_t last_id= 0; 984 static uint_t last_id= 0;
697 uint_t id; 985 uint_t id;
698 986
699 i= STACK_ABS(L,i); 987 i = STACK_ABS( L, i);
700 988
701 STACK_GROW(L,3); 989 STACK_GROW(L,3);
702 990
@@ -819,8 +1107,8 @@ static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uin
819 1107
820static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) 1108static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i )
821{ 1109{
1110 void * const aspointer = (void*)lua_topointer( L, i );
822 // TBD: Merge this and same code for tables 1111 // TBD: Merge this and same code for tables
823
824 ASSERT_L( L2_cache_i != 0 ); 1112 ASSERT_L( L2_cache_i != 0 );
825 1113
826 STACK_GROW(L2,3); 1114 STACK_GROW(L2,3);
@@ -832,7 +1120,7 @@ static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, ui
832 // We don't need to use the from state ('L') in ID since the life span 1120 // We don't need to use the from state ('L') in ID since the life span
833 // is only for the duration of a copy (both states are locked). 1121 // is only for the duration of a copy (both states are locked).
834 // 1122 //
835 lua_pushlightuserdata( L2, (void*)lua_topointer( L, i )); // push a light userdata uniquely representing the function 1123 lua_pushlightuserdata( L2, aspointer); // push a light userdata uniquely representing the function
836 1124
837 //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); 1125 //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) );
838 1126
@@ -886,9 +1174,46 @@ static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, ui
886 // 1174 //
887 // L2 [-1]: function 1175 // L2 [-1]: function
888 1176
889 ASSERT_L( lua_isfunction(L2,-1) ); 1177 ASSERT_L( lua_isfunction(L2,-1));
890} 1178}
891 1179
1180/*
1181* Push a looked-up native/LuaJIT function.
1182*/
1183static void lookup_native_func( lua_State *L2, lua_State *L, uint_t i)
1184{
1185 char const *fqn;
1186 size_t len;
1187 _ASSERT_L( L, lua_isfunction( L, i));
1188 STACK_CHECK( L)
1189 STACK_CHECK( L2)
1190 // fetch the name from the source state's lookup table
1191 lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // {}
1192 _ASSERT_L( L, lua_istable( L, -1));
1193 lua_pushvalue( L, i); // {} f
1194 lua_rawget( L, -2); // {} "f.q.n"
1195 fqn = lua_tolstring( L, -1, &len);
1196 if( !fqn)
1197 {
1198 luaL_error( L, "function not found in origin transfer database.");
1199 }
1200 // push the equivalent function in the destination's stack, retrieved from the lookup table
1201 lua_getfield( L2, LUA_REGISTRYINDEX, LOOKUP_KEY); // {}
1202 _ASSERT_L( L2, lua_istable( L2, -1));
1203 lua_pushlstring( L2, fqn, len); // {} "f.q.n"
1204 lua_pop( L, 2); //
1205 lua_rawget( L2, -2); // {} f
1206 if( !lua_isfunction( L2, -1))
1207 {
1208 // yarglah: luaL_error formatting doesn't support string width modifier!
1209 char message[256];
1210 sprintf( message, "function %*s not found in destination transfer database.", len, fqn);
1211 luaL_error( L, message);
1212 }
1213 lua_remove( L2, -2); // f
1214 STACK_END( L2, 1)
1215 STACK_END( L, 0)
1216}
892 1217
893#define LOG_FUNC_INFO 0 1218#define LOG_FUNC_INFO 0
894 1219
@@ -900,114 +1225,132 @@ enum e_vt {
900}; 1225};
901static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i, enum e_vt value_type ); 1226static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i, enum e_vt value_type );
902 1227
903static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) { 1228static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i )
904 1229{
905 lua_CFunction cfunc= lua_tocfunction( L,i ); 1230 FuncSubType funcSubType;
906 unsigned n; 1231 lua_CFunction cfunc = luaG_tocfunction( L, i, &funcSubType); // NULL for LuaJIT-fast && bytecode functions
907 1232 i = STACK_ABS( L, i);
908 ASSERT_L( L2_cache_i != 0 );
909
910 STACK_GROW(L,2);
911
912 STACK_CHECK(L)
913 if (!cfunc) { // Lua function
914 luaL_Buffer b;
915 const char *s;
916 size_t sz;
917 int tmp;
918 const char *name= NULL;
919 int linedefined = 0;
920 1233
921#if LOG_FUNC_INFO 1234 ASSERT_L( L2_cache_i != 0 );
922 // "To get information about a function you push it onto the 1235 STACK_GROW(L,2);
923 // stack and start the what string with the character '>'." 1236 STACK_CHECK(L)
924 //
925 { lua_Debug ar;
926 lua_pushvalue( L, i );
927 lua_getinfo(L, ">nS", &ar); // fills 'name' 'namewhat' and 'linedefined', pops function
928 name= ar.namewhat;
929 linedefined = ar.linedefined;
930 fprintf( stderr, "NAME: %s @ %d\n", ar.short_src, linedefined); // just gives NULL
931 }
932#endif // LOG_FUNC_INFO
933 // 'lua_dump()' needs the function at top of stack
934 //
935 if (i!=-1) lua_pushvalue( L, i );
936 1237
937 luaL_buffinit(L,&b); 1238 if( funcSubType == FST_Bytecode)
938 tmp= lua_dump(L, buf_writer, &b); 1239 {
939 ASSERT_L(tmp==0); 1240 unsigned n;
940 // 1241 luaL_Buffer b;
941 // "value returned is the error code returned by the last call 1242 // 'lua_dump()' needs the function at top of stack
942 // to the writer" (and we only return 0) 1243 // if already on top of the stack, no need to push again
1244 int needToPush = (i != lua_gettop( L));
1245 if( needToPush)
1246 lua_pushvalue( L, i);
1247
1248 luaL_buffinit( L, &b);
1249 //
1250 // "value returned is the error code returned by the last call
1251 // to the writer" (and we only return 0)
1252 // not sure this could ever fail but for memory shortage reasons
1253 if( lua_dump( L, buf_writer, &b) != 0)
1254 {
1255 luaL_error( L, "internal error: function dump failed.");
1256 }
943 1257
944 luaL_pushresult(&b); // pushes dumped string on 'L' 1258 luaL_pushresult( &b); // pushes dumped string on 'L'
945 s= lua_tolstring(L,-1,&sz);
946 ASSERT_L( s && sz );
947 1259
948 if (i!=-1) lua_remove( L, -2 ); 1260 // if not pushed, no need to pop
1261 if( needToPush)
1262 {
1263 lua_remove( L, -2);
1264 }
949 1265
950 // Note: Line numbers seem to be taken precisely from the 1266 // transfer the bytecode, then the upvalues, to create a similar closure
951 // original function. 'name' is not used since the chunk 1267 {
952 // is precompiled (it seems...). 1268 const char *name= NULL;
953 // 1269
954 // TBD: Can we get the function's original name through, as well? 1270 #if LOG_FUNC_INFO
955 // 1271 // "To get information about a function you push it onto the
956 if (luaL_loadbuffer(L2, s, sz, name) != 0) { 1272 // stack and start the what string with the character '>'."
957 // chunk is precompiled so only LUA_ERRMEM can happen 1273 //
958 // "Otherwise, it pushes an error message" 1274 {
959 // 1275 lua_Debug ar;
960 STACK_GROW( L,1 ); 1276 lua_pushvalue( L, i );
961 luaL_error( L, "%s", lua_tostring(L2,-1) ); 1277 lua_getinfo(L, ">nS", &ar); // fills 'name' 'namewhat' and 'linedefined', pops function
962 } 1278 name= ar.namewhat;
963 lua_pop(L,1); // remove the dumped string 1279 fprintf( stderr, "NAME: %s @ %d\n", ar.short_src, ar.linedefined); // just gives NULL
964 STACK_MID(L,0) 1280 }
965 } 1281 #endif // LOG_FUNC_INFO
1282 {
1283 const char *s;
1284 size_t sz;
1285 s = lua_tolstring( L, -1, &sz);
1286 ASSERT_L( s && sz);
1287
1288 // Note: Line numbers seem to be taken precisely from the
1289 // original function. 'name' is not used since the chunk
1290 // is precompiled (it seems...).
1291 //
1292 // TBD: Can we get the function's original name through, as well?
1293 //
1294 if (luaL_loadbuffer(L2, s, sz, name) != 0)
1295 {
1296 // chunk is precompiled so only LUA_ERRMEM can happen
1297 // "Otherwise, it pushes an error message"
1298 //
1299 STACK_GROW( L,1);
1300 luaL_error( L, "%s", lua_tostring(L2,-1));
1301 }
1302 lua_pop( L, 1); // remove the dumped string
1303 }
1304 STACK_MID( L, 0)
1305
1306 /* push over any upvalues; references to this function will come from
1307 * cache so we don't end up in eternal loop.
1308 */
1309 for( n=0; lua_getupvalue( L, i, 1+n ) != NULL; n++ )
1310 {
1311 if ((!cfunc) && lua_equal(L,i,-1))
1312 {
1313 /* Lua closure that has a (recursive) upvalue to itself
1314 */
1315 lua_pushvalue( L2, -((int)n)-1 );
1316 }
1317 else
1318 {
1319 if( !inter_copy_one_( L2, L2_cache_i, L, lua_gettop(L), VT_NORMAL))
1320 luaL_error( L, "Cannot copy upvalue type '%s'", luaG_typename( L, -1));
1321 }
1322 lua_pop( L, 1);
1323 }
1324 // L2: function + 'n' upvalues (>=0)
1325
1326 STACK_MID(L,0)
1327
1328 // Set upvalues (originally set to 'nil' by 'lua_load')
1329 {
1330 int func_index = lua_gettop( L2) - n;
1331 for( ; n > 0; -- n)
1332 {
1333 char const *rc = lua_setupvalue( L2, func_index, n);
1334 //
1335 // "assigns the value at the top of the stack to the upvalue and returns its name.
1336 // It also pops the value from the stack."
1337
1338 ASSERT_L(rc); // not having enough slots?
1339 }
1340 }
1341 }
1342 }
1343 else // C function OR LuaJIT fast function!!!
1344 {
966#if LOG_FUNC_INFO 1345#if LOG_FUNC_INFO
967 else 1346 fprintf( stderr, "NAME: [C] function %p \n", cfunc);
968 {
969 fprintf( stderr, "NAME: [C] function %p \n", cfunc);
970 }
971#endif // LOG_FUNC_INFO 1347#endif // LOG_FUNC_INFO
972 1348 // No need to transfer upvalues for C/JIT functions since they weren't actually copied, only looked up
973 /* push over any upvalues; references to this function will come from 1349 lookup_native_func( L2, L, i);
974 * cache so we don't end up in eternal loop. 1350 }
975 */ 1351 STACK_END(L,0)
976 for( n=0; lua_getupvalue( L, i, 1+n ) != NULL; n++ ) {
977 if ((!cfunc) && lua_equal(L,i,-1)) {
978 /* Lua closure that has a (recursive) upvalue to itself
979 */
980 lua_pushvalue( L2, -((int)n)-1 );
981 } else {
982 if (!inter_copy_one_( L2, L2_cache_i, L, lua_gettop(L), VT_NORMAL ))
983 luaL_error( L, "Cannot copy upvalue type '%s'", luaG_typename(L,-1) );
984 }
985 lua_pop(L,1);
986 }
987 // L2: function + 'n' upvalues (>=0)
988
989 STACK_MID(L,0)
990
991 if (cfunc) {
992 lua_pushcclosure( L2, cfunc, n ); // eats up upvalues
993 } else {
994 // Set upvalues (originally set to 'nil' by 'lua_load')
995 //
996 int func_index= lua_gettop(L2)-n;
997
998 for( ; n>0; n-- ) {
999 const char *rc= lua_setupvalue( L2, func_index, n );
1000 //
1001 // "assigns the value at the top of the stack to the upvalue and returns its name.
1002 // It also pops the value from the stack."
1003
1004 ASSERT_L(rc); // not having enough slots?
1005 }
1006 }
1007 STACK_END(L,0)
1008} 1352}
1009 1353
1010
1011/* 1354/*
1012* Copies a value from 'L' state (at index 'i') to 'L2' state. Does not remove 1355* Copies a value from 'L' state (at index 'i') to 'L2' state. Does not remove
1013* the original value. 1356* the original value.
@@ -1300,32 +1643,36 @@ MUTEX_T require_cs;
1300// 1643//
1301// Upvalues: [1]: original 'require' function 1644// Upvalues: [1]: original 'require' function
1302// 1645//
1303static int new_require( lua_State *L ) 1646static int new_require( lua_State *L)
1304{ 1647{
1305 int rc; 1648 int rc, i;
1306 int args= lua_gettop(L); 1649 int args = lua_gettop( L);
1650 //char const *modname = luaL_checkstring( L, 1);
1307 1651
1308 STACK_GROW(L,1); 1652 STACK_GROW( L, args + 1);
1309 STACK_CHECK(L) 1653 STACK_CHECK( L)
1654
1655 lua_pushvalue( L, lua_upvalueindex(1));
1656 for( i = 1; i <= args; ++ i)
1657 lua_pushvalue( L, i);
1310 1658
1311 // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would 1659 // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would
1312 // leave us locked, blocking any future 'require' calls from other lanes. 1660 // leave us locked, blocking any future 'require' calls from other lanes.
1313 // 1661 //
1314 MUTEX_LOCK( &require_cs); 1662 MUTEX_LOCK( &require_cs);
1315 { 1663 {
1316 lua_pushvalue( L, lua_upvalueindex(1) ); 1664 rc = lua_pcall( L, args, 1 /*retvals*/, 0 /*errfunc*/ );
1317 lua_insert( L, 1 );
1318
1319 rc= lua_pcall( L, args, 1 /*retvals*/, 0 /*errfunc*/ );
1320 // 1665 //
1321 // LUA_ERRRUN / LUA_ERRMEM 1666 // LUA_ERRRUN / LUA_ERRMEM
1322 } 1667 }
1323 MUTEX_UNLOCK( &require_cs); 1668 MUTEX_UNLOCK( &require_cs);
1324 1669
1670 // the required module (or an error message) is left on the stack as returned value by original require function
1671 STACK_END( L, 1)
1672
1325 if (rc) 1673 if (rc)
1326 lua_error(L); // error message already at [-1] 1674 lua_error(L); // error message already at [-1]
1327 1675
1328 STACK_END(L,0)
1329 return 1; 1676 return 1;
1330} 1677}
1331 1678
diff --git a/src/tools.h b/src/tools.h
index a080257..1c9b00a 100644
--- a/src/tools.h
+++ b/src/tools.h
@@ -10,10 +10,10 @@
10 10
11#include <assert.h> 11#include <assert.h>
12 12
13// Note: The < -10000 test is to leave registry/global/upvalue indices untouched 13// Note: The < LUA_REGISTRYINDEX test is to leave registry/global/upvalue indices untouched
14// 14//
15#define /*int*/ STACK_ABS(L,n) \ 15#define /*int*/ STACK_ABS(L,n) \
16 ( ((n) >= 0 || (n) <= -10000) ? (n) : lua_gettop(L) +(n) +1 ) 16 ( ((n) >= 0 || (n) <= LUA_REGISTRYINDEX) ? (n) : lua_gettop(L) +(n) +1 )
17 17
18#ifdef NDEBUG 18#ifdef NDEBUG
19 #define _ASSERT_L(lua,c) /*nothing*/ 19 #define _ASSERT_L(lua,c) /*nothing*/
@@ -24,7 +24,7 @@
24 #define DEBUG() /*nothing*/ 24 #define DEBUG() /*nothing*/
25 #define DEBUGEXEC(_code) {} /*nothing*/ 25 #define DEBUGEXEC(_code) {} /*nothing*/
26#else 26#else
27 #define _ASSERT_L(lua,c) { if (!(c)) luaL_error( lua, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #c ); } 27 #define _ASSERT_L(lua,c) do { if (!(c)) luaL_error( lua, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #c ); } while( 0)
28 // 28 //
29 #define STACK_CHECK(L) { int _oldtop_##L = lua_gettop(L); 29 #define STACK_CHECK(L) { int _oldtop_##L = lua_gettop(L);
30 #define STACK_MID(L,change) { int a= lua_gettop(L)-_oldtop_##L; int b= (change); \ 30 #define STACK_MID(L,change) { int a= lua_gettop(L)-_oldtop_##L; int b= (change); \
@@ -37,7 +37,7 @@
37#endif 37#endif
38#define ASSERT_L(c) _ASSERT_L(L,c) 38#define ASSERT_L(c) _ASSERT_L(L,c)
39 39
40#define STACK_GROW(L,n) { if (!lua_checkstack(L,n)) luaL_error( L, "Cannot grow stack!" ); } 40#define STACK_GROW(L,n) do { if (!lua_checkstack(L,n)) luaL_error( L, "Cannot grow stack!" ); } while( 0)
41 41
42#define LUAG_FUNC( func_name ) static int LG_##func_name( lua_State *L ) 42#define LUAG_FUNC( func_name ) static int LG_##func_name( lua_State *L )
43 43
@@ -72,6 +72,7 @@ int luaG_inter_move( lua_State *L, lua_State *L2, uint_t n);
72extern MUTEX_T deep_lock; 72extern MUTEX_T deep_lock;
73extern MUTEX_T mtid_lock; 73extern MUTEX_T mtid_lock;
74 74
75void populate_func_lookup_table( lua_State *L, int _i, char const *_name);
75void serialize_require( lua_State *L); 76void serialize_require( lua_State *L);
76extern MUTEX_T require_cs; 77extern MUTEX_T require_cs;
77 78
diff --git a/tests/appendud.lua b/tests/appendud.lua
index eb1f768..e275a98 100644
--- a/tests/appendud.lua
+++ b/tests/appendud.lua
@@ -6,7 +6,8 @@
6-- 6--
7-- Needs Lanes >= 2.0.3 7-- Needs Lanes >= 2.0.3
8-- 8--
9require "lanes" 9local lanes = require "lanes"
10lanes.configure( 1)
10 11
11local _tab = { 12local _tab = {
12 beginupdate = function (this) print('tab.beginupdate') end; 13 beginupdate = function (this) print('tab.beginupdate') end;
diff --git a/tests/atexit.lua b/tests/atexit.lua
index fb4f34a..09fbafd 100644
--- a/tests/atexit.lua
+++ b/tests/atexit.lua
@@ -1,4 +1,5 @@
1require "lanes" 1local lanes = require "lanes"
2lanes.configure( 1)
2 3
3-- create a free-running lane 4-- create a free-running lane
4 5
diff --git a/tests/atomic.lua b/tests/atomic.lua
index a027453..f1c9428 100644
--- a/tests/atomic.lua
+++ b/tests/atomic.lua
@@ -4,7 +4,8 @@
4-- Test program for Lua Lanes 4-- Test program for Lua Lanes
5-- 5--
6 6
7require "lanes" 7local lanes = require "lanes"
8lanes.configure( 1)
8 9
9local linda= lanes.linda() 10local linda= lanes.linda()
10local key= "$" 11local key= "$"
diff --git a/tests/basic.lua b/tests/basic.lua
index 853a8de..8b07697 100644
--- a/tests/basic.lua
+++ b/tests/basic.lua
@@ -7,7 +7,8 @@
7-- - ... 7-- - ...
8-- 8--
9 9
10require "lanes" 10local lanes = require "lanes"
11lanes.configure( 1)
11require "assert" -- assert.fails() 12require "assert" -- assert.fails()
12 13
13local lanes_gen= assert( lanes.gen ) 14local lanes_gen= assert( lanes.gen )
diff --git a/tests/cyclic.lua b/tests/cyclic.lua
index 06452bd..15abc9c 100644
--- a/tests/cyclic.lua
+++ b/tests/cyclic.lua
@@ -4,7 +4,8 @@
4-- Test program for Lua Lanes 4-- Test program for Lua Lanes
5-- 5--
6 6
7require "lanes" 7local lanes = require "lanes"
8lanes.configure( 1)
8 9
9local table_concat= assert(table.concat) 10local table_concat= assert(table.concat)
10 11
diff --git a/tests/ehynes.lua b/tests/ehynes.lua
index 4cc370e..b529f98 100644
--- a/tests/ehynes.lua
+++ b/tests/ehynes.lua
@@ -1,7 +1,8 @@
1-- 1--
2-- Test from <ehynes at dharmagaia.com> 2-- Test from <ehynes at dharmagaia.com>
3-- 3--
4require 'lanes' 4local lanes = require "lanes"
5lanes.configure( 1)
5 6
6local function PRINT_FMT( fmt, ... ) 7local function PRINT_FMT( fmt, ... )
7 io.stderr:write( string.format(fmt,...).."\n" ) 8 io.stderr:write( string.format(fmt,...).."\n" )
diff --git a/tests/errhangtest.lua b/tests/errhangtest.lua
index ddc1bfb..7127326 100644
--- a/tests/errhangtest.lua
+++ b/tests/errhangtest.lua
@@ -1,4 +1,5 @@
1lanes = require('lanes') 1local lanes = require "lanes"
2lanes.configure( 1)
2 3
3local linda = lanes.linda() 4local linda = lanes.linda()
4 5
diff --git a/tests/error.lua b/tests/error.lua
index 4922846..aee4221 100644
--- a/tests/error.lua
+++ b/tests/error.lua
@@ -4,7 +4,8 @@
4-- Note: this code is supposed to end in errors; not included in 'make test' 4-- Note: this code is supposed to end in errors; not included in 'make test'
5-- 5--
6 6
7require "lanes" 7local lanes = require "lanes"
8lanes.configure( 1)
8 9
9local function lane() 10local function lane()
10 11
diff --git a/tests/fibonacci.lua b/tests/fibonacci.lua
index 667a3e9..2e0cd62 100644
--- a/tests/fibonacci.lua
+++ b/tests/fibonacci.lua
@@ -12,7 +12,8 @@
12 12
13-- Need to say it's 'local' so it can be an upvalue 13-- Need to say it's 'local' so it can be an upvalue
14-- 14--
15local lanes= require "lanes" 15local lanes = require "lanes"
16lanes.configure( 1, "NO_TIMERS")
16 17
17local function WR(str) 18local function WR(str)
18 io.stderr:write( str.."\n" ) 19 io.stderr:write( str.."\n" )
@@ -38,7 +39,9 @@ local function fib( n )
38 else 39 else
39 -- Splits into two; this task remains waiting for the results 40 -- Splits into two; this task remains waiting for the results
40 -- 41 --
41 local gen_f= lanes.gen( "io,math,debug", fib ) 42 -- note that lanes is pulled in as upvalue, so we need package library to require internals properly
43 -- (because lua51-lanes is always required internally if possible, which is necessary in that case)
44 local gen_f= lanes.gen( "package,string,io,math,debug", fib )
42 45
43 local n1=floor(n/2) +1 46 local n1=floor(n/2) +1
44 local n2=floor(n/2) -1 + n%2 47 local n2=floor(n/2) -1 + n%2
@@ -64,7 +67,22 @@ end
64-- 67--
65-- Right answers from: <http://sonic.net/~douglasi/fibo.htm> 68-- Right answers from: <http://sonic.net/~douglasi/fibo.htm>
66-- 69--
67local right= { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025, 20365011074, 32951280099, 53316291173, 86267571272, 139583862445, 225851433717, 365435296162, 591286729879, 956722026041, 1548008755920, 2504730781961, 4052739537881, 6557470319842, 10610209857723, 17167680177565, 27777890035288, 44945570212853, 72723460248141, 117669030460994, 190392490709135, 308061521170129, 498454011879264, 806515533049393, 1304969544928657, 2111485077978050, 3416454622906707, 5527939700884757, 8944394323791464, 14472334024676220, 23416728348467684, 37889062373143900, 61305790721611580, 99194853094755490, 160500643816367070, 259695496911122560, 420196140727489660, 679891637638612200, 1100087778366101900, 1779979416004714000, 2880067194370816000, 4660046610375530000, 7540113804746346000, 12200160415121877000, 19740274219868226000, 31940434634990105000, 51680708854858334000, 83621143489848440000, 135301852344706780000, 218922995834555200000 70local right=
71{
72 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711,
73 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887,
74 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437,
75 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025, 20365011074,
76 32951280099, 53316291173, 86267571272, 139583862445, 225851433717, 365435296162, 591286729879,
77 956722026041, 1548008755920, 2504730781961, 4052739537881, 6557470319842, 10610209857723,
78 17167680177565, 27777890035288, 44945570212853, 72723460248141, 117669030460994, 190392490709135,
79 308061521170129, 498454011879264, 806515533049393, 1304969544928657, 2111485077978050,
80 3416454622906707, 5527939700884757, 8944394323791464, 14472334024676220, 23416728348467684,
81 37889062373143900, 61305790721611580, 99194853094755490, 160500643816367070, 259695496911122560,
82 420196140727489660, 679891637638612200, 1100087778366101900, 1779979416004714000,
83 2880067194370816000, 4660046610375530000, 7540113804746346000, 12200160415121877000,
84 19740274219868226000, 31940434634990105000, 51680708854858334000, 83621143489848440000,
85 135301852344706780000, 218922995834555200000
68} 86}
69assert( #right==99 ) 87assert( #right==99 )
70 88
diff --git a/tests/fifo.lua b/tests/fifo.lua
index 898b04d..d8289c1 100644
--- a/tests/fifo.lua
+++ b/tests/fifo.lua
@@ -4,7 +4,8 @@
4-- Sample program for Lua Lanes 4-- Sample program for Lua Lanes
5-- 5--
6 6
7require "lanes" 7local lanes = require "lanes"
8lanes.configure( 1)
8 9
9local linda= lanes.linda() 10local linda= lanes.linda()
10local atomic_inc= lanes.genatomic( linda, "FIFO_n" ) 11local atomic_inc= lanes.genatomic( linda, "FIFO_n" )
@@ -18,8 +19,12 @@ local function FIFO()
18 return { 19 return {
19 -- Giving explicit 'nil' timeout allows numbers to be used as 'my_channel' 20 -- Giving explicit 'nil' timeout allows numbers to be used as 'my_channel'
20 -- 21 --
21 send= function(...) linda:send( nil, my_channel, ... ) end, 22 send= function(self, ...)
22 receive= function(timeout) linda:receive( timeout, my_channel ) end 23 linda:send( nil, my_channel, ... )
24 end,
25 receive = function(self, timeout)
26 linda:receive( timeout, my_channel )
27 end
23 } 28 }
24end 29end
25 30
diff --git a/tests/finalizer.lua b/tests/finalizer.lua
index c94b36d..88f51c1 100644
--- a/tests/finalizer.lua
+++ b/tests/finalizer.lua
@@ -8,7 +8,8 @@
8-- thing to do. -- AKa 22-Jan-2009 8-- thing to do. -- AKa 22-Jan-2009
9-- 9--
10 10
11require "lanes" 11local lanes = require "lanes"
12lanes.configure( 1)
12 13
13local FN= "finalizer-test.tmp" 14local FN= "finalizer-test.tmp"
14 15
diff --git a/tests/func_is_string.lua b/tests/func_is_string.lua
index 98ea62b..5d94f85 100644
--- a/tests/func_is_string.lua
+++ b/tests/func_is_string.lua
@@ -1,4 +1,5 @@
1require "lanes" 1local lanes = require "lanes"
2lanes.configure( 1)
2 3
3local options = {globals = { b = 666 }} 4local options = {globals = { b = 666 }}
4 5
diff --git a/tests/hangtest.lua b/tests/hangtest.lua
index d0bbea4..923496a 100644
--- a/tests/hangtest.lua
+++ b/tests/hangtest.lua
@@ -2,7 +2,8 @@
2-- Test case for hang on [1]s and :join()s. 2-- Test case for hang on [1]s and :join()s.
3-- 3--
4 4
5require "lanes" 5local lanes = require "lanes"
6lanes.configure( 1)
6 7
7local function ret(b) 8local function ret(b)
8 return b 9 return b
diff --git a/tests/irayo_closure.lua b/tests/irayo_closure.lua
index 3a82302..77ddbde 100644
--- a/tests/irayo_closure.lua
+++ b/tests/irayo_closure.lua
@@ -10,7 +10,8 @@ haven't investigated further.
10e.g. { globals = { data = 1, func = function() useclosurehere() end } }" 10e.g. { globals = { data = 1, func = function() useclosurehere() end } }"
11]] 11]]
12 12
13require "lanes" 13local lanes = require "lanes"
14lanes.configure( 1)
14 15
15local function testrun() 16local function testrun()
16 assert( print ) 17 assert( print )
@@ -20,7 +21,7 @@ local function testrun()
20 return true 21 return true
21end 22end
22 23
23-- When some function dereferences a global key, the asssociated global in the source state 24-- When some function dereferences a global key, the associated global in the source state
24-- isn't sent over the target lane 25-- isn't sent over the target lane
25-- therefore, the necessary functions must either be pulled as upvalues (hence locals) 26-- therefore, the necessary functions must either be pulled as upvalues (hence locals)
26-- or the globals must exist in the target lanes because the modules have been required there 27-- or the globals must exist in the target lanes because the modules have been required there
diff --git a/tests/irayo_recursive.lua b/tests/irayo_recursive.lua
index 82e5a54..3eb14b8 100644
--- a/tests/irayo_recursive.lua
+++ b/tests/irayo_recursive.lua
@@ -8,7 +8,8 @@ local function recurse()
8 print("level "..i); 8 print("level "..i);
9 if i > 10 then return "finished" end 9 if i > 10 then return "finished" end
10 10
11 require "lanes" 11 local lanes = require "lanes"
12 lanes.configure( 1, "NO_TIMERS")
12 13
13 local lane = lanes.gen( "*", { globals = { ["i"]= i + 1 } }, recurse ) () 14 local lane = lanes.gen( "*", { globals = { ["i"]= i + 1 } }, recurse ) ()
14 return lane[1] 15 return lane[1]
diff --git a/tests/keeper.lua b/tests/keeper.lua
index 5c8c23a..7b11242 100644
--- a/tests/keeper.lua
+++ b/tests/keeper.lua
@@ -4,7 +4,8 @@
4-- Test program for Lua Lanes 4-- Test program for Lua Lanes
5-- 5--
6 6
7require "lanes" 7local lanes = require "lanes"
8lanes.configure( 1)
8 9
9local function keeper(linda) 10local function keeper(linda)
10 local mt= { 11 local mt= {
diff --git a/tests/launchtest.lua b/tests/launchtest.lua
index 5e3037f..8456091 100644
--- a/tests/launchtest.lua
+++ b/tests/launchtest.lua
@@ -45,7 +45,8 @@ for k,v in pairs( argtable(...) ) do
45 end 45 end
46end 46end
47 47
48require "lanes" 48local lanes = require "lanes"
49lanes.configure( 1)
49 50
50local g= lanes.gen( LIBS, function(i) 51local g= lanes.gen( LIBS, function(i)
51 --io.stderr:write( i.."\t" ) 52 --io.stderr:write( i.."\t" )
diff --git a/tests/linda_perf.lua b/tests/linda_perf.lua
index bad0024..12159da 100644
--- a/tests/linda_perf.lua
+++ b/tests/linda_perf.lua
@@ -1,4 +1,5 @@
1require "lanes" 1local lanes = require "lanes"
2lanes.configure( 1)
2 3
3-- this lane eats items in the linda one by one 4-- this lane eats items in the linda one by one
4local eater = function( l, loop) 5local eater = function( l, loop)
diff --git a/tests/objects.lua b/tests/objects.lua
index 8f56a5f..29e16d8 100644
--- a/tests/objects.lua
+++ b/tests/objects.lua
@@ -4,7 +4,8 @@
4-- Tests that objects (metatables) can be passed between lanes. 4-- Tests that objects (metatables) can be passed between lanes.
5-- 5--
6 6
7require "lanes" 7local lanes = require "lanes"
8lanes.configure( 1)
8 9
9local linda= lanes.linda() 10local linda= lanes.linda()
10 11
diff --git a/tests/perftest.lua b/tests/perftest.lua
index 8ce1b3c..3d9fde0 100644
--- a/tests/perftest.lua
+++ b/tests/perftest.lua
@@ -27,7 +27,8 @@
27local MSYS= os.getenv("OSTYPE")=="msys" 27local MSYS= os.getenv("OSTYPE")=="msys"
28 28
29 29
30require "lanes" 30local lanes = require "lanes"
31lanes.configure( 1)
31 32
32local m= require "argtable" 33local m= require "argtable"
33local argtable= assert( m.argtable ) 34local argtable= assert( m.argtable )
diff --git a/tests/protectproxy.lua b/tests/protectproxy.lua
index 57ca831..363dbf5 100644
--- a/tests/protectproxy.lua
+++ b/tests/protectproxy.lua
@@ -1,4 +1,5 @@
1require "lanes" 1local lanes = require "lanes"
2lanes.configure( 1)
2 3
3local body = function( param) 4local body = function( param)
4 print ( "lane body: " .. param) 5 print ( "lane body: " .. param)
diff --git a/tests/recursive.lua b/tests/recursive.lua
index 49c03d3..ad32df8 100644
--- a/tests/recursive.lua
+++ b/tests/recursive.lua
@@ -11,7 +11,9 @@ local function func( depth )
11 return "done!" 11 return "done!"
12 end 12 end
13 13
14 require "lanes" 14 local lanes = require "lanes"
15 -- lanes.configure() is gone after we call it...
16 lanes.configure( 1)
15 local lane= lanes.gen("*", func)( depth+1 ) 17 local lane= lanes.gen("*", func)( depth+1 )
16 return lane[1] 18 return lane[1]
17end 19end
diff --git a/tests/require.lua b/tests/require.lua
index 2cfe780..96717dc 100644
--- a/tests/require.lua
+++ b/tests/require.lua
@@ -3,7 +3,8 @@
3-- 3--
4-- Test that 'require' works from sublanes 4-- Test that 'require' works from sublanes
5-- 5--
6require 'lanes' 6local lanes = require "lanes"
7lanes.configure( 1)
7 8
8local function a_lane() 9local function a_lane()
9 -- To require 'math' we still actually need to have it initialized for 10 -- To require 'math' we still actually need to have it initialized for
diff --git a/tests/timer.lua b/tests/timer.lua
index e95f326..f0027d7 100644
--- a/tests/timer.lua
+++ b/tests/timer.lua
@@ -8,7 +8,8 @@
8io.stderr:setvbuf "no" 8io.stderr:setvbuf "no"
9 9
10 10
11require "lanes" 11local lanes = require "lanes"
12lanes.configure( 1)
12 13
13local linda= lanes.linda() 14local linda= lanes.linda()
14 15