aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ABOUT4
-rw-r--r--CHANGES43
-rw-r--r--COPYRIGHT1
-rw-r--r--Makefile16
-rw-r--r--docs/index.html214
-rw-r--r--lanes-3.0-beta.rockspec97
-rw-r--r--lanes-3.0.0-1.rockspec97
-rw-r--r--lanes-3.1.0-1.rockspec91
-rw-r--r--setup-vc.cmd15
-rw-r--r--src/Makefile2
-rw-r--r--src/keeper.c152
-rw-r--r--src/keeper.h4
-rw-r--r--src/keeper.lua222
-rw-r--r--src/lanes.c696
-rw-r--r--src/lanes.lua87
-rw-r--r--src/threading.c62
-rw-r--r--src/threading.h55
-rw-r--r--src/tools.c671
-rw-r--r--src/tools.h13
-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.lua224
-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
43 files changed, 2268 insertions, 599 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 58b6061..b2fe499 100644
--- a/CHANGES
+++ b/CHANGES
@@ -3,6 +3,49 @@ CHANGES:
3 3
4CHANGE X: 4CHANGE X:
5 5
6CHANGE 35 BGe 17-Feb-2012
7 * changed lanes.configure signature to receive a table instead of individual parameters
8 * added support for an on_state_create callback called to load custom functions in a state in addition to the base libraries
9
10CHANGE 34 BGe 14-Nov-2011
11 * removed packagepath and packagecpath options, replaced by a package table, whose fields path, cpath, loaders, preload are transfered
12 * code cleanup to facilitate transition between WIN32 and PTHREAD impleentations
13 * tentative fix for desinit crashes when free running lanes are killed at process shutdown
14
15CHANGE 33 BGe 5-Nov-2011: Lanes version 3.0-beta
16 * process exit change: close everything at GC when main state closes, not when atexit() handlers are processed
17 * Lua 5.2-style module:
18 * module() is no longer used to implement lanes.lua
19 * a global "lanes" variable is no longer created when the module is required
20 * the Lanes module table is returned instead
21 * Lanes must be initialized before used:
22 * the first occurence of 'require "lanes"' produces a minimal interface that only contains a configure() function
23 * the remainder of the interface is made available once this function is called
24 * subsequent calls to configure() do nothing
25 * configure() controls the number of keeper states and the startup of timers
26 * LuaJIT 2 compatibility
27 * non-Lua functions are no longer copied by creating a C closure from a C pointer, but through 2-way lookup tables
28 * 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
29 * introduces a change in configuration .globals management: contents are copied *after* std libs are loaded
30 * new .required configuration entry to list modules that must be require()'ed before lane body is transferred
31 * lane:cancel() wakes up waiting lindas like what is done at lane shutdown
32
33CHANGE 32 BGe 14-May-2011
34 * raise an error when linda:send() has nothing to send
35
36CHANGE 31 BGe 17-Apr-2011
37 * linda uses a fast FIFO implementation to speed up data exchanges
38 * new linda:count() method
39 * new linda batched data read mode
40 * proper key type check in all linda methods
41 * fix setup-vc.cmd to support Visual Studio 2010 and Windows 7 64 bits
42 * bugfix: release keeper state mutex at desinit
43
44CHANGE 30 BGe 30-Mar-2011
45 * linda honors __tostring and __concat
46 * new accessor linda:keys(), to retrieve the list of keys with pending data inside a linda
47 * new lanes options packagepath and packagecpath, in case one needs to set them differently than the default
48
6CHANGE 29 BGe 1-Mar-2011 49CHANGE 29 BGe 1-Mar-2011
7 fixed potential crash at application shutdown when calling lua_close() on a killed thread's VM. 50 fixed potential crash at application shutdown when calling lua_close() on a killed thread's VM.
8 exposed cancel_test() in the lanes to enable manual testing for cancellation requests. 51 exposed cancel_test() in the lanes to enable manual testing for cancellation requests.
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 ddd8675..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)
@@ -85,11 +85,12 @@ test:
85 $(MAKE) recursive 85 $(MAKE) recursive
86 $(MAKE) func_is_string 86 $(MAKE) func_is_string
87 $(MAKE) atexit 87 $(MAKE) atexit
88 $(MAKE) linda_perf
88 89
89basic: tests/basic.lua $(_TARGET_SO) 90basic: tests/basic.lua $(_TARGET_SO)
90 $(_PREFIX) $(LUA) $< 91 $(_PREFIX) $(LUA) $<
91 92
92# 93#
93# This tries to show out a bug which happens in lane cleanup (multicore CPU's only) 94# This tries to show out a bug which happens in lane cleanup (multicore CPU's only)
94# 95#
95REP_ARGS=-llanes -e "print'say aaa'; for i=1,10 do print(i) end" 96REP_ARGS=-llanes -e "print'say aaa'; for i=1,10 do print(i) end"
@@ -155,6 +156,9 @@ appendud: tests/appendud.lua $(_TARGET_SO)
155func_is_string: tests/func_is_string.lua $(_TARGET_SO) 156func_is_string: tests/func_is_string.lua $(_TARGET_SO)
156 $(_PREFIX) $(LUA) $< 157 $(_PREFIX) $(LUA) $<
157 158
159linda_perf: tests/linda_perf.lua $(_TARGET_SO)
160 $(_PREFIX) $(LUA) $<
161
158atexit: tests/atexit.lua $(_TARGET_SO) 162atexit: tests/atexit.lua $(_TARGET_SO)
159 $(_PREFIX) $(LUA) $< 163 $(_PREFIX) $(LUA) $<
160 164
@@ -190,7 +194,7 @@ LUA_LIBDIR=$(DESTDIR)/lib/lua/5.1
190LUA_SHAREDIR=$(DESTDIR)/share/lua/5.1 194LUA_SHAREDIR=$(DESTDIR)/share/lua/5.1
191 195
192# 196#
193# AKa 17-Oct: changed to use 'install -m 644' and 'cp -p' 197# AKa 17-Oct: changed to use 'install -m 644' and 'cp -p'
194# 198#
195install: $(_TARGET_SO) src/lanes.lua 199install: $(_TARGET_SO) src/lanes.lua
196 mkdir -p $(LUA_LIBDIR) $(LUA_SHAREDIR) 200 mkdir -p $(LUA_LIBDIR) $(LUA_SHAREDIR)
@@ -211,7 +215,7 @@ tar tgz:
211ifeq "$(VERSION)" "" 215ifeq "$(VERSION)" ""
212 echo "Usage: make tar VERSION=x.x"; false 216 echo "Usage: make tar VERSION=x.x"; false
213else 217else
214 $(MAKE) clean 218 $(MAKE) clean
215 -rm -rf $(MODULE)-$(VERSION) 219 -rm -rf $(MODULE)-$(VERSION)
216 mkdir $(MODULE)-$(VERSION) 220 mkdir $(MODULE)-$(VERSION)
217 tar c * --exclude=.svn --exclude=.DS_Store --exclude="_*" \ 221 tar c * --exclude=.svn --exclude=.DS_Store --exclude="_*" \
@@ -223,8 +227,8 @@ else
223 rm -rf $(MODULE)-$(VERSION) 227 rm -rf $(MODULE)-$(VERSION)
224 md5sum $(MODULE)-$(VERSION).tgz 228 md5sum $(MODULE)-$(VERSION).tgz
225endif 229endif
226 230
227 231
228#--- Undocumented --- 232#--- Undocumented ---
229# 233#
230 234
diff --git a/docs/index.html b/docs/index.html
index ba25515..72e91ba 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 17-Feb-11, and applies to version 3.1.0
60</font></p> 60</font></p>
61 61
62</center> 62</center>
@@ -162,6 +162,69 @@ 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
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".configure()
177 </pre>
178 </table>
179
180 <p>
181 Starting with version 3.0-beta, requiring the module follows Lua 5.2 rules:
182 the module is not available under the global name "lanes", but has to be accessed
183 through require's return value.
184 After lanes is required, it is necessary to call <tt>lanes.configure()</tt>, which is the
185 only function exposed by the module at this point. Calling <tt>configure()</tt> will
186 perform one-time initializations and make the rest of the API available.
187 At the same time, <tt>configure()</tt> itself will be replaced by another function that
188 raises an error if called again with differing arguments.
189 </p>
190
191 <p>
192 <table border="1" bgcolor="#E0E0FF" cellpadding="10">
193 <tr>
194 <td>
195 <code>
196 lanes.configure( [opt_tbl])
197 </code>
198 </table>
199 </p>
200 <p>
201 <tt>lanes.configure</tt> accepts an optional options table as sole argument.
202 <table>
203 <tr valign=top><td width=40></td><td>
204 <code>.nb_keepers</code> <br/><nobr>N</nobr></td><td width=40></td>
205 <td>
206 Controls the number of keeper states used internally by lindas to transfer data between lanes. (see below). Default is 1.
207 </td></tr>
208
209 <tr valign=top><td/><td>
210 <code>.with_timers</code> <br/>nil/false/anything</td><td/>
211 <td>
212 If equal to <tt>false</tt>, Lanes doesn't start the timer service,
213 and the associated API will be absent from the interface (see below).
214 Any other value (including <tt>nil</tt>), starts the timer service.
215 </td></tr>
216
217 <tr valign="top">
218 <td/>
219 <td>
220 <code>.on_create_state</code> <br/>C function/nil
221 </td><td/>
222 <td>
223 If provided, will be called in every created Lua state (keepers and lanes) right after it is created, and *before* any library is loaded.
224 That way, all C functions it loads in the state can be added to the function lookup database.
225 </td>
226 </tr>
227 </table>
165<h2 id="creation">Creation</h2> 228<h2 id="creation">Creation</h2>
166 229
167<p>The following sample shows preparing a function for parallel calling, and 230<p>The following sample shows preparing a function for parallel calling, and
@@ -172,7 +235,8 @@ joins the threads, waiting for any results not already there.
172 235
173<table border=1 bgcolor="#FFFFE0" width=500><tr><td> 236<table border=1 bgcolor="#FFFFE0" width=500><tr><td>
174<pre> 237<pre>
175 require "lanes" 238 local lanes = require "lanes"
239 lanes.configure()
176 240
177 f= lanes.gen( function(n) return 2*n end ) 241 f= lanes.gen( function(n) return 2*n end )
178 a= f(1) 242 a= f(1)
@@ -189,14 +253,14 @@ joins the threads, waiting for any results not already there.
189 lane_h= func( ... )</code> 253 lane_h= func( ... )</code>
190</table> 254</table>
191</p> 255</p>
192</p><p> 256<p>
193 The function returned by <tt>lanes.gen()</tt> is a "generator" for 257 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, 258 launching any number of lanes. They will share code, options, initial globals,
195 but the particular arguments may vary. Only calling the generator function 259 but the particular arguments may vary. Only calling the generator function
196 actually launches a lane, and provides a handle for controlling it. 260 actually launches a lane, and provides a handle for controlling it.
197 Alternatively, <tt>lane_func</tt> may be a string, in which case it will be compiled 261 Alternatively, <tt>lane_func</tt> may be a string, in which case it will be compiled
198 in the lane. This is to be able to launch lanes whith LuaJIT, 262 in the lane. This was to be able to launch lanes with older versions of LuaJIT,
199 which does not support lua_dump, used internally to transfer functions to the lane. 263 which didn't not support lua_dump, used internally to transfer functions to the lane.
200<!-- 264<!--
201</p> 265</p>
202<p>This prepares <tt>lane_func</tt> to be called in parallel. It does not yet start 266<p>This prepares <tt>lane_func</tt> to be called in parallel. It does not yet start
@@ -212,19 +276,21 @@ also in the new lanes.
212 <code>libs_str</code> defines the standard libraries made available to the 276 <code>libs_str</code> defines the standard libraries made available to the
213 new Lua state: 277 new Lua state:
214 <table> 278 <table>
215 <tr><td/><td>(nothing)</td><td>no standard libraries (default)</td></tr> 279 <tr><td width=40></td><td>(nothing)</td><td width=40></td><td>no standard libraries (default)</td></tr>
216 <tr><td width=40><td><tt>"base"</tt> or <tt>""</tt></td> 280 <tr><td/>
217 <td>root level names, <tt>print</tt>, <tt>assert</tt>, <tt>unpack</tt> etc.</td></tr> 281 <td><tt>"base"</tt> or <tt>""</tt></td><td/>
218 <tr><td/><td><tt>"coroutine"</tt></td><td><tt>coroutine.*</tt> namespace <font size="-1">(part of base in Lua 5.1)</font></td></tr> 282 <td>root level names, <tt>print</tt>, <tt>assert</tt>, <tt>unpack</tt> etc.</td>
219 <tr><td/><td><tt>"debug"</tt></td><td><tt>debug.*</tt> namespace</td></tr> 283 </tr>
220 <tr><td/><td><tt>"io"</tt></td><td><tt>io.*</tt> namespace</td></tr> 284 <tr><td/><td><tt>"coroutine"</tt></td><td/><td><tt>coroutine.*</tt> namespace <font size="-1">(part of base in Lua 5.1)</font></td></tr>
221 <tr><td/><td><tt>"math"</tt></td><td><tt>math.*</tt> namespace</td></tr> 285 <tr><td/><td><tt>"debug"</tt></td><td/><td><tt>debug.*</tt> namespace</td></tr>
222 <tr><td/><td><tt>"os"</tt></td><td><tt>os.*</tt> namespace</td></tr> 286 <tr><td/><td><tt>"io"</tt></td><td/><td><tt>io.*</tt> namespace</td></tr>
223 <tr><td/><td><tt>"package"</tt></td><td><tt>package.*</tt> namespace and <tt>require</tt></td></tr> 287 <tr><td/><td><tt>"math"</tt></td><td/><td><tt>math.*</tt> namespace</td></tr>
224 <tr><td/><td><tt>"string"</tt></td><td><tt>string.*</tt> namespace</td></tr> 288 <tr><td/><td><tt>"os"</tt></td><td/><td><tt>os.*</tt> namespace</td></tr>
225 <tr><td/><td><tt>"table"</tt></td><td><tt>table.*</tt> namespace</td></tr> 289 <tr><td/><td><tt>"package"</tt></td><td/><td><tt>package.*</tt> namespace and <tt>require</tt></td></tr>
290 <tr><td/><td><tt>"string"</tt></td><td/><td><tt>string.*</tt> namespace</td></tr>
291 <tr><td/><td><tt>"table"</tt></td><td/><td><tt>table.*</tt> namespace</td></tr>
226 <br/> 292 <br/>
227 <tr><td/><td><tt>"*"</tt></td><td>all standard libraries</td></tr> 293 <tr><td/><td><tt>"*"</tt></td><td/><td>all standard libraries</td></tr>
228 </table> 294 </table>
229 295
230</p><p> 296</p><p>
@@ -236,9 +302,9 @@ also in the new lanes.
236 lanes are run: 302 lanes are run:
237</p><p> 303</p><p>
238 <table> 304 <table>
239 <tr valign=top><td/><td> 305 <tr valign=top><td width=40></td><td>
240 <code>.cancelstep</code> <br/><nobr>N / true</nobr></td> 306 <code>.cancelstep</code> <br/><nobr>N / true</nobr></td>
241 <td> 307 <td width=40></td><td>
242 By default, lanes are only cancellable when they <u>enter</u> a pending 308 By default, lanes are only cancellable when they <u>enter</u> a pending
243 <tt>:receive()</tt> or <tt>:send()</tt> call. 309 <tt>:receive()</tt> or <tt>:send()</tt> call.
244 With this option, one can set cancellation check to occur every <tt>N</tt> 310 With this option, one can set cancellation check to occur every <tt>N</tt>
@@ -247,7 +313,7 @@ also in the new lanes.
247 </td></tr> 313 </td></tr>
248 314
249 <tr valign=top><td/><td> 315 <tr valign=top><td/><td>
250 <code>.globals</code> <br/>globals_tbl</td> 316 <code>.globals</code> <br/>globals_tbl</td><td/>
251 <td> 317 <td>
252 Sets the globals table for the launched threads. This can be used for giving 318 Sets the globals table for the launched threads. This can be used for giving
253 them constants. 319 them constants.
@@ -256,13 +322,40 @@ also in the new lanes.
256 modifying one will only affect the particular lane. 322 modifying one will only affect the particular lane.
257 </td></tr> 323 </td></tr>
258 324
325 <tr valign="top">
326 <td/>
327 <td>
328 <code>.required</code> <br/>modules_tbl
329 </td><td/>
330 <td>
331 Lists modules that have to be required in order to be able to trasnfer
332 functions they exposed. Starting with Lanes 3.0-beta, non-Lua functions are
333 no longer copied by recreating a C closure from a C pointer, but are searched
334 in lookup tables. These tables are built from the modules listed here. <tt>required</tt>
335 must be a list of strings, each one being the name of a module to be required.
336 </td>
337 </tr>
338
259 <tr valign=top><td width=40><td> 339 <tr valign=top><td width=40><td>
260 <code>.priority</code> <br/><nobr>-2..+2</nobr></td> 340 <code>.priority</code> <br/><nobr>-2..+2</nobr></td><td/>
261 <td>The priority of lanes generated. -2 is lowest, +2 is highest. 341 <td>The priority of lanes generated. -2 is lowest, +2 is highest.
262 <br> 342 <br>
263 Implementation and dependability of priorities varies 343 Implementation and dependability of priorities varies
264 by platform. Especially Linux kernel 2.6 is not supporting priorities in user mode. 344 by platform. Especially Linux kernel 2.6 is not supporting priorities in user mode.
265 </td></tr> 345 </td></tr>
346
347 <tr valign="top">
348 <td width="40">
349 <td>
350 <code>.package</code><br/>
351 </td>
352 <td><td/>
353 <code>package</code> contents overrides, if needed.
354 Specifying it when <code>libs_str</code> doesn't cause the <code>package</code> library to be loaded will generate an error.
355 If not specified, the created lane will receive the current values of <tt>package</tt>. Only path, cpath, preload and loaders are transfered.
356 <br>
357 </td>
358 </tr>
266 </table> 359 </table>
267 <p>Each lane also gets a function <tt>set_debug_threadname()</tt> that it can use anytime to do as the name says. 360 <p>Each lane also gets a function <tt>set_debug_threadname()</tt> that it can use anytime to do as the name says.
268Supported debuggers are Microsoft Visual Studio (for the C side) and Decoda (for the Lua side). 361Supported debuggers are Microsoft Visual Studio (for the C side) and Decoda (for the Lua side).
@@ -394,8 +487,9 @@ OS thread running the lane is forcefully killed. This means no GC, and should
394generally be the last resort. 487generally be the last resort.
395</p> 488</p>
396<p>Cancellation is tested <u>before</u> going to sleep in <tt>receive()</tt> or <tt>send()</tt> calls 489<p>Cancellation is tested <u>before</u> going to sleep in <tt>receive()</tt> or <tt>send()</tt> calls
397and after executing <tt>cancelstep</tt> Lua statements. A currently pending <tt>receive()</tt> 490and after executing <tt>cancelstep</tt> Lua statements. Starting with version 3.0-beta, a pending <tt>receive()</tt>
398or <tt>send()</tt> call is currently not awakened, and may be a reason for a non-detected cancel. 491or <tt>send()</tt> call is awakened. This means the execution of the lane will resume although the operation has
492not completed, to give the lane a chance to detect cancellation. The code should be able to handle this situation appropriately if required.
399It is also possible to manually test for cancel requests with <tt>cancel_test()</tt>. 493It is also possible to manually test for cancel requests with <tt>cancel_test()</tt>.
400</p> 494</p>
401 495
@@ -470,7 +564,7 @@ level locking is required; each Linda operation is atomic.
470<p>Characteristics of the Lanes implementation of Lindas are: 564<p>Characteristics of the Lanes implementation of Lindas are:
471 565
472<ul> 566<ul>
473 <li>keys can be of number, string or boolean type 567 <li>keys can be of boolean, number, string or light userdata type
474 </li> 568 </li>
475 <li>values can be any type supported by inter-state copying (same limits 569 <li>values can be any type supported by inter-state copying (same limits
476 as for function parameters and upvalues) 570 as for function parameters and upvalues)
@@ -548,6 +642,23 @@ Writing to a slot overwrites existing value, and clears any possible queued
548entries. Table access and <tt>send</tt>/<tt>receive</tt> can be used together; 642entries. Table access and <tt>send</tt>/<tt>receive</tt> can be used together;
549reading a slot essentially peeks the next outcoming value of a queue. 643reading a slot essentially peeks the next outcoming value of a queue.
550</p> 644</p>
645 <p>
646
647 <table border="1" bgcolor="#E0E0FF" cellpadding="10">
648 <tr>
649 <td>
650 <code>[val]= linda_h:count( [key[,...]])</code>
651 </table>
652
653 </p>
654 <p>
655 Returns some information about the contents of the linda.
656 <br/>If no key is specified, and the linda is empty, returns nothing.
657 <br/>If no key is specified, and the linda is not empty, returns a table of key/count pairs that counts the number of items in each
658 of the exiting keys of the linda. This count can be 0 if the key has been used but is empty.
659 <br/>If a single key is specified, returns the number of pending items, or nothing if the key is unknown.
660 <br/>If more than one key is specified, return a table of key/count pairs for the known keys.
661 </p>
551 662
552<!-- 663<!--
553<p> 664<p>
@@ -589,6 +700,8 @@ you want to use several?
589 <li>Performance. Changing any slot in a Linda causes all pending threads 700 <li>Performance. Changing any slot in a Linda causes all pending threads
590 for that Linda to be momentarily awakened (at least in the C level). 701 for that Linda to be momentarily awakened (at least in the C level).
591 This can degrade performance due to unnecessary OS level context switches. 702 This can degrade performance due to unnecessary OS level context switches.
703 The more keeper states you declared with <tt>lanes.configure()</tt> the less
704 this should be a problem.
592 </li> 705 </li>
593</ul> 706</ul>
594 707
@@ -610,6 +723,10 @@ events to a common Linda, but... :).</font>
610</table> 723</table>
611 724
612<p> 725<p>
726 Timers are implemented as a lane. They can be disabled by passing <tt>"NO_TIMERS"</tt>
727 to <tt>lanes.configure()</tt>.
728 </p>
729<p>
613Timers can be run once, or in a reoccurring fashion (<tt>period_secs > 0</tt>). 730Timers can be run once, or in a reoccurring fashion (<tt>period_secs > 0</tt>).
614The first occurrence can be given either as a date or as a relative delay in seconds. 731The first occurrence can be given either as a date or as a relative delay in seconds.
615The <tt>date</tt> table is like what <tt>os.date("*t")</tt> returns, in the 732The <tt>date</tt> table is like what <tt>os.date("*t")</tt> returns, in the
@@ -625,7 +742,8 @@ A timer can be stopped simply by <tt>first_secs=0</tt> and no period.
625 742
626<table border=1 bgcolor="#FFFFE0" width=500><tr><td> 743<table border=1 bgcolor="#FFFFE0" width=500><tr><td>
627<pre> 744<pre>
628 require "lanes" 745 local lanes = require "lanes"
746 lanes.configure( 1)
629 747
630 local linda= lanes.linda() 748 local linda= lanes.linda()
631 749
@@ -960,9 +1078,49 @@ its actual value.
960<h2 id="changes">Change log</h2> 1078<h2 id="changes">Change log</h2>
961 1079
962<p> 1080<p>
1081 Feb-2012
1082 <ul>
1083 <li>Added support for an on_state_create callback invoked on a pristine Lua state created by Lanes.</li>
1084 <li>This required a change in the <tt>lanes.configure()</tt> signature, hence the minor version bump.</li>
1085 </ul>
963 1086
964Mar-2011 (2.1.0) 1087
965<ul> 1088 Nov-2011
1089 <ul>
1090 <li>process exit change: close everything at GC when main state closes, not when atexit() handlers are processed</li>
1091 <li>Lua 5.2-style module:</li>
1092 <ul>
1093 <li>module() is no longer used to implement lanes.lua</li>
1094 <li>a global "lanes" variable is no longer created when the module is required</li>
1095 <li>the Lanes module table is returned instead</li>
1096 </ul>
1097 <li>Lanes must be initialized before used:</li>
1098 <ul>
1099 <li>the first occurence of 'require "lanes"' produces a minimal interface that only contains a configure() function</li>
1100 <li>the remainder of the interface is made available once this function is called</li>
1101 <li>subsequent calls to configure() do nothing</li>
1102 <li>configure() controls the number of keeper states and the startup of timers</li>
1103 </ul>
1104 <li>* LuaJIT 2 compatibility</li>
1105 <ul>
1106 <li>non-Lua functions are no longer copied by creating a C closure from a C pointer, but through 2-way lookup tables</li>
1107 <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>
1108 <li>introduces a change in configuration .globals management: contents are copied *after* std libs are loaded</li>
1109 <li>new .required configuration entry to list modules that must be require()'ed before lane body is transferred</li>
1110 </ul>
1111 <li>lane:cancel() wakes up waiting lindas like what is done at lane shutdown</li>
1112 <li>removed packagepath and packagecpath options, replaced by a package table, whose fields path, cpath, loaders, preload are transfered</li>
1113 </ul>
1114
1115 Mar-2011 (not yet versioned)
1116 <ul>
1117 <li>linda honors __tostring and __concat</li>
1118 <li>new accessor linda:count(), to get info about data stored inside a linda.</li>
1119 <li>new lanes options packagepath and packagecpath, in case one needs to set them differently than the default.</li>
1120 </ul>
1121
1122 Mar-2011 (2.1.0)
1123 <ul>
966 <li>fixed potential crash at application shutdown when calling lua_close() on a killed thread's VM.</li> 1124 <li>fixed potential crash at application shutdown when calling lua_close() on a killed thread's VM.</li>
967 <li>exposed cancel_test() in the lanes to enable manual testing for cancellation requests.</li> 1125 <li>exposed cancel_test() in the lanes to enable manual testing for cancellation requests.</li>
968 <li>removed kludgy {globals={threadName}} support, replaced with a new function set_debug_threadname().</li> 1126 <li>removed kludgy {globals={threadName}} support, replaced with a new function set_debug_threadname().</li>
diff --git a/lanes-3.0-beta.rockspec b/lanes-3.0-beta.rockspec
new file mode 100644
index 0000000..8f10ef4
--- /dev/null
+++ b/lanes-3.0-beta.rockspec
@@ -0,0 +1,97 @@
1--
2-- Lanes rockspec
3--
4-- Ref:
5-- <http://luarocks.org/en/Rockspec_format>
6--
7-- History:
8-- BGe 1-Mar-2011: 2.1.0
9-- BGe 27-Jan-2011: 2.0.11 (see CHANGES)
10-- AKa 1-Sep-2008: 2.0-2 (NOT sent to list): fixed VC++ not finding DLL issue
11-- AKa 20-Aug-2008: 2.0-1 sent to luarocks-developers
12--
13
14package = "Lanes"
15
16version = "3.0-beta"
17
18source= {
19 url= "git://github.com/LuaLanes/lanes.git",
20 branch= "v3.0-beta"
21}
22
23description = {
24 summary= "Multithreading support for Lua",
25 detailed= [[
26 Lua Lanes is a portable, message passing multithreading library
27 providing the possibility to run multiple Lua states in parallel.
28 ]],
29 license= "MIT/X11",
30 homepage="http://kotisivu.dnainternet.net/askok/lanes/",
31 maintainer="Benoit Germain <bnt.germain@gmail.com>"
32}
33
34-- Q: What is the difference of "windows" and "win32"? Seems there is none;
35-- so should we list either one or both?
36--
37supported_platforms= { "win32",
38 "macosx",
39 "linux",
40 "freebsd", -- TBD: not tested
41 "msys", -- TBD: not supported by LuaRocks 1.0 (or is it?)
42}
43
44dependencies= {
45 "lua >= 5.1, <= 5.2",
46}
47
48--
49-- Non-Win32: build using the Makefile
50-- Win32: build using 'make-vc.cmd' and "manual" copy of products
51--
52-- TBD: How is MSYS treated? We'd like (really) it to use the Makefile.
53-- It should be a target like "cygwin", not defining "windows".
54-- "windows" should actually guarantee Visual C++ as the compiler.
55--
56-- Q: Does "win32" guarantee we have Visual C++ 2005/2008 command line tools?
57--
58-- Note: Cannot use the simple "module" build type, because we need to precompile
59-- 'src/keeper.lua' -> keeper.lch and bake it into lanes.c.
60--
61build = {
62
63 -- Win32 (Visual C++) uses 'make-vc.cmd' for building
64 --
65 platforms= {
66 windows= {
67 type= "command",
68 build_command= "make-vc.cmd",
69 install= {
70 lua = { "src/lanes.lua" },
71 lib = { "lua51-lanes.dll" }
72 }
73 }
74 },
75
76 -- Other platforms use the Makefile
77 --
78 -- LuaRocks defines CFLAGS, LIBFLAG and LUA_INCDIR for 'make rock',
79 -- defines LIBDIR, LUADIR for 'make install'
80 --
81 -- Ref: <http://www.luarocks.org/en/Paths_and_external_dependencies>
82 --
83 type = "make",
84
85 build_target = "rock",
86 build_variables= {
87 CFLAGS= "$(CFLAGS) -I$(LUA_INCDIR)",
88 LIBFLAG= "$(LIBFLAG)",
89 },
90
91 install_target = "install",
92 install_variables= {
93 LUA_LIBDIR= "$(LIBDIR)",
94 LUA_SHAREDIR= "$(LUADIR)",
95 }
96}
97
diff --git a/lanes-3.0.0-1.rockspec b/lanes-3.0.0-1.rockspec
new file mode 100644
index 0000000..1e090d5
--- /dev/null
+++ b/lanes-3.0.0-1.rockspec
@@ -0,0 +1,97 @@
1--
2-- Lanes rockspec
3--
4-- Ref:
5-- <http://luarocks.org/en/Rockspec_format>
6--
7-- History:
8-- BGe 1-Mar-2011: 2.1.0
9-- BGe 27-Jan-2011: 2.0.11 (see CHANGES)
10-- AKa 1-Sep-2008: 2.0-2 (NOT sent to list): fixed VC++ not finding DLL issue
11-- AKa 20-Aug-2008: 2.0-1 sent to luarocks-developers
12--
13
14package = "Lanes"
15
16version = "3.0.0-1"
17
18source= {
19 url= "git://github.com/LuaLanes/lanes.git",
20 branch= "v3.0.0"
21}
22
23description = {
24 summary= "Multithreading support for Lua",
25 detailed= [[
26 Lua Lanes is a portable, message passing multithreading library
27 providing the possibility to run multiple Lua states in parallel.
28 ]],
29 license= "MIT/X11",
30 homepage="http://kotisivu.dnainternet.net/askok/lanes/",
31 maintainer="Benoit Germain <bnt.germain@gmail.com>"
32}
33
34-- Q: What is the difference of "windows" and "win32"? Seems there is none;
35-- so should we list either one or both?
36--
37supported_platforms= { "win32",
38 "macosx",
39 "linux",
40 "freebsd", -- TBD: not tested
41 "msys", -- TBD: not supported by LuaRocks 1.0 (or is it?)
42}
43
44dependencies= {
45 "lua >= 5.1, <= 5.2",
46}
47
48--
49-- Non-Win32: build using the Makefile
50-- Win32: build using 'make-vc.cmd' and "manual" copy of products
51--
52-- TBD: How is MSYS treated? We'd like (really) it to use the Makefile.
53-- It should be a target like "cygwin", not defining "windows".
54-- "windows" should actually guarantee Visual C++ as the compiler.
55--
56-- Q: Does "win32" guarantee we have Visual C++ 2005/2008 command line tools?
57--
58-- Note: Cannot use the simple "module" build type, because we need to precompile
59-- 'src/keeper.lua' -> keeper.lch and bake it into lanes.c.
60--
61build = {
62
63 -- Win32 (Visual C++) uses 'make-vc.cmd' for building
64 --
65 platforms= {
66 windows= {
67 type= "command",
68 build_command= "make-vc.cmd",
69 install= {
70 lua = { "src/lanes.lua" },
71 lib = { "lua51-lanes.dll" }
72 }
73 }
74 },
75
76 -- Other platforms use the Makefile
77 --
78 -- LuaRocks defines CFLAGS, LIBFLAG and LUA_INCDIR for 'make rock',
79 -- defines LIBDIR, LUADIR for 'make install'
80 --
81 -- Ref: <http://www.luarocks.org/en/Paths_and_external_dependencies>
82 --
83 type = "make",
84
85 build_target = "rock",
86 build_variables= {
87 CFLAGS= "$(CFLAGS) -I$(LUA_INCDIR)",
88 LIBFLAG= "$(LIBFLAG)",
89 },
90
91 install_target = "install",
92 install_variables= {
93 LUA_LIBDIR= "$(LIBDIR)",
94 LUA_SHAREDIR= "$(LUADIR)",
95 }
96}
97
diff --git a/lanes-3.1.0-1.rockspec b/lanes-3.1.0-1.rockspec
new file mode 100644
index 0000000..98bbbf5
--- /dev/null
+++ b/lanes-3.1.0-1.rockspec
@@ -0,0 +1,91 @@
1--
2-- Lanes rockspec
3--
4-- Ref:
5-- <http://luarocks.org/en/Rockspec_format>
6--
7
8package = "Lanes"
9
10version = "3.1.0-1"
11
12source= {
13 url= "git://github.com/LuaLanes/lanes.git",
14 branch= "v3.1.0"
15}
16
17description = {
18 summary= "Multithreading support for Lua",
19 detailed= [[
20 Lua Lanes is a portable, message passing multithreading library
21 providing the possibility to run multiple Lua states in parallel.
22 ]],
23 license= "MIT/X11",
24 homepage="http://kotisivu.dnainternet.net/askok/lanes/",
25 maintainer="Benoit Germain <bnt.germain@gmail.com>"
26}
27
28-- Q: What is the difference of "windows" and "win32"? Seems there is none;
29-- so should we list either one or both?
30--
31supported_platforms= { "win32",
32 "macosx",
33 "linux",
34 "freebsd", -- TBD: not tested
35 "msys", -- TBD: not supported by LuaRocks 1.0 (or is it?)
36}
37
38dependencies= {
39 "lua >= 5.1, <= 5.2",
40}
41
42--
43-- Non-Win32: build using the Makefile
44-- Win32: build using 'make-vc.cmd' and "manual" copy of products
45--
46-- TBD: How is MSYS treated? We'd like (really) it to use the Makefile.
47-- It should be a target like "cygwin", not defining "windows".
48-- "windows" should actually guarantee Visual C++ as the compiler.
49--
50-- Q: Does "win32" guarantee we have Visual C++ 2005/2008 command line tools?
51--
52-- Note: Cannot use the simple "module" build type, because we need to precompile
53-- 'src/keeper.lua' -> keeper.lch and bake it into lanes.c.
54--
55build = {
56
57 -- Win32 (Visual C++) uses 'make-vc.cmd' for building
58 --
59 platforms= {
60 windows= {
61 type= "command",
62 build_command= "make-vc.cmd",
63 install= {
64 lua = { "src/lanes.lua" },
65 lib = { "lua51-lanes.dll" }
66 }
67 }
68 },
69
70 -- Other platforms use the Makefile
71 --
72 -- LuaRocks defines CFLAGS, LIBFLAG and LUA_INCDIR for 'make rock',
73 -- defines LIBDIR, LUADIR for 'make install'
74 --
75 -- Ref: <http://www.luarocks.org/en/Paths_and_external_dependencies>
76 --
77 type = "make",
78
79 build_target = "rock",
80 build_variables= {
81 CFLAGS= "$(CFLAGS) -I$(LUA_INCDIR)",
82 LIBFLAG= "$(LIBFLAG)",
83 },
84
85 install_target = "install",
86 install_variables= {
87 LUA_LIBDIR= "$(LIBDIR)",
88 LUA_SHAREDIR= "$(LUADIR)",
89 }
90}
91
diff --git a/setup-vc.cmd b/setup-vc.cmd
index 247459c..e623495 100644
--- a/setup-vc.cmd
+++ b/setup-vc.cmd
@@ -30,10 +30,19 @@ goto FOUND_VC
30 30
31:TRY_VC9 31:TRY_VC9
32set VSINSTALLDIR=%ProgramFiles%\Microsoft Visual Studio 9.0 32set VSINSTALLDIR=%ProgramFiles%\Microsoft Visual Studio 9.0
33if not exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto ERR_NOVC 33if exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto WARN_VC
34set VSINSTALLDIR=%ProgramFiles(x86)%\Microsoft Visual Studio 9.0
35if exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto WARN_VC
34 36
37:TRY_VC10
38set VSINSTALLDIR=%ProgramFiles%\Microsoft Visual Studio 10.0
39if exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto WARN_VC
40set VSINSTALLDIR=%ProgramFiles(x86)%\Microsoft Visual Studio 10.0
41if exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto WARN_VC
42
43:WARN_VC
35echo. 44echo.
36echo *** Warning: Visual C++ 2008 in use *** 45echo *** Warning: Visual C++ 2008/2010 in use ***
37echo. 46echo.
38echo Using VC++2005 is recommended for runtime compatibility issues 47echo Using VC++2005 is recommended for runtime compatibility issues
39echo (LuaBinaries and LfW use it; if you compile everything from 48echo (LuaBinaries and LfW use it; if you compile everything from
@@ -70,7 +79,7 @@ goto EXIT
70REM --- 79REM ---
71:ERR_NOVC 80:ERR_NOVC
72echo. 81echo.
73echo ** ERROR: Visual C++ 2005/08 Express - not detected 82echo ** ERROR: Visual C++ 2005/08/10 Express - not detected
74echo You can set the environment variables separately, and run 'make-vc.cmd' 83echo You can set the environment variables separately, and run 'make-vc.cmd'
75echo or download the compiler from: 84echo or download the compiler from:
76echo http://msdn.microsoft.com/vstudio/express/downloads/ 85echo http://msdn.microsoft.com/vstudio/express/downloads/
diff --git a/src/Makefile b/src/Makefile
index df65926..03f5558 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -124,7 +124,7 @@ all: lua51-$(MODULE)$(_SO)
124# Note: Don't put $(LUA_LIBS) ahead of $^; MSYS will not like that (I think) 124# Note: Don't put $(LUA_LIBS) ahead of $^; MSYS will not like that (I think)
125# 125#
126lua51-$(MODULE)$(_SO): $(OBJ) 126lua51-$(MODULE)$(_SO): $(OBJ)
127 $(CC) $(LIBFLAG) $(LIBS) $^ $(LUA_LIBS) -o $@ 127 $(CC) $(LIBFLAG) $^ $(LIBS) $(LUA_LIBS) -o $@
128 128
129clean: 129clean:
130 -rm -rf lua51-$(MODULE)$(_SO) *.lch *.o *.tmp *.map 130 -rm -rf lua51-$(MODULE)$(_SO) *.lch *.o *.tmp *.map
diff --git a/src/keeper.c b/src/keeper.c
index 01e8880..6f5bd95 100644
--- a/src/keeper.c
+++ b/src/keeper.c
@@ -78,7 +78,7 @@ static char const keeper_chunk[]=
78* unclosed, because it does not really matter. In production code, this 78* unclosed, because it does not really matter. In production code, this
79* function never fails. 79* function never fails.
80*/ 80*/
81char const *init_keepers( int const _nbKeepers) 81char const* init_keepers( int const _nbKeepers, lua_CFunction _on_state_create)
82{ 82{
83 int i; 83 int i;
84 assert( _nbKeepers >= 1); 84 assert( _nbKeepers >= 1);
@@ -87,69 +87,146 @@ char const *init_keepers( int const _nbKeepers)
87 for( i = 0; i < _nbKeepers; ++ i) 87 for( i = 0; i < _nbKeepers; ++ i)
88 { 88 {
89 89
90 // Initialize Keeper states with bare minimum of libs (those required 90 // Initialize Keeper states with bare minimum of libs (those required by 'keeper.lua')
91 // by 'keeper.lua') 91 //
92 // 92 // 'io' for debugging messages, 'package' because we need to require modules exporting idfuncs
93 lua_State *L= luaL_newstate(); 93 // the others because they export functions that we may store in a keeper for transfer between lanes
94 if (!L) 94 lua_State* K = luaG_newstate( "*", _on_state_create);
95 if (!K)
95 return "out of memory"; 96 return "out of memory";
96 97
97 // to see VM name in Decoda debugger 98 // to see VM name in Decoda debugger
98 lua_pushliteral( L, "Keeper #"); 99 lua_pushliteral( K, "Keeper #");
99 lua_pushinteger( L, i + 1); 100 lua_pushinteger( K, i + 1);
100 lua_concat( L, 2); 101 lua_concat( K, 2);
101 lua_setglobal( L, "decoda_name"); 102 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
106 /* We could use an empty table in 'keeper.lua' as the sentinel, but maybe
107 * checking for a lightuserdata is faster. (any unique value will do -> take the address of some global of ours)
108 */
109 lua_pushlightuserdata( L, &GNbKeepers);
110 lua_setglobal( L, "nil_sentinel");
111 103
112 // Read in the preloaded chunk (and run it) 104 // Read in the preloaded chunk (and run it)
113 // 105 //
114 if (luaL_loadbuffer( L, keeper_chunk, sizeof(keeper_chunk), "=lanes_keeper" )) 106 if( luaL_loadbuffer( K, keeper_chunk, sizeof(keeper_chunk), "@keeper.lua"))
115 return "luaL_loadbuffer() failed"; // LUA_ERRMEM 107 return "luaL_loadbuffer() failed"; // LUA_ERRMEM
116 108
117 if (lua_pcall( L, 0 /*args*/, 0 /*results*/, 0 /*errfunc*/ )) 109 if( lua_pcall( K, 0 /*args*/, 0 /*results*/, 0 /*errfunc*/))
118 { 110 {
119 // LUA_ERRRUN / LUA_ERRMEM / LUA_ERRERR 111 // LUA_ERRRUN / LUA_ERRMEM / LUA_ERRERR
120 // 112 //
121 const char *err= lua_tostring(L,-1); 113 char const* err = lua_tostring( K, -1);
122 assert(err); 114 assert( err);
123 return err; 115 return err;
124 } 116 }
125 117
126 MUTEX_INIT( &GKeepers[i].lock_ ); 118 MUTEX_INIT( &GKeepers[i].lock_);
127 GKeepers[i].L= L; 119 GKeepers[i].L = K;
128 //GKeepers[i].count = 0; 120 //GKeepers[i].count = 0;
129 } 121 }
130 return NULL; // ok 122 return NULL; // ok
131} 123}
132 124
125// cause each keeper state to populate its database of transferable functions with those from the specified module
126void populate_keepers( lua_State *L)
127{
128 size_t name_len;
129 char const *name = luaL_checklstring( L, -1, &name_len);
130 size_t package_path_len;
131 char const *package_path;
132 size_t package_cpath_len;
133 char const *package_cpath;
134 int i;
135
136 // we need to make sure that package.path & package.cpath are the same in the keepers
137// than what is currently in use when the module is required in the caller's Lua state
138 STACK_CHECK(L)
139 STACK_GROW( L, 3);
140 lua_getglobal( L, "package");
141 lua_getfield( L, -1, "path");
142 package_path = luaL_checklstring( L, -1, &package_path_len);
143 lua_getfield( L, -2, "cpath");
144 package_cpath = luaL_checklstring( L, -1, &package_cpath_len);
145
146 for( i = 0; i < GNbKeepers; ++ i)
147 {
148 lua_State *K = GKeepers[i].L;
149 int res;
150 MUTEX_LOCK( &GKeepers[i].lock_);
151 STACK_CHECK(K)
152 STACK_GROW( K, 2);
153 lua_getglobal( K, "package");
154 lua_pushlstring( K, package_path, package_path_len);
155 lua_setfield( K, -2, "path");
156 lua_pushlstring( K, package_cpath, package_cpath_len);
157 lua_setfield( K, -2, "cpath");
158 lua_pop( K, 1);
159 lua_getglobal( K, "require");
160 lua_pushlstring( K, name, name_len);
161 res = lua_pcall( K, 1, 0, 0);
162 if( res != 0)
163 {
164 char const *err = luaL_checkstring( K, -1);
165 luaL_error( L, "error requiring '%s' in keeper state: %s", name, err);
166 }
167 STACK_END(K, 0)
168 MUTEX_UNLOCK( &GKeepers[i].lock_);
169 }
170 lua_pop( L, 3);
171 STACK_END(L, 0)
172}
173
133struct s_Keeper *keeper_acquire( const void *ptr) 174struct s_Keeper *keeper_acquire( const void *ptr)
134{ 175{
135 /* 176 // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers)
136 * Any hashing will do that maps pointers to 0..GNbKeepers-1 177 if( GNbKeepers == 0)
137 * consistently. 178 {
138 * 179 return NULL;
139 * Pointers are often aligned by 8 or so - ignore the low order bits 180 }
140 */ 181 else
141 unsigned int i= ((unsigned long)(ptr) >> 3) % GNbKeepers; 182 {
142 struct s_Keeper *K= &GKeepers[i]; 183 /*
184 * Any hashing will do that maps pointers to 0..GNbKeepers-1
185 * consistently.
186 *
187 * Pointers are often aligned by 8 or so - ignore the low order bits
188 */
189 unsigned int i= ((unsigned long)(ptr) >> 3) % GNbKeepers;
190 struct s_Keeper *K= &GKeepers[i];
143 191
144 MUTEX_LOCK( &K->lock_); 192 MUTEX_LOCK( &K->lock_);
145 //++ K->count; 193 //++ K->count;
146 return K; 194 return K;
195 }
147} 196}
148 197
149void keeper_release( struct s_Keeper *K) 198void keeper_release( struct s_Keeper *K)
150{ 199{
151 //-- K->count; 200 //-- K->count;
152 MUTEX_UNLOCK( &K->lock_); 201 if( K) MUTEX_UNLOCK( &K->lock_);
202}
203
204void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, int _nil_to_sentinel)
205{
206 int i, n = lua_gettop( L);
207 /* We could use an empty table in 'keeper.lua' as the sentinel, but maybe
208 * checking for a lightuserdata is faster. (any unique value will do -> take the address of some global of ours)
209 */
210 void *nil_sentinel = &GNbKeepers;
211 for( i = _val_i; i <= n; ++ i)
212 {
213 if( _nil_to_sentinel)
214 {
215 if( lua_isnil( L, i))
216 {
217 lua_pushlightuserdata( L, nil_sentinel);
218 lua_replace( L, i);
219 }
220 }
221 else
222 {
223 if( lua_touserdata( L, i) == nil_sentinel)
224 {
225 lua_pushnil( L);
226 lua_replace( L, i);
227 }
228 }
229 }
153} 230}
154 231
155/* 232/*
@@ -197,6 +274,7 @@ void close_keepers(void)
197 lua_close( GKeepers[i].L); 274 lua_close( GKeepers[i].L);
198 GKeepers[i].L = 0; 275 GKeepers[i].L = 0;
199 //assert( GKeepers[i].count == 0); 276 //assert( GKeepers[i].count == 0);
277 MUTEX_FREE( &GKeepers[i].lock_);
200 } 278 }
201 if( GKeepers) free( GKeepers); 279 if( GKeepers) free( GKeepers);
202 GKeepers = NULL; 280 GKeepers = NULL;
diff --git a/src/keeper.h b/src/keeper.h
index e959c7c..0990846 100644
--- a/src/keeper.h
+++ b/src/keeper.h
@@ -8,9 +8,11 @@ struct s_Keeper
8 //int count; 8 //int count;
9}; 9};
10 10
11const char *init_keepers( int const _nbKeepers); 11char const* init_keepers( int const _nbKeepers, lua_CFunction _on_state_create);
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);
15void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, int _nil_to_sentinel);
14int keeper_call( lua_State *K, char const *func_name, lua_State *L, void *linda, uint_t starting_index); 16int keeper_call( lua_State *K, char const *func_name, lua_State *L, void *linda, uint_t starting_index);
15void close_keepers(void); 17void close_keepers(void);
16 18
diff --git a/src/keeper.lua b/src/keeper.lua
index 2c38c0b..77bf880 100644
--- a/src/keeper.lua
+++ b/src/keeper.lua
@@ -34,29 +34,27 @@ THE SOFTWARE.
34=============================================================================== 34===============================================================================
35]]-- 35]]--
36 36
37-- unique key instead of 'nil' in queues
38--
39assert( nil_sentinel )
40
41-- We only need to have base and table libraries (and io for debugging) 37-- We only need to have base and table libraries (and io for debugging)
42-- 38--
43local table_remove= assert( table.remove ) 39local table_concat = assert( table.concat)
44local table_concat= assert( table.concat ) 40local table_insert = assert( table.insert)
41local table_remove = assert( table.remove)
42local select, unpack = assert( select), assert( unpack)
45 43
46--[[ 44--[[
47local function WR(...) 45local function WR(...)
48 if io then 46 if io then
49 io.stderr:write( table_concat({...},'\t').."\n" ) 47 io.stderr:write( table_concat({...},'\t').."\n" )
50 end 48 end
51end 49end
52 50
53local function DEBUG(title,ud,key) 51local function DEBUG(title,ud,key)
54 assert( title and ud and key ) 52 assert( title and ud and key )
55 53
56 local data,incoming,_= tables(ud) 54 local data,_= tables(ud)
57 55
58 local s= tostring(data[key]) 56 local s= tostring(data[key])
59 for _,v in ipairs( incoming[key] or {} ) do 57 for _,v in ipairs( data[key] or {} ) do
60 s= s..", "..tostring(v) 58 s= s..", "..tostring(v)
61 end 59 end
62 WR( "*** "..title.." ("..tostring(key).."): ", s ) 60 WR( "*** "..title.." ("..tostring(key).."): ", s )
@@ -64,34 +62,64 @@ end
64--]] 62--]]
65 63
66----- 64-----
67-- Actual data store 65-- FIFO for a key
68--
69-- { [linda_deep_ud]= { key= val [, ...] }
70-- ...
71-- }
72-- 66--
73local _data= {} 67
68local fifo_new = function()
69 return { first = 1, count = 0}
70end
71
72local fifo_push = function( fifo, ...)
73 local first, count, added = fifo.first, fifo.count, select( '#', ...)
74 local start = first + count - 1
75 for i = 1, added do
76 fifo[start + i] = select( i, ...)
77 end
78 fifo.count = count + added
79end
80
81local fifo_peek = function( fifo, count)
82 if count <= fifo.count then
83 local first = fifo.first
84 local last = first + count - 1
85 return unpack( fifo, first, last)
86 end
87end
88
89local fifo_pop = function( fifo, count)
90 if count > fifo.count then error("list is too short") end
91 local first = fifo.first
92 local last = first + count - 1
93 local out = { unpack( fifo, first, last)}
94 for i = first, last do
95 fifo[i] = nil
96 end
97 fifo.first = first + count
98 fifo.count = fifo.count - count
99 return unpack( out)
100end
101
74 102
75----- 103-----
76-- Entries queued for use when the existing 'data[ud][key]' entry is consumed. 104-- Actual data store
77-- 105--
78-- { [linda_deep_ud]= { key= { val [, ... } [, ...] } 106-- { [linda_deep_ud]= { key= { val [, ... ] } [, ...] }
79-- ... 107-- ...
80-- } 108-- }
81-- 109--
82local _incoming= {} 110local _data= {}
83 111
84----- 112-----
85-- Length limits (if any) for queues 113-- Length limits (if any) for queues
86-- 114--
87-- 0: don't queue values at all; ':send()' waits if the slot is not vacant 115-- 0: don't queue values at all; ':send()' waits if the slot is not vacant
88-- N: allow N values to be queued (slot itself + N-1); wait if full 116-- N: allow N values to be queued (slot itself + N-1); wait if full
89-- nil: no limits, '_incoming' may grow endlessly 117-- nil: no limits, '_data' may grow endlessly
90-- 118--
91local _limits= {} 119local _limits= {}
92 120
93----- 121-----
94-- data_tbl, incoming_tbl, limits_tbl = tables( linda_deep_ud ) 122-- data_tbl, limits_tbl = tables( linda_deep_ud )
95-- 123--
96-- Gives appropriate tables for a certain Linda (creates them if needed) 124-- Gives appropriate tables for a certain Linda (creates them if needed)
97-- 125--
@@ -100,14 +128,13 @@ local function tables( ud )
100 -- 128 --
101 if not _data[ud] then 129 if not _data[ud] then
102 _data[ud]= {} 130 _data[ud]= {}
103 _incoming[ud]= {}
104 _limits[ud]= {} 131 _limits[ud]= {}
105 end 132 end
106 return _data[ud], _incoming[ud], _limits[ud] 133 return _data[ud], _limits[ud]
107end 134end
108 135
109----- 136-----
110-- bool= send( linda_deep_ud, key, ... ) 137-- bool= send( linda_deep_ud, key, ...)
111-- 138--
112-- Send new data (1..N) to 'key' slot. This send is atomic; all the values 139-- Send new data (1..N) to 'key' slot. This send is atomic; all the values
113-- end up one after each other (this is why having possibility for sending 140-- end up one after each other (this is why having possibility for sending
@@ -118,42 +145,27 @@ end
118-- Returns: 'true' if all the values were placed 145-- Returns: 'true' if all the values were placed
119-- 'false' if sending would exceed the queue limit (wait & retry) 146-- 'false' if sending would exceed the queue limit (wait & retry)
120-- 147--
121function send( ud, key, ... ) 148function send( ud, key, ...)
122 149
123 local data,incoming,limits= tables(ud) 150 local data, limits = tables( ud)
124 151
125 local n= select('#',...) 152 local n = select( '#', ...)
126 if n==0 then return true end -- nothing to send
127 153
128 -- Initialize queue for all keys that have been used with ':send()' 154 -- Initialize queue for all keys that have been used with ':send()'
129 -- 155 --
130 if incoming[key]==nil then 156 if data[key] == nil then
131 incoming[key]= {} 157 data[key] = fifo_new()
132 end 158 end
159 local fifo = data[key]
133 160
134 local len= data[key] and 1+#incoming[key] or 0 161 local len = fifo.count
135 local m= limits[key] 162 local m = limits[key]
136 163
137 if m and len+n > m then 164 if m and len+n > m then
138 return false -- would exceed the limit; try again later 165 return false -- would exceed the limit; try again later
139 end 166 end
140 167
141 for i=1,n do 168 fifo_push( fifo, ...)
142 local val= select(i,...)
143
144 -- 'nil' in the data replaced by sentinel
145 if val==nil then
146 val= nil_sentinel
147 end
148
149 if len==0 then
150 data[key]= val
151 len= 1
152 else
153 incoming[key][len]= val
154 len= len+1
155 end
156 end
157 return true 169 return true
158end 170end
159 171
@@ -164,81 +176,125 @@ end
164-- Read any of the given keys, consuming the data found. Keys are read in 176-- Read any of the given keys, consuming the data found. Keys are read in
165-- order. 177-- order.
166-- 178--
167function receive( ud, ... ) 179function receive( ud, ...)
168 180
169 local data,incoming,_= tables(ud) 181 local data, _ = tables( ud)
170 182
171 for i=1,select('#',...) do 183 for i = 1, select( '#', ...) do
172 local key= select(i,...) 184 local key = select( i, ...)
173 local val= data[key] 185 local fifo = data[key]
174 186 if fifo and fifo.count > 0 then
175 if val~=nil then 187 local val = fifo_pop( fifo, 1)
176 if incoming[key] and incoming[key][1]~=nil then 188 if val ~= nil then
177 -- pop [1] from 'incoming[key]' into the actual slot 189 return val, key
178 data[key]= table_remove( incoming[key], 1 )
179 else
180 data[key]= nil -- empty the slot
181 end
182 if val==nil_sentinel then
183 val= nil
184 end 190 end
185 return val, key
186 end 191 end
187 end 192 end
188 --return nil 193end
194
195
196-----
197-- [val1, ... valCOUNT]= receive_batched( linda_deep_ud, batch_sentinel, key , COUNT)
198--
199-- Read any of the given keys, consuming the data found. Keys are read in
200-- order.
201--
202receive_batched = function( ud, batch_sentinel, key, count)
203 if count > 0 then
204 local data, _ = tables( ud)
205 local fifo = data[key]
206 if fifo and fifo.count >= count then
207 return fifo_pop( fifo, count)
208 end
209 end
189end 210end
190 211
191 212
192----- 213-----
193-- = limit( linda_deep_ud, key, uint ) 214-- = limit( linda_deep_ud, key, uint )
194-- 215--
195function limit( ud, key, n ) 216function limit( ud, key, n)
196 217
197 local _,_,limits= tables(ud) 218 local _, limits = tables( ud)
198 219
199 limits[key]= n 220 limits[key] = n
200end 221end
201 222
202 223
203----- 224-----
204-- void= set( linda_deep_ud, key, [val] ) 225-- void= set( linda_deep_ud, key, [val] )
205-- 226--
206function set( ud, key, val ) 227function set( ud, key, val)
207 228
208 local data,incoming,_= tables(ud) 229 local data, _ = tables( ud)
209 230
210 -- Setting a key to 'nil' really clears it; only queing uses sentinels. 231 -- Setting a key to 'nil' really clears it; only queing uses sentinels.
211 -- 232 --
212 data[key]= val 233 if val ~= nil then
213 incoming[key]= nil 234 local fifo = fifo_new()
235 fifo_push( fifo, val)
236 data[key] = fifo
237 else
238 data[key] = nil
239 end
214end 240end
215 241
216 242
217----- 243-----
218-- [val]= get( linda_deep_ud, key ) 244-- [val]= get( linda_deep_ud, key )
219-- 245--
220function get( ud, key ) 246function get( ud, key)
247 local data, _ = tables( ud)
248 local fifo = data[key]
249 return fifo and fifo_peek( fifo, 1)
250end
221 251
222 local data,_,_= tables(ud)
223 252
224 local val= data[key] 253-----
225 if val==nil_sentinel then 254-- [val]= count( linda_deep_ud, ...)
226 val= nil 255--
256-- 3 modes of operation
257-- linda:count() -> returns a table of key/count pairs
258-- linda:count(key) returns the number of items waiting in the key
259-- linda:count(key,...) -> returns a table telling, for each key, the number of items
260function count( ud, ...)
261 local data, _ = tables( ud)
262 local n = select( '#', ...)
263 if n == 0 then
264 local out
265 for key, _ in pairs( data) do
266 local fifo = data[key]
267 local count = fifo and fifo.count or 0
268 out = out or {}
269 out[key] = count
270 found = true
271 end
272 return out
273 elseif n == 1 then
274 local key = ...
275 local fifo = data[key]
276 return fifo and fifo.count or nil
277 else -- more than 1 key
278 local out
279 for i = 1, n do
280 local key = select( i, ...)
281 local fifo = data[key]
282 local count = fifo and fifo.count or nil
283 out = out or {}
284 out[key] = count
285 end
286 return out
227 end 287 end
228 return val
229end 288end
230 289
231 290
232----- 291-----
233-- void= clear( linda_deep_ud ) 292-- void= clear( linda_deep_ud)
234-- 293--
235-- Clear the data structures used for a Linda (at its destructor) 294-- Clear the data structures used for a Linda (at its destructor)
236-- 295--
237function clear( ud ) 296function clear( ud)
238 297
239 _data[ud]= nil 298 _data[ud]= nil
240 _incoming[ud]= nil
241 _limits[ud]= nil 299 _limits[ud]= nil
242end 300end
243
244
diff --git a/src/lanes.c b/src/lanes.c
index 0c943aa..513a006 100644
--- a/src/lanes.c
+++ b/src/lanes.c
@@ -51,7 +51,7 @@
51 * ... 51 * ...
52 */ 52 */
53 53
54const char *VERSION= "2.1.0"; 54char const* VERSION = "3.1.0";
55 55
56/* 56/*
57=============================================================================== 57===============================================================================
@@ -139,7 +139,7 @@ struct s_lane {
139 // M: sets to FALSE, flags TRUE for cancel request 139 // M: sets to FALSE, flags TRUE for cancel request
140 // S: reads to see if cancel is requested 140 // S: reads to see if cancel is requested
141 141
142#if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) ) 142#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
143 SIGNAL_T done_signal_; 143 SIGNAL_T done_signal_;
144 // 144 //
145 // M: Waited upon at lane ending (if Posix with no PTHREAD_TIMEDJOIN) 145 // M: Waited upon at lane ending (if Posix with no PTHREAD_TIMEDJOIN)
@@ -149,7 +149,7 @@ struct s_lane {
149 // 149 //
150 // Lock required by 'done_signal' condition variable, protecting 150 // Lock required by 'done_signal' condition variable, protecting
151 // lane status changes to DONE/ERROR_ST/CANCELLED. 151 // lane status changes to DONE/ERROR_ST/CANCELLED.
152#endif 152#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
153 153
154 volatile enum { 154 volatile enum {
155 NORMAL, // normal master side state 155 NORMAL, // normal master side state
@@ -249,6 +249,20 @@ static void linda_id( lua_State*, char const * const which);
249#define lua_toLinda(L,n) ((struct s_Linda *)luaG_todeep( L, linda_id, n )) 249#define lua_toLinda(L,n) ((struct s_Linda *)luaG_todeep( L, linda_id, n ))
250 250
251 251
252static void check_key_types( lua_State *L, int _start, int _end)
253{
254 int i;
255 for( i = _start; i <= _end; ++ i)
256 {
257 int t = lua_type( L, i);
258 if( t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA)
259 {
260 continue;
261 }
262 luaL_error( L, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", i);
263 }
264}
265
252/* 266/*
253* bool= linda_send( linda_ud, [timeout_secs=-1,] key_num|str|bool|lightuserdata, ... ) 267* bool= linda_send( linda_ud, [timeout_secs=-1,] key_num|str|bool|lightuserdata, ... )
254* 268*
@@ -271,15 +285,24 @@ LUAG_FUNC( linda_send)
271 if( lua_isnumber(L, 2)) 285 if( lua_isnumber(L, 2))
272 { 286 {
273 timeout= SIGNAL_TIMEOUT_PREPARE( lua_tonumber(L,2) ); 287 timeout= SIGNAL_TIMEOUT_PREPARE( lua_tonumber(L,2) );
274 key_i++; 288 ++ key_i;
275 } 289 }
276 else if( lua_isnil( L, 2)) 290 else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key
277 { 291 {
278 key_i++; 292 ++ key_i;
279 } 293 }
280 294
281 if( lua_isnil( L, key_i)) 295 // make sure the keys are of a valid type
282 luaL_error( L, "nil key" ); 296 check_key_types( L, key_i, key_i);
297
298 // make sure there is something to send
299 if( (uint_t)lua_gettop( L) == key_i)
300 {
301 luaL_error( L, "no data to send");
302 }
303
304 // convert nils to some special non-nil sentinel in sent values
305 keeper_toggle_nil_sentinels( L, key_i + 1, 1);
283 306
284 STACK_GROW(L, 1); 307 STACK_GROW(L, 1);
285 { 308 {
@@ -315,11 +338,11 @@ LUAG_FUNC( linda_send)
315 338
316 cancel = cancel_test( L); // testing here causes no delays 339 cancel = cancel_test( L); // testing here causes no delays
317 if (cancel) 340 if (cancel)
341 {
318 break; 342 break;
343 }
319 344
320 // Bugfix by Benoit Germain Dec-2009: change status of lane to "waiting" 345 // change status of lane to "waiting"
321 //
322#if 1
323 { 346 {
324 struct s_lane *s; 347 struct s_lane *s;
325 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings 348 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings
@@ -338,6 +361,7 @@ LUAG_FUNC( linda_send)
338 ASSERT_L( s->waiting_on == NULL); 361 ASSERT_L( s->waiting_on == NULL);
339 s->waiting_on = &linda->read_happened; 362 s->waiting_on = &linda->read_happened;
340 } 363 }
364 // could not send because no room: wait until some data was read before trying again, or until timeout is reached
341 if( !SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout)) 365 if( !SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout))
342 { 366 {
343 if( s) 367 if( s)
@@ -353,12 +377,6 @@ LUAG_FUNC( linda_send)
353 s->status = prev_status; 377 s->status = prev_status;
354 } 378 }
355 } 379 }
356#else
357 // K lock will be released for the duration of wait and re-acquired
358 //
359 if( !SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout))
360 break; // timeout
361#endif
362 } 380 }
363 STACK_END( KL, 0) 381 STACK_END( KL, 0)
364 keeper_release( K); 382 keeper_release( K);
@@ -379,18 +397,23 @@ LUAG_FUNC( linda_send)
379 397
380 398
381/* 399/*
382* [val, key]= linda_receive( linda_ud, [timeout_secs_num=-1], key_num|str|bool|lightuserdata [, ...] ) 400 * 2 modes of operation
383* 401 * [val, key]= linda_receive( linda_ud, [timeout_secs_num=-1], key_num|str|bool|lightuserdata [, ...] )
384* Receive a value from Linda, consuming it. 402 * Consumes a single value from the Linda, in any key.
385* 403 * Returns: received value (which is consumed from the slot), and the key which had it
386* Returns: value received (which is consumed from the slot) 404
387* key which had it 405 * [val1, ... valCOUNT]= linda_receive( linda_ud, [timeout_secs_num=-1], linda.batched, key_num|str|bool|lightuserdata, COUNT)
388*/ 406 * Consumes COUNT values from the linda, from a single key.
407 * returns the COUNT consumed values, or nil if there weren't enough values to consume
408 *
409 */
410#define BATCH_SENTINEL "270e6c9d-280f-4983-8fee-a7ecdda01475"
389LUAG_FUNC( linda_receive) 411LUAG_FUNC( linda_receive)
390{ 412{
391 struct s_Linda *linda = lua_toLinda( L, 1); 413 struct s_Linda *linda = lua_toLinda( L, 1);
392 int pushed; 414 int pushed, expected_pushed;
393 bool_t cancel = FALSE; 415 bool_t cancel = FALSE;
416 char *keeper_receive;
394 417
395 time_d timeout = -1.0; 418 time_d timeout = -1.0;
396 uint_t key_i = 2; 419 uint_t key_i = 2;
@@ -400,26 +423,44 @@ LUAG_FUNC( linda_receive)
400 if( lua_isnumber( L, 2)) 423 if( lua_isnumber( L, 2))
401 { 424 {
402 timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); 425 timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2));
403 key_i++; 426 ++ key_i;
427 }
428 else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key
429 {
430 ++ key_i;
431 }
432
433 // make sure the keys are of a valid type
434 check_key_types( L, key_i, lua_gettop( L));
435
436 // are we in batched mode?
437 lua_pushliteral( L, BATCH_SENTINEL);
438 if( lua_equal( L, key_i, -1))
439 {
440 keeper_receive = "receive_batched";
441 expected_pushed = (int)luaL_checkinteger( L, key_i + 2);
404 } 442 }
405 else if( lua_isnil( L, 2)) 443 else
406 { 444 {
407 key_i++; 445 keeper_receive = "receive";
446 expected_pushed = 2;
408 } 447 }
448 lua_pop( L, 1);
409 449
410 { 450 {
411 struct s_Keeper *K = keeper_acquire( linda); 451 struct s_Keeper *K = keeper_acquire( linda);
412 for( ;;) 452 for( ;;)
413 { 453 {
414 pushed = keeper_call( K->L, "receive", L, linda, key_i); 454 pushed = keeper_call( K->L, keeper_receive, L, linda, key_i);
415 if( pushed < 0) 455 if( pushed < 0)
416 { 456 {
417 break; 457 break;
418 } 458 }
419 if( pushed > 0) 459 if( pushed > 0)
420 { 460 {
421 ASSERT_L( pushed == 2); 461 ASSERT_L( pushed == expected_pushed);
422 462 // replace sentinels with real nils
463 keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, 0);
423 // To be done from within the 'K' locking area 464 // To be done from within the 'K' locking area
424 // 465 //
425 SIGNAL_ALL( &linda->read_happened); 466 SIGNAL_ALL( &linda->read_happened);
@@ -438,9 +479,7 @@ LUAG_FUNC( linda_receive)
438 break; 479 break;
439 } 480 }
440 481
441 // Bugfix by Benoit Germain Dec-2009: change status of lane to "waiting" 482 // change status of lane to "waiting"
442 //
443#if 1
444 { 483 {
445 struct s_lane *s; 484 struct s_lane *s;
446 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings 485 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings
@@ -459,6 +498,7 @@ LUAG_FUNC( linda_receive)
459 ASSERT_L( s->waiting_on == NULL); 498 ASSERT_L( s->waiting_on == NULL);
460 s->waiting_on = &linda->write_happened; 499 s->waiting_on = &linda->write_happened;
461 } 500 }
501 // not enough data to read: wakeup when data was sent, or when timeout is reached
462 if( !SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout)) 502 if( !SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout))
463 { 503 {
464 if( s) 504 if( s)
@@ -474,12 +514,6 @@ LUAG_FUNC( linda_receive)
474 s->status = prev_status; 514 s->status = prev_status;
475 } 515 }
476 } 516 }
477#else
478 // Release the K lock for the duration of wait, and re-acquire
479 //
480 if( !SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout))
481 break;
482#endif
483 } 517 }
484 keeper_release( K); 518 keeper_release( K);
485 } 519 }
@@ -501,6 +535,7 @@ LUAG_FUNC( linda_receive)
501* = linda_set( linda_ud, key_num|str|bool|lightuserdata [,value] ) 535* = linda_set( linda_ud, key_num|str|bool|lightuserdata [,value] )
502* 536*
503* Set a value to Linda. 537* Set a value to Linda.
538* TODO: what do we do if we set to non-nil and limit is 0?
504* 539*
505* Existing slot value is replaced, and possible queue entries removed. 540* Existing slot value is replaced, and possible queue entries removed.
506*/ 541*/
@@ -510,9 +545,14 @@ LUAG_FUNC( linda_set)
510 bool_t has_value = !lua_isnil( L, 3); 545 bool_t has_value = !lua_isnil( L, 3);
511 luaL_argcheck( L, linda, 1, "expected a linda object!"); 546 luaL_argcheck( L, linda, 1, "expected a linda object!");
512 547
548 // make sure the key is of a valid type
549 check_key_types( L, 2, 2);
550
513 { 551 {
552 int pushed;
514 struct s_Keeper *K = keeper_acquire( linda); 553 struct s_Keeper *K = keeper_acquire( linda);
515 int pushed = keeper_call( K->L, "set", L, linda, 2); 554 // no nil->sentinel toggling, we really clear the linda contents for the given key with a set()
555 pushed = keeper_call( K->L, "set", L, linda, 2);
516 if( pushed >= 0) // no error? 556 if( pushed >= 0) // no error?
517 { 557 {
518 ASSERT_L( pushed == 0); 558 ASSERT_L( pushed == 0);
@@ -537,20 +577,55 @@ LUAG_FUNC( linda_set)
537 577
538 578
539/* 579/*
580 * [val] = linda_count( linda_ud, [key [, ...]])
581 *
582 * Get a count of the pending elements in the specified keys
583 */
584LUAG_FUNC( linda_count)
585{
586 struct s_Linda *linda= lua_toLinda( L, 1);
587 int pushed;
588
589 luaL_argcheck( L, linda, 1, "expected a linda object!");
590 // make sure the keys are of a valid type
591 check_key_types( L, 2, lua_gettop( L));
592
593 {
594 struct s_Keeper *K = keeper_acquire( linda);
595 pushed = keeper_call( K->L, "count", L, linda, 2);
596 keeper_release( K);
597 if( pushed < 0)
598 {
599 luaL_error( L, "tried to count an invalid key");
600 }
601 }
602 return pushed;
603}
604
605
606/*
540* [val]= linda_get( linda_ud, key_num|str|bool|lightuserdata ) 607* [val]= linda_get( linda_ud, key_num|str|bool|lightuserdata )
541* 608*
542* Get a value from Linda. 609* Get a value from Linda.
610* TODO: add support to get multiple values?
543*/ 611*/
544LUAG_FUNC( linda_get) 612LUAG_FUNC( linda_get)
545{ 613{
546 struct s_Linda *linda= lua_toLinda( L, 1); 614 struct s_Linda *linda= lua_toLinda( L, 1);
547 int pushed; 615 int pushed;
616
548 luaL_argcheck( L, linda, 1, "expected a linda object!"); 617 luaL_argcheck( L, linda, 1, "expected a linda object!");
618 // make sure the key is of a valid type
619 check_key_types( L, 2, 2);
549 620
550 { 621 {
551 struct s_Keeper *K = keeper_acquire( linda); 622 struct s_Keeper *K = keeper_acquire( linda);
552 pushed = keeper_call( K->L, "get", L, linda, 2); 623 pushed = keeper_call( K->L, "get", L, linda, 2);
553 ASSERT_L( pushed==0 || pushed==1 ); 624 ASSERT_L( pushed==0 || pushed==1 );
625 if( pushed > 0)
626 {
627 keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, 0);
628 }
554 keeper_release(K); 629 keeper_release(K);
555 // must trigger error after keeper state has been released 630 // must trigger error after keeper state has been released
556 if( pushed < 0) 631 if( pushed < 0)
@@ -571,7 +646,10 @@ LUAG_FUNC( linda_get)
571LUAG_FUNC( linda_limit) 646LUAG_FUNC( linda_limit)
572{ 647{
573 struct s_Linda *linda= lua_toLinda( L, 1 ); 648 struct s_Linda *linda= lua_toLinda( L, 1 );
649
574 luaL_argcheck( L, linda, 1, "expected a linda object!"); 650 luaL_argcheck( L, linda, 1, "expected a linda object!");
651 // make sure the key is of a valid type
652 check_key_types( L, 2, 2);
575 653
576 { 654 {
577 struct s_Keeper *K = keeper_acquire( linda); 655 struct s_Keeper *K = keeper_acquire( linda);
@@ -608,6 +686,57 @@ LUAG_FUNC( linda_deep ) {
608 686
609 687
610/* 688/*
689* string = linda:__tostring( linda_ud)
690*
691* Return the stringification of a linda
692*
693* Useful for concatenation or debugging purposes
694*/
695LUAG_FUNC( linda_tostring)
696{
697 char text[32];
698 struct s_Linda *linda = lua_toLinda( L, 1);
699 luaL_argcheck( L, linda, 1, "expected a linda object!");
700 sprintf( text, "linda: %p", linda);
701 lua_pushstring( L, text);
702 return 1;
703}
704
705
706/*
707* string = linda:__concat( a, b)
708*
709* Return the concatenation of a pair of items, one of them being a linda
710*
711* Useful for concatenation or debugging purposes
712*/
713LUAG_FUNC( linda_concat)
714{
715 struct s_Linda *linda1 = lua_toLinda( L, 1);
716 struct s_Linda *linda2 = lua_toLinda( L, 2);
717 // lua semantics should enforce that one of the parameters we got is a linda
718 luaL_argcheck( L, linda1 || linda2, 1, "expected a linda object!");
719 // replace the lindas by their string equivalents in the stack
720 if ( linda1)
721 {
722 char text[32];
723 sprintf( text, "linda: %p", linda1);
724 lua_pushstring( L, text);
725 lua_replace( L, 1);
726 }
727 if ( linda2)
728 {
729 char text[32];
730 sprintf( text, "linda: %p", linda2);
731 lua_pushstring( L, text);
732 lua_replace( L, 2);
733 }
734 // concat the result
735 lua_concat( L, 2);
736 return 1;
737}
738
739/*
611* Identity function of a shared userdata object. 740* Identity function of a shared userdata object.
612* 741*
613* lightuserdata= linda_id( "new" [, ...] ) 742* lightuserdata= linda_id( "new" [, ...] )
@@ -658,10 +787,11 @@ static void linda_id( lua_State *L, char const * const which)
658 /* Clean associated structures in the keeper state. 787 /* Clean associated structures in the keeper state.
659 */ 788 */
660 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)
661 { 791 {
662 keeper_call( K->L, "clear", L, s, 0 ); 792 keeper_call( K->L, "clear", L, s, 0 );
793 keeper_release(K);
663 } 794 }
664 keeper_release(K);
665 795
666 /* 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
667 * have been gc'ed. Right? 797 * have been gc'ed. Right?
@@ -678,29 +808,43 @@ static void linda_id( lua_State *L, char const * const which)
678 // metatable is its own index 808 // metatable is its own index
679 lua_pushvalue( L, -1); 809 lua_pushvalue( L, -1);
680 lua_setfield( L, -2, "__index"); 810 lua_setfield( L, -2, "__index");
811
681 // protect metatable from external access 812 // protect metatable from external access
682 lua_pushboolean( L, 0); 813 lua_pushboolean( L, 0);
683 lua_setfield( L, -2, "__metatable"); 814 lua_setfield( L, -2, "__metatable");
815
816 lua_pushcfunction( L, LG_linda_tostring);
817 lua_setfield( L, -2, "__tostring");
818
819 lua_pushcfunction( L, LG_linda_concat);
820 lua_setfield( L, -2, "__concat");
821
684 // 822 //
685 // [-1]: linda metatable 823 // [-1]: linda metatable
686 lua_pushcfunction( L, LG_linda_send ); 824 lua_pushcfunction( L, LG_linda_send );
687 lua_setfield( L, -2, "send" ); 825 lua_setfield( L, -2, "send" );
688 826
689 lua_pushcfunction( L, LG_linda_receive ); 827 lua_pushcfunction( L, LG_linda_receive );
690 lua_setfield( L, -2, "receive" ); 828 lua_setfield( L, -2, "receive" );
691 829
692 lua_pushcfunction( L, LG_linda_limit ); 830 lua_pushcfunction( L, LG_linda_limit );
693 lua_setfield( L, -2, "limit" ); 831 lua_setfield( L, -2, "limit" );
694 832
695 lua_pushcfunction( L, LG_linda_set ); 833 lua_pushcfunction( L, LG_linda_set );
696 lua_setfield( L, -2, "set" ); 834 lua_setfield( L, -2, "set" );
697 835
836 lua_pushcfunction( L, LG_linda_count );
837 lua_setfield( L, -2, "count" );
838
698 lua_pushcfunction( L, LG_linda_get ); 839 lua_pushcfunction( L, LG_linda_get );
699 lua_setfield( L, -2, "get" ); 840 lua_setfield( L, -2, "get" );
700 841
701 lua_pushcfunction( L, LG_linda_deep ); 842 lua_pushcfunction( L, LG_linda_deep );
702 lua_setfield( L, -2, "deep" ); 843 lua_setfield( L, -2, "deep" );
703 844
845 lua_pushliteral( L, BATCH_SENTINEL);
846 lua_setfield(L, -2, "batched");
847
704 STACK_END(L,1) 848 STACK_END(L,1)
705 } 849 }
706 else if( strcmp( which, "module") == 0) 850 else if( strcmp( which, "module") == 0)
@@ -714,6 +858,11 @@ static void linda_id( lua_State *L, char const * const which)
714 } 858 }
715} 859}
716 860
861/*
862 * ud = lanes.linda()
863 *
864 * returns a linda object
865 */
717LUAG_FUNC( linda) 866LUAG_FUNC( linda)
718{ 867{
719 return luaG_deep_userdata( L, linda_id); 868 return luaG_deep_userdata( L, linda_id);
@@ -845,8 +994,9 @@ static void selfdestruct_add( struct s_lane *s ) {
845/* 994/*
846* A free-running lane has ended; remove it from selfdestruct chain 995* A free-running lane has ended; remove it from selfdestruct chain
847*/ 996*/
848static void selfdestruct_remove( struct s_lane *s ) { 997static bool_t selfdestruct_remove( struct s_lane *s )
849 998{
999 bool_t found = FALSE;
850 MUTEX_LOCK( &selfdestruct_cs ); 1000 MUTEX_LOCK( &selfdestruct_cs );
851 { 1001 {
852 // Make sure (within the MUTEX) that we actually are in the chain 1002 // Make sure (within the MUTEX) that we actually are in the chain
@@ -855,7 +1005,6 @@ static void selfdestruct_remove( struct s_lane *s ) {
855 // 1005 //
856 if (s->selfdestruct_next != NULL) { 1006 if (s->selfdestruct_next != NULL) {
857 struct s_lane **ref= (struct s_lane **) &selfdestruct_first; 1007 struct s_lane **ref= (struct s_lane **) &selfdestruct_first;
858 bool_t found= FALSE;
859 1008
860 while( *ref != SELFDESTRUCT_END ) { 1009 while( *ref != SELFDESTRUCT_END ) {
861 if (*ref == s) { 1010 if (*ref == s) {
@@ -870,6 +1019,7 @@ static void selfdestruct_remove( struct s_lane *s ) {
870 } 1019 }
871 } 1020 }
872 MUTEX_UNLOCK( &selfdestruct_cs ); 1021 MUTEX_UNLOCK( &selfdestruct_cs );
1022 return found;
873} 1023}
874 1024
875// Initialized by 'init_once_LOCKED()': the deep userdata Linda object 1025// Initialized by 'init_once_LOCKED()': the deep userdata Linda object
@@ -880,9 +1030,10 @@ volatile DEEP_PRELUDE *timer_deep; // = NULL
880/* 1030/*
881* Process end; cancel any still free-running threads 1031* Process end; cancel any still free-running threads
882*/ 1032*/
883static void selfdestruct_atexit( void ) 1033static int selfdestruct_atexit( lua_State *L)
884{ 1034{
885 if (selfdestruct_first == SELFDESTRUCT_END) return; // no free-running threads 1035 (void)L; // unused
1036 if (selfdestruct_first == SELFDESTRUCT_END) return 0; // no free-running threads
886 1037
887 // Signal _all_ still running threads to exit (including the timer thread) 1038 // Signal _all_ still running threads to exit (including the timer thread)
888 // 1039 //
@@ -899,7 +1050,7 @@ static void selfdestruct_atexit( void )
899 // signal the linda the wake up the thread so that it can react to the cancel query 1050 // signal the linda the wake up the thread so that it can react to the cancel query
900 // let us hope we never land here with a pointer on a linda that has been destroyed... 1051 // let us hope we never land here with a pointer on a linda that has been destroyed...
901 SIGNAL_T *waiting_on = s->waiting_on; 1052 SIGNAL_T *waiting_on = s->waiting_on;
902 s->waiting_on = NULL; 1053 //s->waiting_on = NULL; // useful, or not?
903 SIGNAL_ALL( waiting_on); 1054 SIGNAL_ALL( waiting_on);
904 } 1055 }
905 s = s->selfdestruct_next; 1056 s = s->selfdestruct_next;
@@ -969,6 +1120,7 @@ static void selfdestruct_atexit( void )
969 // 1120 //
970 if ( selfdestruct_first != SELFDESTRUCT_END ) { 1121 if ( selfdestruct_first != SELFDESTRUCT_END ) {
971 unsigned n=0; 1122 unsigned n=0;
1123#if 0
972 MUTEX_LOCK( &selfdestruct_cs ); 1124 MUTEX_LOCK( &selfdestruct_cs );
973 { 1125 {
974 struct s_lane *s= selfdestruct_first; 1126 struct s_lane *s= selfdestruct_first;
@@ -983,15 +1135,14 @@ static void selfdestruct_atexit( void )
983 // and works without the block (so let's leave those lanes running) 1135 // and works without the block (so let's leave those lanes running)
984 // 1136 //
985//we want to free memory and such when we exit. 1137//we want to free memory and such when we exit.
986#if 0
987 // 2.0.2: at least timer lane is still here 1138 // 2.0.2: at least timer lane is still here
988 // 1139 //
989 DEBUGEXEC(fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n )); 1140 DEBUGEXEC(fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n ));
1141 n=0;
990#else 1142#else
991 // first thing we did was to raise the linda signals the threads were waiting on (if any) 1143 // first thing we did was to raise the linda signals the threads were waiting on (if any)
992 // therefore, any well-behaved thread should be in CANCELLED state 1144 // therefore, any well-behaved thread should be in CANCELLED state
993 // these are not running, and the state can be closed 1145 // these are not running, and the state can be closed
994 n=0;
995 MUTEX_LOCK( &selfdestruct_cs ); 1146 MUTEX_LOCK( &selfdestruct_cs );
996 { 1147 {
997 struct s_lane *s= selfdestruct_first; 1148 struct s_lane *s= selfdestruct_first;
@@ -999,8 +1150,19 @@ static void selfdestruct_atexit( void )
999 { 1150 {
1000 struct s_lane *next_s= s->selfdestruct_next; 1151 struct s_lane *next_s= s->selfdestruct_next;
1001 s->selfdestruct_next= NULL; // detach from selfdestruct chain 1152 s->selfdestruct_next= NULL; // detach from selfdestruct chain
1002 THREAD_KILL( &s->thread); 1153 if( !THREAD_ISNULL( s->thread)) // can be NULL if previous 'soft' termination succeeded
1154 {
1155 THREAD_KILL( &s->thread);
1156#if THREADAPI == THREADAPI_PTHREAD
1157 // pthread: make sure the thread is really stopped!
1158 THREAD_WAIT( &s->thread, -1, &s->done_signal_, &s->done_lock_, &s->status);
1159#endif // THREADAPI == THREADAPI_PTHREAD
1160 }
1003 // NO lua_close() in this case because we don't know where execution of the state was interrupted 1161 // NO lua_close() in this case because we don't know where execution of the state was interrupted
1162#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1163 SIGNAL_FREE( &s->done_signal_);
1164 MUTEX_FREE( &s->done_lock_);
1165#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1004 free( s); 1166 free( s);
1005 s = next_s; 1167 s = next_s;
1006 n++; 1168 n++;
@@ -1013,6 +1175,7 @@ static void selfdestruct_atexit( void )
1013#endif 1175#endif
1014 } 1176 }
1015 close_keepers(); 1177 close_keepers();
1178 return 0;
1016} 1179}
1017 1180
1018 1181
@@ -1205,15 +1368,15 @@ void SetThreadName( DWORD dwThreadID, char const *_threadName)
1205 1368
1206LUAG_FUNC( set_debug_threadname) 1369LUAG_FUNC( set_debug_threadname)
1207{ 1370{
1208 char const *threadName;
1209 luaL_checktype( L, -1, LUA_TSTRING); 1371 luaL_checktype( L, -1, LUA_TSTRING);
1210 threadName = lua_tostring( L, -1);
1211
1212#if defined PLATFORM_WIN32 && !defined __GNUC__ 1372#if defined PLATFORM_WIN32 && !defined __GNUC__
1213 // to see thead name in Visual Studio C debugger 1373 {
1214 SetThreadName(-1, threadName); 1374 char const *threadName = lua_tostring( L, -1);
1215#endif
1216 1375
1376 // to see thead name in Visual Studio C debugger
1377 SetThreadName(-1, threadName);
1378 }
1379#endif // defined PLATFORM_WIN32 && !defined __GNUC__
1217 // to see VM name in Decoda debugger Virtual Machine window 1380 // to see VM name in Decoda debugger Virtual Machine window
1218 lua_setglobal( L, "decoda_name"); 1381 lua_setglobal( L, "decoda_name");
1219 1382
@@ -1221,11 +1384,7 @@ LUAG_FUNC( set_debug_threadname)
1221} 1384}
1222 1385
1223//--- 1386//---
1224#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 1387static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs)
1225 static THREAD_RETURN_T __stdcall lane_main( void *vs )
1226#else
1227 static THREAD_RETURN_T lane_main( void *vs )
1228#endif
1229{ 1388{
1230 struct s_lane *s= (struct s_lane *)vs; 1389 struct s_lane *s= (struct s_lane *)vs;
1231 int rc, rc2; 1390 int rc, rc2;
@@ -1315,20 +1474,22 @@ LUAG_FUNC( set_debug_threadname)
1315 lua_newtable(L); 1474 lua_newtable(L);
1316 } 1475 }
1317 s->waiting_on = NULL; // just in case 1476 s->waiting_on = NULL; // just in case
1318 if (s->selfdestruct_next != NULL) { 1477 if( selfdestruct_remove( s)) // check and remove (under lock!)
1478 {
1319 // We're a free-running thread and no-one's there to clean us up. 1479 // We're a free-running thread and no-one's there to clean us up.
1320 // 1480 //
1321 lua_close( s->L ); 1481 lua_close( s->L );
1322 s->L = L = 0; 1482 s->L = L = 0;
1323 1483
1324 #if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) ) 1484 #if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1325 SIGNAL_FREE( &s->done_signal_ ); 1485 SIGNAL_FREE( &s->done_signal_);
1326 MUTEX_FREE( &s->done_lock_ ); 1486 MUTEX_FREE( &s->done_lock_);
1327 #endif 1487 #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1328 selfdestruct_remove(s); // away from selfdestruct chain
1329 free(s); 1488 free(s);
1330 1489
1331 } else { 1490 }
1491 else
1492 {
1332 // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them 1493 // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them
1333 1494
1334 enum e_status st= 1495 enum e_status st=
@@ -1339,16 +1500,16 @@ LUAG_FUNC( set_debug_threadname)
1339 // Posix no PTHREAD_TIMEDJOIN: 1500 // Posix no PTHREAD_TIMEDJOIN:
1340 // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change 1501 // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change
1341 // 1502 //
1342 #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) 1503#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1343 s->status= st; 1504 MUTEX_LOCK( &s->done_lock_);
1344 #else
1345 MUTEX_LOCK( &s->done_lock_ );
1346 { 1505 {
1347 s->status= st; 1506#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1348 SIGNAL_ONE( &s->done_signal_ ); // wake up master (while 's->done_lock' is on) 1507 s->status = st;
1508#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1509 SIGNAL_ONE( &s->done_signal_); // wake up master (while 's->done_lock' is on)
1349 } 1510 }
1350 MUTEX_UNLOCK( &s->done_lock_ ); 1511 MUTEX_UNLOCK( &s->done_lock_);
1351 #endif 1512#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1352 } 1513 }
1353 return 0; // ignored 1514 return 0; // ignored
1354} 1515}
@@ -1359,22 +1520,52 @@ LUAG_FUNC( set_debug_threadname)
1359// [cancelstep_uint=0], 1520// [cancelstep_uint=0],
1360// [prio_int=0], 1521// [prio_int=0],
1361// [globals_tbl], 1522// [globals_tbl],
1523// [package_tbl],
1524// [required],
1362// [... args ...] ) 1525// [... args ...] )
1363// 1526//
1364// Upvalues: metatable to use for 'lane_ud' 1527// Upvalues: metatable to use for 'lane_ud'
1365// 1528//
1529
1530// helper function to require a module in the keeper states and in the target state
1531// source state contains module name at the top of the stack
1532static void require_one_module( lua_State *L, lua_State *L2, bool_t _fatal)
1533{
1534 size_t len;
1535 char const *name = lua_tolstring( L, -1, &len);
1536 // require the module in the target lane
1537 STACK_GROW( L2, 2);
1538 lua_getglobal( L2, "require");
1539 if( lua_isnil( L2, -1))
1540 {
1541 lua_pop( L2, 1);
1542 if( _fatal)
1543 luaL_error( L, "cannot pre-require modules without loading 'package' library first");
1544 }
1545 else
1546 {
1547 lua_pushlstring( L2, name, len);
1548 lua_pcall( L2, 1, 0, 0);
1549 // we need to require this module in the keeper states as well
1550 populate_keepers( L);
1551 }
1552}
1553
1366LUAG_FUNC( thread_new ) 1554LUAG_FUNC( thread_new )
1367{ 1555{
1368 lua_State *L2; 1556 lua_State *L2;
1369 struct s_lane *s; 1557 struct s_lane *s;
1370 struct s_lane **ud; 1558 struct s_lane **ud;
1371 1559
1372 const char *libs= lua_tostring( L, 2 ); 1560 char const* libs = lua_tostring( L, 2);
1373 uint_t cs= luaG_optunsigned( L, 3,0); 1561 lua_CFunction on_state_create = lua_iscfunction( L, 3) ? lua_tocfunction( L, 3) : NULL;
1374 int prio= (int)luaL_optinteger( L, 4,0); 1562 uint_t cs = luaG_optunsigned( L, 4, 0);
1375 uint_t glob= luaG_isany(L,5) ? 5:0; 1563 int prio = (int) luaL_optinteger( L, 5, 0);
1564 uint_t glob = luaG_isany( L, 6) ? 6 : 0;
1565 uint_t package = luaG_isany( L,7) ? 7 : 0;
1566 uint_t required = luaG_isany( L, 8) ? 8 : 0;
1376 1567
1377#define FIXED_ARGS (5) 1568#define FIXED_ARGS 8
1378 uint_t args= lua_gettop(L) - FIXED_ARGS; 1569 uint_t args= lua_gettop(L) - FIXED_ARGS;
1379 1570
1380 if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) 1571 if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX)
@@ -1385,45 +1576,109 @@ LUAG_FUNC( thread_new )
1385 1576
1386 /* --- Create and prepare the sub state --- */ 1577 /* --- Create and prepare the sub state --- */
1387 1578
1388 L2 = luaL_newstate(); // uses standard 'realloc()'-based allocator, 1579 // populate with selected libraries at the same time
1389 // sets the panic callback 1580 //
1390 1581 L2 = luaG_newstate( libs, on_state_create);
1391 if (!L2) luaL_error( L, "'luaL_newstate()' failed; out of memory" ); 1582 if (!L2) luaL_error( L, "'luaL_newstate()' failed; out of memory" );
1392 1583
1393 STACK_GROW( L,2 ); 1584 STACK_GROW( L, 2);
1394 1585
1395 // Setting the globals table (needs to be done before loading stdlibs, 1586 ASSERT_L( lua_gettop(L2) == 0);
1396 // and the lane function)
1397 //
1398 if (glob!=0)
1399 {
1400 STACK_CHECK(L)
1401 if (!lua_istable(L,glob))
1402 luaL_error( L, "Expected table, got %s", luaG_typename(L,glob) );
1403 1587
1404 lua_pushvalue( L, glob ); 1588 // package.path
1589 STACK_CHECK(L)
1590 STACK_CHECK(L2)
1591 if( package)
1592 {
1593 if (lua_type(L,package) != LUA_TTABLE)
1594 luaL_error( L, "expected package as table, got %s", luaL_typename(L,package));
1595 lua_getglobal( L2, "package");
1596 if( !lua_isnil( L2, -1)) // package library not loaded: do nothing
1597 {
1598 int i;
1599 char const *entries[] = { "path", "cpath", "preload", "loaders", NULL};
1600 for( i = 0; entries[i]; ++ i)
1601 {
1602 lua_getfield( L, package, entries[i]);
1603 if( lua_isnil( L, -1))
1604 {
1605 lua_pop( L, 1);
1606 }
1607 else
1608 {
1609 luaG_inter_move( L, L2, 1); // moves the entry to L2
1610 lua_setfield( L2, -2, entries[i]); // set package[entries[i]]
1611 }
1612 }
1613 }
1614 lua_pop( L2, 1);
1615 }
1616 STACK_END(L2,0)
1617 STACK_END(L,0)
1405 1618
1406 luaG_inter_move( L, L2, 1); // moves the table to L2 1619 // modules to require in the target lane *before* the function is transfered!
1407 1620
1408 // L2 [-1]: table of globals 1621 //start by requiring lua51-lanes, since it is a bit special
1622 // it is not fatal if 'require' isn't loaded, just ignore (may cause function transfer errors later on if the lane pulls the lanes module itself)
1623 STACK_CHECK(L)
1624 STACK_CHECK(L2)
1625 lua_pushliteral( L, "lua51-lanes");
1626 require_one_module( L, L2, FALSE);
1627 lua_pop( L, 1);
1628 STACK_END(L2,0)
1629 STACK_END(L,0)
1409 1630
1410 // "You can change the global environment of a Lua thread using lua_replace" 1631 STACK_CHECK(L)
1411 // (refman-5.0.pdf p. 30) 1632 STACK_CHECK(L2)
1412 // 1633 if( required)
1413 lua_replace( L2, LUA_GLOBALSINDEX ); 1634 {
1414 STACK_END(L,0) 1635 int nbRequired = 1;
1636 // should not happen, was checked in lanes.lua before calling thread_new()
1637 if (lua_type(L, required) != LUA_TTABLE)
1638 luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required));
1639 lua_pushnil( L);
1640 while( lua_next( L, required) != 0)
1641 {
1642 if (lua_type(L,-1) != LUA_TSTRING || lua_type(L,-2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired)
1643 {
1644 luaL_error( L, "required module list should be a list of strings.");
1645 }
1646 else
1647 {
1648 require_one_module( L, L2, TRUE);
1649 }
1650 lua_pop( L, 1);
1651 ++ nbRequired;
1652 }
1415 } 1653 }
1654 STACK_END(L2,0)
1655 STACK_END(L,0)
1416 1656
1417 // Selected libraries 1657 // Appending the specified globals to the global environment
1658 // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed...
1418 // 1659 //
1419 if (libs) 1660 if (glob!=0)
1420 { 1661 {
1421 const char *err= luaG_openlibs( L2, libs ); 1662 STACK_CHECK(L)
1422 ASSERT_L( !err ); // bad libs should have been noticed by 'lanes.lua' 1663 STACK_CHECK(L2)
1664 if (!lua_istable(L,glob))
1665 luaL_error( L, "Expected table, got %s", luaL_typename(L,glob));
1423 1666
1424 serialize_require( L2 ); 1667 lua_pushnil( L);
1668 while( lua_next( L, glob))
1669 {
1670 luaG_inter_copy( L, L2, 2); // moves the key/value pair to the L2 stack
1671 // assign it in the globals table
1672 lua_rawset( L2, LUA_GLOBALSINDEX);
1673 lua_pop( L, 1);
1674 }
1675
1676 STACK_END(L2, 0)
1677 STACK_END(L, 0)
1425 } 1678 }
1426 1679
1680 ASSERT_L( lua_gettop(L2) == 0);
1681
1427 // Lane main function 1682 // Lane main function
1428 // 1683 //
1429 STACK_CHECK(L) 1684 STACK_CHECK(L)
@@ -1470,10 +1725,10 @@ LUAG_FUNC( thread_new )
1470 s->waiting_on = NULL; 1725 s->waiting_on = NULL;
1471 s->cancel_request= FALSE; 1726 s->cancel_request= FALSE;
1472 1727
1473#if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) ) 1728#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1474 MUTEX_INIT( &s->done_lock_ ); 1729 MUTEX_INIT( &s->done_lock_);
1475 SIGNAL_INIT( &s->done_signal_ ); 1730 SIGNAL_INIT( &s->done_signal_);
1476#endif 1731#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1477 s->mstatus= NORMAL; 1732 s->mstatus= NORMAL;
1478 s->selfdestruct_next= NULL; 1733 s->selfdestruct_next= NULL;
1479 1734
@@ -1488,7 +1743,7 @@ LUAG_FUNC( thread_new )
1488 lua_newtable( L); 1743 lua_newtable( L);
1489 lua_setfenv( L, -2); 1744 lua_setfenv( L, -2);
1490 1745
1491 // Place 's' to registry, for 'cancel_test()' (even if 'cs'==0 we still 1746 // Place 's' in registry, for 'cancel_test()' (even if 'cs'==0 we still
1492 // do cancel tests at pending send/receive). 1747 // do cancel tests at pending send/receive).
1493 // 1748 //
1494 lua_pushlightuserdata( L2, CANCEL_TEST_KEY ); 1749 lua_pushlightuserdata( L2, CANCEL_TEST_KEY );
@@ -1545,6 +1800,7 @@ LUAG_FUNC( thread_gc )
1545 { 1800 {
1546 // Make sure a kill has proceeded, before cleaning up the data structure. 1801 // Make sure a kill has proceeded, before cleaning up the data structure.
1547 // 1802 //
1803 // NO lua_close() in this case because we don't know where execution of the state was interrupted
1548 // If not doing 'THREAD_WAIT()' we should close the Lua state here 1804 // If not doing 'THREAD_WAIT()' we should close the Lua state here
1549 // (can it be out of order, since we killed the lane abruptly?) 1805 // (can it be out of order, since we killed the lane abruptly?)
1550 // 1806 //
@@ -1553,11 +1809,7 @@ LUAG_FUNC( thread_gc )
1553 s->L = 0; 1809 s->L = 0;
1554#else // 0 1810#else // 0
1555 DEBUGEXEC(fprintf( stderr, "** Joining with a killed thread (needs testing) **" )); 1811 DEBUGEXEC(fprintf( stderr, "** Joining with a killed thread (needs testing) **" ));
1556#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) 1812 THREAD_WAIT( &s->thread, -1, &s->done_signal_, &s->done_lock_, &s->status);
1557 THREAD_WAIT( &s->thread, -1 );
1558#else
1559 THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, -1 );
1560#endif
1561 DEBUGEXEC(fprintf( stderr, "** Joined ok **" )); 1813 DEBUGEXEC(fprintf( stderr, "** Joined ok **" ));
1562#endif // 0 1814#endif // 0
1563 } 1815 }
@@ -1569,12 +1821,12 @@ LUAG_FUNC( thread_gc )
1569 1821
1570 // Clean up after a (finished) thread 1822 // Clean up after a (finished) thread
1571 // 1823 //
1572#if (! ((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN))) 1824#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1573 SIGNAL_FREE( &s->done_signal_ ); 1825 SIGNAL_FREE( &s->done_signal_);
1574 MUTEX_FREE( &s->done_lock_ ); 1826 MUTEX_FREE( &s->done_lock_);
1575#endif 1827#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1576 1828
1577 free(s); 1829 free( s);
1578 1830
1579 return 0; 1831 return 0;
1580} 1832}
@@ -1603,13 +1855,19 @@ static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force)
1603 // 1855 //
1604 if( s->status < DONE) 1856 if( s->status < DONE)
1605 { 1857 {
1606 s->cancel_request = TRUE; // it's now signalled to stop 1858 s->cancel_request = TRUE; // it's now signaled to stop
1607 done= 1859 // signal the linda the wake up the thread so that it can react to the cancel query
1608#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) 1860 // let us hope we never land here with a pointer on a linda that has been destroyed...
1609 THREAD_WAIT( &s->thread, secs); 1861 //MUTEX_LOCK( &selfdestruct_cs );
1610#else 1862 {
1611 THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, secs); 1863 SIGNAL_T *waiting_on = s->waiting_on;
1612#endif 1864 if( s->status == WAITING && waiting_on != NULL)
1865 {
1866 SIGNAL_ALL( waiting_on);
1867 }
1868 }
1869 //MUTEX_UNLOCK( &selfdestruct_cs );
1870 done = THREAD_WAIT( &s->thread, secs, &s->done_signal_, &s->done_lock_, &s->status);
1613 1871
1614 if ((!done) && force) 1872 if ((!done) && force)
1615 { 1873 {
@@ -1627,23 +1885,32 @@ static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force)
1627 1885
1628LUAG_FUNC( thread_cancel) 1886LUAG_FUNC( thread_cancel)
1629{ 1887{
1630 struct s_lane *s= lua_toLane(L,1); 1888 if( lua_gettop( L) != 1 || lua_type( L, 1) != LUA_TUSERDATA)
1631 double secs= 0.0; 1889 {
1632 uint_t force_i=2; 1890 return luaL_error( L, "invalid argument #1, did you use ':' as you should?");
1633 bool_t force, done= TRUE; 1891 }
1892 else
1893 {
1894 struct s_lane *s = lua_toLane( L, 1);
1895 double secs = 0.0;
1896 uint_t force_i = 2;
1897 bool_t force, done= TRUE;
1634 1898
1635 if (lua_isnumber(L,2)) { 1899 if( lua_isnumber( L, 2))
1636 secs= lua_tonumber(L,2); 1900 {
1637 force_i++; 1901 secs = lua_tonumber( L, 2);
1638 } else if (lua_isnil(L,2)) 1902 ++ force_i;
1639 force_i++; 1903 }
1904 else if( lua_isnil( L, 2))
1905 ++ force_i;
1640 1906
1641 force= lua_toboolean(L,force_i); // FALSE if nothing there 1907 force = lua_toboolean( L, force_i); // FALSE if nothing there
1642 1908
1643 done = thread_cancel( s, secs, force); 1909 done = thread_cancel( s, secs, force);
1644 1910
1645 lua_pushboolean( L, done); 1911 lua_pushboolean( L, done);
1646 return 1; 1912 return 1;
1913 }
1647} 1914}
1648 1915
1649//--- 1916//---
@@ -1698,12 +1965,7 @@ LUAG_FUNC( thread_join )
1698 int ret; 1965 int ret;
1699 bool_t done; 1966 bool_t done;
1700 1967
1701 done = (s->thread == 0) || 1968 done = THREAD_ISNULL( s->thread) || THREAD_WAIT( &s->thread, wait_secs, &s->done_signal_, &s->done_lock_, &s->status);
1702#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN)
1703 THREAD_WAIT( &s->thread, wait_secs );
1704#else
1705 THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, wait_secs);
1706#endif
1707 if (!done || !L2) 1969 if (!done || !L2)
1708 return 0; // timeout: pushes none, leaves 'L2' alive 1970 return 0; // timeout: pushes none, leaves 'L2' alive
1709 1971
@@ -1915,7 +2177,8 @@ LUAG_FUNC( now_secs )
1915LUAG_FUNC( wakeup_conv ) 2177LUAG_FUNC( wakeup_conv )
1916{ 2178{
1917 int year, month, day, hour, min, sec, isdst; 2179 int year, month, day, hour, min, sec, isdst;
1918 struct tm tm= {0}; 2180 struct tm t;
2181 memset( &t, 0, sizeof( t));
1919 // 2182 //
1920 // .year (four digits) 2183 // .year (four digits)
1921 // .month (1..12) 2184 // .month (1..12)
@@ -1942,15 +2205,15 @@ LUAG_FUNC( wakeup_conv )
1942 lua_pop(L,1); 2205 lua_pop(L,1);
1943 STACK_END(L,0) 2206 STACK_END(L,0)
1944 2207
1945 tm.tm_year= year-1900; 2208 t.tm_year= year-1900;
1946 tm.tm_mon= month-1; // 0..11 2209 t.tm_mon= month-1; // 0..11
1947 tm.tm_mday= day; // 1..31 2210 t.tm_mday= day; // 1..31
1948 tm.tm_hour= hour; // 0..23 2211 t.tm_hour= hour; // 0..23
1949 tm.tm_min= min; // 0..59 2212 t.tm_min= min; // 0..59
1950 tm.tm_sec= sec; // 0..60 2213 t.tm_sec= sec; // 0..60
1951 tm.tm_isdst= isdst; // 0/1/negative 2214 t.tm_isdst= isdst; // 0/1/negative
1952 2215
1953 lua_pushnumber( L, (double) mktime( &tm ) ); // ms=0 2216 lua_pushnumber( L, (double) mktime( &t)); // ms=0
1954 return 1; 2217 return 1;
1955} 2218}
1956 2219
@@ -1968,7 +2231,7 @@ static const struct luaL_reg lanes_functions [] = {
1968/* 2231/*
1969* One-time initializations 2232* One-time initializations
1970*/ 2233*/
1971static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ref, int const nbKeepers) 2234static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_ref, int const nbKeepers, lua_CFunction _on_state_create)
1972{ 2235{
1973 const char *err; 2236 const char *err;
1974 2237
@@ -1994,7 +2257,7 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_
1994 // Selfdestruct chain handling 2257 // Selfdestruct chain handling
1995 // 2258 //
1996 MUTEX_INIT( &selfdestruct_cs ); 2259 MUTEX_INIT( &selfdestruct_cs );
1997 atexit( selfdestruct_atexit ); 2260 //atexit( selfdestruct_atexit );
1998 2261
1999 //--- 2262 //---
2000 // Linux needs SCHED_RR to change thread priorities, and that is only 2263 // Linux needs SCHED_RR to change thread priorities, and that is only
@@ -2019,7 +2282,7 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_
2019 } 2282 }
2020 #endif 2283 #endif
2021#endif 2284#endif
2022 err= init_keepers( nbKeepers); 2285 err = init_keepers( nbKeepers, _on_state_create);
2023 if (err) 2286 if (err)
2024 { 2287 {
2025 luaL_error( L, "Unable to initialize: %s", err ); 2288 luaL_error( L, "Unable to initialize: %s", err );
@@ -2044,7 +2307,17 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_
2044 2307
2045 // The host Lua state must always have a reference to this Linda object in order for our 'timer_deep_ref' to be valid. 2308 // The host Lua state must always have a reference to this Linda object in order for our 'timer_deep_ref' to be valid.
2046 // So store a reference that we will never actually use. 2309 // So store a reference that we will never actually use.
2047 lua_pushlightuserdata(L, (void *)init_once_LOCKED); 2310 // at the same time, use this object as a 'desinit' marker:
2311 // when the main lua State is closed, this object will be GC'ed
2312 {
2313 lua_newuserdata( L, 1);
2314 lua_newtable( L);
2315 lua_pushcfunction( L, selfdestruct_atexit);
2316 lua_setfield( L, -2, "__gc");
2317 lua_pushliteral( L, "AtExit");
2318 lua_setfield( L, -2, "__metatable");
2319 lua_setmetatable( L, -2);
2320 }
2048 lua_insert(L, -2); // Swap key with the Linda object 2321 lua_insert(L, -2); // Swap key with the Linda object
2049 lua_rawset(L, LUA_REGISTRYINDEX); 2322 lua_rawset(L, LUA_REGISTRYINDEX);
2050 2323
@@ -2052,15 +2325,15 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_
2052 STACK_END(L,0) 2325 STACK_END(L,0)
2053} 2326}
2054 2327
2055int 2328static volatile long s_initCount = 0;
2056#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 2329
2057__declspec(dllexport) 2330LUAG_FUNC( configure )
2058#endif
2059luaopen_lanes( lua_State *L )
2060{ 2331{
2061 static volatile int /*bool*/ go_ahead; // = 0 2332 char const* name = luaL_checkstring( L, lua_upvalueindex( 1));
2062 int const nbKeepers = luaL_optint( L, 2, 1); 2333 int const nbKeepers = luaL_optint( L, 1, 1);
2063 luaL_argcheck( L, nbKeepers > 0, 2, "Number of keeper states must be > 0"); 2334 lua_CFunction on_state_create = lua_iscfunction( L, 2) ? lua_tocfunction( L, 2) : NULL;
2335 luaL_argcheck( L, nbKeepers > 0, 1, "Number of keeper states must be > 0");
2336 luaL_argcheck( L, lua_iscfunction( L, 2) || lua_isnil( L, 2), 2, "on_state_create should be a C function");
2064 /* 2337 /*
2065 * Making one-time initializations. 2338 * Making one-time initializations.
2066 * 2339 *
@@ -2068,42 +2341,43 @@ luaopen_lanes( lua_State *L )
2068 * there is no problem. But if the host is multithreaded, we need to lock around the 2341 * there is no problem. But if the host is multithreaded, we need to lock around the
2069 * initializations. 2342 * initializations.
2070 */ 2343 */
2071#ifdef PLATFORM_WIN32 2344#if THREADAPI == THREADAPI_WINDOWS
2072 { 2345 {
2073 // TBD: Someone please replace this with reliable Win32 API code. Problem is, 2346 static volatile int /*bool*/ go_ahead; // = 0
2074 // there's no autoinitializing locks (s.a. PTHREAD_MUTEX_INITIALIZER) in 2347 if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0)
2075 // Windows so 'InterlockedIncrement' or something needs to be used. 2348 {
2076 // This is 99.9999% safe, though (and always safe if host is single-threaded) 2349 init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create);
2077 // -- AKa 24-Jun-2009
2078 //
2079 static volatile unsigned my_number; // = 0
2080
2081 if (my_number++ == 0) { // almost atomic
2082 init_once_LOCKED(L, &timer_deep, nbKeepers);
2083 go_ahead= 1; // let others pass 2350 go_ahead= 1; // let others pass
2084 } else { 2351 }
2352 else
2353 {
2085 while( !go_ahead ) { Sleep(1); } // changes threads 2354 while( !go_ahead ) { Sleep(1); } // changes threads
2086 } 2355 }
2087 } 2356 }
2088#else 2357#else // THREADAPI == THREADAPI_PTHREAD
2089 if (!go_ahead) { 2358 if( s_initCount == 0)
2359 {
2090 static pthread_mutex_t my_lock= PTHREAD_MUTEX_INITIALIZER; 2360 static pthread_mutex_t my_lock= PTHREAD_MUTEX_INITIALIZER;
2091 pthread_mutex_lock(&my_lock); 2361 pthread_mutex_lock( &my_lock);
2092 { 2362 {
2093 // Recheck now that we're within the lock 2363 // Recheck now that we're within the lock
2094 // 2364 //
2095 if (!go_ahead) { 2365 if( s_initCount == 0)
2096 init_once_LOCKED(L, &timer_deep, nbKeepers); 2366 {
2097 go_ahead= 1; 2367 init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create);
2368 s_initCount = 1;
2098 } 2369 }
2099 } 2370 }
2100 pthread_mutex_unlock(&my_lock); 2371 pthread_mutex_unlock(&my_lock);
2101 } 2372 }
2102#endif 2373#endif // THREADAPI == THREADAPI_PTHREAD
2103 assert( timer_deep != 0 ); 2374 assert( timer_deep != 0 );
2104 2375
2105 // Create main module interface table 2376 // Create main module interface table
2106 lua_newtable(L); 2377 lua_pushvalue( L, lua_upvalueindex( 2));
2378 // remove configure() (this function) from the module interface
2379 lua_pushnil( L);
2380 lua_setfield( L, -2, "configure");
2107 luaL_register(L, NULL, lanes_functions); 2381 luaL_register(L, NULL, lanes_functions);
2108 2382
2109 // metatable for threads 2383 // metatable for threads
@@ -2124,7 +2398,7 @@ luaopen_lanes( lua_State *L )
2124 lua_setfield( L, -2, "join"); 2398 lua_setfield( L, -2, "join");
2125 lua_pushcfunction( L, LG_thread_cancel); 2399 lua_pushcfunction( L, LG_thread_cancel);
2126 lua_setfield( L, -2, "cancel"); 2400 lua_setfield( L, -2, "cancel");
2127 lua_pushboolean( L, 0); 2401 lua_pushliteral( L, "Lane");
2128 lua_setfield( L, -2, "__metatable"); 2402 lua_setfield( L, -2, "__metatable");
2129 2403
2130 lua_pushcclosure( L, LG_thread_new, 1 ); // metatable as closure param 2404 lua_pushcclosure( L, LG_thread_new, 1 ); // metatable as closure param
@@ -2142,8 +2416,42 @@ luaopen_lanes( lua_State *L )
2142 lua_pushlightuserdata( L, CANCEL_ERROR ); 2416 lua_pushlightuserdata( L, CANCEL_ERROR );
2143 lua_setfield(L, -2, "cancel_error"); 2417 lua_setfield(L, -2, "cancel_error");
2144 2418
2145 // Return the local module table 2419 // register all native functions found in that module in the transferable functions database
2146 return 1; 2420 // we process it before _G because we don't want to find the module when scanning _G (this would generate longer names)
2421 populate_func_lookup_table( L, -1, name);
2422 // record all existing C/JIT-fast functions
2423 populate_func_lookup_table( L, LUA_GLOBALSINDEX, NULL);
2424 // Return nothing
2425 lua_pop( L, 1);
2426 return 0;
2427}
2428
2429int
2430#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
2431__declspec(dllexport)
2432#endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
2433luaopen_lanes( lua_State *L )
2434{
2435 // Create main module interface table
2436 // we only have 1 closure, which must be called to configure Lanes
2437 STACK_GROW( L, 3);
2438 STACK_CHECK( L)
2439 lua_newtable(L);
2440 lua_pushvalue(L, 1); // module name
2441 lua_pushvalue(L, -2); // module table
2442 lua_pushcclosure( L, LG_configure, 2);
2443 if( s_initCount == 0)
2444 {
2445 lua_setfield( L, -2, "configure");
2446 }
2447 else // already initialized: call it immediately and be done
2448 {
2449 lua_pushinteger( L, 666); // any value will do, it will be ignored
2450 lua_pushnil( L); // almost idem
2451 lua_call( L, 2, 0);
2452 }
2453 STACK_END( L, 1)
2454 return 1;
2147} 2455}
2148 2456
2149 2457
diff --git a/src/lanes.lua b/src/lanes.lua
index 05c2ff1..ec5a4e4 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -39,11 +39,27 @@ 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( _params)
49_params = _params or { nb_keepers = 1, with_timers = true, on_state_create = nil}
50if type( _params) ~= "table" then
51 error( "Bad parameter #1 to lanes.configure(), should be a table")
52end
53-- on_state_create may be nil or a function
54if _params.on_state_create and (type( _params.on_state_create) ~= "function") then
55 error( "Bad on_state_create: " .. tostring( _params.on_state_create), 2)
56end
43 57
44local mm = require "lua51-lanes" 58local mm = require "lua51-lanes"
45assert( type(mm)=="table" ) 59assert( type(mm)=="table" )
46 60
61-- 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
62if mm.configure then mm.configure( _params.nb_keepers, _params.on_state_create) end
47 63
48local thread_new = assert(mm.thread_new) 64local thread_new = assert(mm.thread_new)
49 65
@@ -74,7 +90,7 @@ local pairs= assert( pairs )
74local tostring= assert( tostring ) 90local tostring= assert( tostring )
75local error= assert( error ) 91local error= assert( error )
76 92
77ABOUT= 93lanes.ABOUT=
78{ 94{
79 author= "Asko Kauppi <akauppi@gmail.com>", 95 author= "Asko Kauppi <akauppi@gmail.com>",
80 description= "Running multiple Lua states in parallel", 96 description= "Running multiple Lua states in parallel",
@@ -140,6 +156,7 @@ end
140-- 156--
141-- .globals: table of globals to set for a new thread (passed by value) 157-- .globals: table of globals to set for a new thread (passed by value)
142-- 158--
159-- .required: table of packages to require
143-- ... (more options may be introduced later) ... 160-- ... (more options may be introduced later) ...
144-- 161--
145-- Calling with a function parameter ('lane_func') ends the string/table 162-- Calling with a function parameter ('lane_func') ends the string/table
@@ -161,7 +178,8 @@ local valid_libs= {
161 ["*"]= true 178 ["*"]= true
162} 179}
163 180
164function gen( ... ) 181-- PUBLIC LANES API
182local function gen( ... )
165 local opt= {} 183 local opt= {}
166 local libs= nil 184 local libs= nil
167 local lev= 2 -- level for errors 185 local lev= 2 -- level for errors
@@ -204,25 +222,31 @@ function gen( ... )
204 end 222 end
205 end 223 end
206 224
207 local prio, cs, g_tbl 225 local prio, cs, g_tbl, package_tbl, required
208 226
209 for k,v in pairs(opt) do 227 for k,v in pairs(opt) do
210 if k=="priority" then prio= v 228 if k=="priority" then prio= v
211 elseif k=="cancelstep" then cs= (v==true) and 100 or 229 elseif k=="cancelstep" then
212 (v==false) and 0 or 230 cs = (v==true) and 100 or
213 type(v)=="number" and v or 231 (v==false) and 0 or
214 error( "Bad cancelstep: "..tostring(v), lev ) 232 type(v)=="number" and v or
233 error( "Bad cancelstep: "..tostring(v), lev )
215 elseif k=="globals" then g_tbl= v 234 elseif k=="globals" then g_tbl= v
235 elseif k=="package" then
236 package_tbl = (type( v) == "table") and v or error( "Bad package: " .. tostring( v), lev)
237 elseif k=="required" then
238 required= (type( v) == "table") and v or error( "Bad required: " .. tostring( v), lev)
216 --.. 239 --..
217 elseif k==1 then error( "unkeyed option: ".. tostring(v), lev ) 240 elseif k==1 then error( "unkeyed option: ".. tostring(v), lev )
218 else error( "Bad option: ".. tostring(k), lev ) 241 else error( "Bad option: ".. tostring(k), lev )
219 end 242 end
220 end 243 end
221 244
245 if not package_tbl then package_tbl = package end
222 -- Lane generator 246 -- Lane generator
223 -- 247 --
224 return function(...) 248 return function(...)
225 return thread_new( func, libs, cs, prio, g_tbl, ...) -- args 249 return thread_new( func, libs, _params.on_state_create, cs, prio, g_tbl, package_tbl, required, ...) -- args
226 end 250 end
227end 251end
228 252
@@ -233,11 +257,17 @@ end
233----- 257-----
234-- lanes.linda() -> linda_ud 258-- lanes.linda() -> linda_ud
235-- 259--
236linda = mm.linda 260-- PUBLIC LANES API
261local linda = mm.linda
237 262
238 263
239---=== Timers ===--- 264---=== Timers ===---
240 265
266-- PUBLIC LANES API
267local timer = function() error "timers are not active" end
268
269if _params.with_timers ~= false then
270
241local timer_gateway= assert( mm.timer_gateway ) 271local timer_gateway= assert( mm.timer_gateway )
242-- 272--
243-- On first 'require "lanes"', a timer lane is spawned that will maintain 273-- On first 'require "lanes"', a timer lane is spawned that will maintain
@@ -420,6 +450,8 @@ if first_time then
420 assert( key and wakeup_at and period ) 450 assert( key and wakeup_at and period )
421 451
422 set_timer( linda, key, wakeup_at, period>0 and period or nil ) 452 set_timer( linda, key, wakeup_at, period>0 and period or nil )
453 elseif secs == 0 then -- got no value while block-waiting?
454 WR( "timer lane: no linda, aborted?")
423 end 455 end
424 end 456 end
425 end )() 457 end )()
@@ -428,7 +460,8 @@ end
428----- 460-----
429-- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] ) 461-- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] )
430-- 462--
431function timer( linda, key, a, period ) 463-- PUBLIC LANES API
464timer = function( linda, key, a, period )
432 465
433 if a==0.0 then 466 if a==0.0 then
434 -- Caller expects to get current time stamp in Linda, on return 467 -- Caller expects to get current time stamp in Linda, on return
@@ -452,6 +485,7 @@ function timer( linda, key, a, period )
452 timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period ) 485 timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period )
453end 486end
454 487
488end -- _params.with_timers
455 489
456---=== Lock & atomic generators ===--- 490---=== Lock & atomic generators ===---
457 491
@@ -468,7 +502,8 @@ end
468-- Returns an access function that allows 'N' simultaneous entries between 502-- Returns an access function that allows 'N' simultaneous entries between
469-- acquire (+M) and release (-M). For binary locks, use M==1. 503-- acquire (+M) and release (-M). For binary locks, use M==1.
470-- 504--
471function genlock( linda, key, N ) 505-- PUBLIC LANES API
506local function genlock( linda, key, N )
472 linda:limit(key,N) 507 linda:limit(key,N)
473 linda:set(key,nil) -- clears existing data 508 linda:set(key,nil) -- clears existing data
474 509
@@ -501,7 +536,8 @@ end
501-- Returns an access function that allows atomic increment/decrement of the 536-- Returns an access function that allows atomic increment/decrement of the
502-- number in 'key'. 537-- number in 'key'.
503-- 538--
504function genatomic( linda, key, initial_val ) 539-- PUBLIC LANES API
540local function genatomic( linda, key, initial_val )
505 linda:limit(key,2) -- value [,true] 541 linda:limit(key,2) -- value [,true]
506 linda:set(key,initial_val or 0.0) -- clears existing data (also queue) 542 linda:set(key,initial_val or 0.0) -- clears existing data (also queue)
507 543
@@ -517,4 +553,29 @@ end
517 553
518-- newuserdata = mm.newuserdata 554-- newuserdata = mm.newuserdata
519 555
556 -- activate full interface
557 lanes.gen = gen
558 lanes.linda = mm.linda
559 lanes.timer = timer
560 lanes.genlock = genlock
561 lanes.genatomic = genatomic
562 -- from now on, calling configure does nothing but checking that we don't call it with parameters that changed compared to the first invocation
563 lanes.configure = function( _params2)
564 _params2 = _params2 or _params
565 if _params2.nb_keepers ~= _params.nb_keepers then
566 error( "mismatched configuration: " .. tostring( _params2.nb_keepers) .. " keepers instead of " .. tostring( _params.nb_keepers))
567 end
568 if _params2.with_timers ~= _params.with_timers then
569 error( "mismatched configuration: " .. tostring( _params2.with_timers) .. " timer activity instead of " .. tostring( _params.with_timers))
570 end
571 if _params2.on_create_state and _params2.on_create_state ~= _params.on_create_state then
572 error( "mismatched configuration: " .. tostring( _params2.on_create_state) .. " timer activity instead of " .. tostring( _params.on_create_state))
573 end
574 return lanes
575 end
576 return lanes
577end -- lanes.configure
578
520--the end 579--the end
580return lanes
581
diff --git a/src/threading.c b/src/threading.c
index 00be243..8966dc6 100644
--- a/src/threading.c
+++ b/src/threading.c
@@ -41,9 +41,9 @@ THE SOFTWARE.
41#include "threading.h" 41#include "threading.h"
42#include "lua.h" 42#include "lua.h"
43 43
44#if !((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)) 44#if THREADAPI == THREADAPI_PTHREAD
45# include <sys/time.h> 45# include <sys/time.h>
46#endif 46#endif // THREADAPI == THREADAPI_PTHREAD
47 47
48 48
49#if defined(PLATFORM_LINUX) || defined(PLATFORM_CYGWIN) 49#if defined(PLATFORM_LINUX) || defined(PLATFORM_CYGWIN)
@@ -71,12 +71,15 @@ THE SOFTWARE.
71* FAIL is for unexpected API return values - essentially programming 71* FAIL is for unexpected API return values - essentially programming
72* error in _this_ code. 72* error in _this_ code.
73*/ 73*/
74#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 74#if THREADAPI == THREADAPI_WINDOWS
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#ifdef _MSC_VER
78 __debugbreak(); // give a chance to the debugger!
79#endif // _MSC_VER
77 abort(); 80 abort();
78} 81}
79#endif 82#endif // THREADAPI == THREADAPI_WINDOWS
80 83
81 84
82/* 85/*
@@ -87,7 +90,7 @@ static void FAIL( const char *funcname, int rc ) {
87*/ 90*/
88time_d now_secs(void) { 91time_d now_secs(void) {
89 92
90#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 93#if THREADAPI == THREADAPI_WINDOWS
91 /* 94 /*
92 * Windows FILETIME values are "100-nanosecond intervals since 95 * Windows FILETIME values are "100-nanosecond intervals since
93 * January 1, 1601 (UTC)" (MSDN). Well, we'd want Unix Epoch as 96 * January 1, 1601 (UTC)" (MSDN). Well, we'd want Unix Epoch as
@@ -142,7 +145,7 @@ time_d now_secs(void) {
142 // <= 2.0.2 code 145 // <= 2.0.2 code
143 return (double)(uli.QuadPart - uli_epoch.QuadPart) / 10000000.0; 146 return (double)(uli.QuadPart - uli_epoch.QuadPart) / 10000000.0;
144# endif 147# endif
145#else 148#else // THREADAPI == THREADAPI_PTHREAD
146 struct timeval tv; 149 struct timeval tv;
147 // { 150 // {
148 // time_t tv_sec; /* seconds since Jan. 1, 1970 */ 151 // time_t tv_sec; /* seconds since Jan. 1, 1970 */
@@ -153,7 +156,7 @@ time_d now_secs(void) {
153 assert( rc==0 ); 156 assert( rc==0 );
154 157
155 return ((double)tv.tv_sec) + ((tv.tv_usec)/1000) / 1000.0; 158 return ((double)tv.tv_sec) + ((tv.tv_usec)/1000) / 1000.0;
156#endif 159#endif // THREADAPI THREADAPI_PTHREAD
157} 160}
158 161
159 162
@@ -165,7 +168,7 @@ time_d SIGNAL_TIMEOUT_PREPARE( double secs ) {
165} 168}
166 169
167 170
168#if !((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)) 171#if THREADAPI == THREADAPI_PTHREAD
169/* 172/*
170* Prepare 'abs_secs' kind of timeout to 'timespec' format 173* Prepare 'abs_secs' kind of timeout to 'timespec' format
171*/ 174*/
@@ -184,7 +187,7 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
184 ts->tv_sec = ts->tv_sec + 1; 187 ts->tv_sec = ts->tv_sec + 1;
185 } 188 }
186} 189}
187#endif 190#endif // THREADAPI == THREADAPI_PTHREAD
188 191
189 192
190/*---=== Threading ===---*/ 193/*---=== Threading ===---*/
@@ -227,7 +230,7 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
227# endif 230# endif
228#endif 231#endif
229 232
230#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 233#if THREADAPI == THREADAPI_WINDOWS
231 // 234 //
232 void MUTEX_INIT( MUTEX_T *ref ) { 235 void MUTEX_INIT( MUTEX_T *ref ) {
233 *ref= CreateMutex( NULL /*security attr*/, FALSE /*not locked*/, NULL ); 236 *ref= CreateMutex( NULL /*security attr*/, FALSE /*not locked*/, NULL );
@@ -279,10 +282,11 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
279 *ref= h; 282 *ref= h;
280 } 283 }
281 // 284 //
282 bool_t THREAD_WAIT( THREAD_T *ref, double secs ) { 285bool_t THREAD_WAIT_IMPL( THREAD_T *ref, double secs)
283 long ms= (long)((secs*1000.0)+0.5); 286{
287 DWORD ms = (secs<0.0) ? INFINITE : (DWORD)((secs*1000.0)+0.5);
284 288
285 DWORD rc= WaitForSingleObject( *ref, ms<0 ? INFINITE:ms /*timeout*/ ); 289 DWORD rc= WaitForSingleObject( *ref, ms /*timeout*/ );
286 // 290 //
287 // (WAIT_ABANDONED) 291 // (WAIT_ABANDONED)
288 // WAIT_OBJECT_0 success (0) 292 // WAIT_OBJECT_0 success (0)
@@ -370,7 +374,7 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
370 if (!PulseEvent( *ref )) 374 if (!PulseEvent( *ref ))
371 FAIL( "PulseEvent", GetLastError() ); 375 FAIL( "PulseEvent", GetLastError() );
372 } 376 }
373#else 377#else // THREADAPI == THREADAPI_PTHREAD
374 // PThread (Linux, OS X, ...) 378 // PThread (Linux, OS X, ...)
375 // 379 //
376 // On OS X, user processes seem to be able to change priorities. 380 // On OS X, user processes seem to be able to change priorities.
@@ -649,7 +653,7 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
649 } 653 }
650 } 654 }
651 // 655 //
652 /* 656 /*
653 * Wait for a thread to finish. 657 * Wait for a thread to finish.
654 * 658 *
655 * 'mu_ref' is a lock we should use for the waiting; initially unlocked. 659 * 'mu_ref' is a lock we should use for the waiting; initially unlocked.
@@ -657,11 +661,7 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
657 * 661 *
658 * Returns TRUE for succesful wait, FALSE for timed out 662 * Returns TRUE for succesful wait, FALSE for timed out
659 */ 663 */
660#ifdef PTHREAD_TIMEDJOIN 664bool_t THREAD_WAIT( THREAD_T *ref, double secs , SIGNAL_T *signal_ref, MUTEX_T *mu_ref, volatile enum e_status *st_ref)
661 bool_t THREAD_WAIT( THREAD_T *ref, double secs )
662#else
663 bool_t THREAD_WAIT( THREAD_T *ref, SIGNAL_T *signal_ref, MUTEX_T *mu_ref, volatile enum e_status *st_ref, double secs )
664#endif
665{ 665{
666 struct timespec ts_store; 666 struct timespec ts_store;
667 const struct timespec *timeout= NULL; 667 const struct timespec *timeout= NULL;
@@ -669,16 +669,17 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
669 669
670 // Do timeout counting before the locks 670 // Do timeout counting before the locks
671 // 671 //
672#ifdef PTHREAD_TIMEDJOIN 672#if THREADWAIT_METHOD == THREADWAIT_TIMEOUT
673 if (secs>=0.0) { 673 if (secs>=0.0)
674#else 674#else // THREADWAIT_METHOD == THREADWAIT_CONDVAR
675 if (secs>0.0) { 675 if (secs>0.0)
676#endif 676#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
677 {
677 prepare_timeout( &ts_store, now_secs()+secs ); 678 prepare_timeout( &ts_store, now_secs()+secs );
678 timeout= &ts_store; 679 timeout= &ts_store;
679 } 680 }
680 681
681#ifdef PTHREAD_TIMEDJOIN 682#if THREADWAIT_METHOD == THREADWAIT_TIMEOUT
682 /* Thread is joinable 683 /* Thread is joinable
683 */ 684 */
684 if (!timeout) { 685 if (!timeout) {
@@ -691,10 +692,11 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
691 } 692 }
692 done= rc==0; 693 done= rc==0;
693 } 694 }
694#else 695#else // THREADWAIT_METHOD == THREADWAIT_CONDVAR
695 /* Since we've set the thread up as PTHREAD_CREATE_DETACHED, we cannot 696 /* Since we've set the thread up as PTHREAD_CREATE_DETACHED, we cannot
696 * join with it. Use the cond.var. 697 * join with it. Use the cond.var.
697 */ 698 */
699 (void) ref; // unused
698 MUTEX_LOCK( mu_ref ); 700 MUTEX_LOCK( mu_ref );
699 701
700 // 'secs'==0.0 does not need to wait, just take the current status 702 // 'secs'==0.0 does not need to wait, just take the current status
@@ -714,13 +716,13 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
714 done= *st_ref >= DONE; // DONE|ERROR_ST|CANCELLED 716 done= *st_ref >= DONE; // DONE|ERROR_ST|CANCELLED
715 717
716 MUTEX_UNLOCK( mu_ref ); 718 MUTEX_UNLOCK( mu_ref );
717#endif 719#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
718 return done; 720 return done;
719 } 721 }
720 // 722 //
721 void THREAD_KILL( THREAD_T *ref ) { 723 void THREAD_KILL( THREAD_T *ref ) {
722 pthread_cancel( *ref ); 724 pthread_cancel( *ref );
723 } 725 }
724#endif 726#endif // THREADAPI == THREADAPI_PTHREAD
725 727
726static const lua_Alloc alloc_f= 0; 728static const lua_Alloc alloc_f= 0;
diff --git a/src/threading.h b/src/threading.h
index 2bf48e5..b0a3db0 100644
--- a/src/threading.h
+++ b/src/threading.h
@@ -1,8 +1,8 @@
1/* 1/*
2* THREADING.H 2* THREADING.H
3*/ 3*/
4#ifndef THREADING_H 4#ifndef __threading_h__
5#define THREADING_H 5#define __threading_h__ 1
6 6
7/* Platform detection 7/* Platform detection
8*/ 8*/
@@ -38,11 +38,19 @@ typedef unsigned int uint_t;
38*/ 38*/
39enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED }; 39enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED };
40 40
41#define THREADAPI_WINDOWS 1
42#define THREADAPI_PTHREAD 2
43
44#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
45#define THREADAPI THREADAPI_WINDOWS
46#else // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
47#define THREADAPI THREADAPI_PTHREAD
48#endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
41 49
42/*---=== Locks & Signals ===--- 50/*---=== Locks & Signals ===---
43*/ 51*/
44 52
45#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 53#if THREADAPI == THREADAPI_WINDOWS
46 #define WIN32_LEAN_AND_MEAN 54 #define WIN32_LEAN_AND_MEAN
47 // 'SignalObjectAndWait' needs this (targets Windows 2000 and above) 55 // 'SignalObjectAndWait' needs this (targets Windows 2000 and above)
48 #define _WIN32_WINNT 0x0400 56 #define _WIN32_WINNT 0x0400
@@ -61,12 +69,13 @@ enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED };
61 void MUTEX_LOCK( MUTEX_T *ref ); 69 void MUTEX_LOCK( MUTEX_T *ref );
62 void MUTEX_UNLOCK( MUTEX_T *ref ); 70 void MUTEX_UNLOCK( MUTEX_T *ref );
63 71
64 typedef unsigned THREAD_RETURN_T; 72 typedef unsigned int THREAD_RETURN_T;
65 73
66 #define SIGNAL_T HANDLE 74 #define SIGNAL_T HANDLE
67 75
68 #define YIELD() Sleep(0) 76 #define YIELD() Sleep(0)
69#else 77 #define THREAD_CALLCONV __stdcall
78#else // THREADAPI == THREADAPI_PTHREAD
70 // PThread (Linux, OS X, ...) 79 // PThread (Linux, OS X, ...)
71 // 80 //
72 #include <pthread.h> 81 #include <pthread.h>
@@ -107,7 +116,8 @@ enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED };
107 #else 116 #else
108 #define YIELD() pthread_yield() 117 #define YIELD() pthread_yield()
109 #endif 118 #endif
110#endif 119 #define THREAD_CALLCONV
120#endif //THREADAPI == THREADAPI_PTHREAD
111 121
112void SIGNAL_INIT( SIGNAL_T *ref ); 122void SIGNAL_INIT( SIGNAL_T *ref );
113void SIGNAL_FREE( SIGNAL_T *ref ); 123void SIGNAL_FREE( SIGNAL_T *ref );
@@ -129,9 +139,10 @@ bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout );
129/*---=== Threading ===--- 139/*---=== Threading ===---
130*/ 140*/
131 141
132#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 142#if THREADAPI == THREADAPI_WINDOWS
133 143
134 typedef HANDLE THREAD_T; 144 typedef HANDLE THREAD_T;
145# define THREAD_ISNULL( _h) (_h == 0)
135 // 146 //
136 void THREAD_CREATE( THREAD_T *ref, 147 void THREAD_CREATE( THREAD_T *ref,
137 THREAD_RETURN_T (__stdcall *func)( void * ), 148 THREAD_RETURN_T (__stdcall *func)( void * ),
@@ -140,7 +151,7 @@ bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout );
140# define THREAD_PRIO_MIN (-3) 151# define THREAD_PRIO_MIN (-3)
141# define THREAD_PRIO_MAX (+3) 152# define THREAD_PRIO_MAX (+3)
142 153
143#else 154#else // THREADAPI == THREADAPI_PTHREAD
144 /* Platforms that have a timed 'pthread_join()' can get away with a simpler 155 /* Platforms that have a timed 'pthread_join()' can get away with a simpler
145 * implementation. Others will use a condition variable. 156 * implementation. Others will use a condition variable.
146 */ 157 */
@@ -154,6 +165,7 @@ bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout );
154# endif 165# endif
155 166
156 typedef pthread_t THREAD_T; 167 typedef pthread_t THREAD_T;
168# define THREAD_ISNULL( _h) 0 // pthread_t may be a structure: never 'null' by itself
157 169
158 void THREAD_CREATE( THREAD_T *ref, 170 void THREAD_CREATE( THREAD_T *ref,
159 THREAD_RETURN_T (*func)( void * ), 171 THREAD_RETURN_T (*func)( void * ),
@@ -171,19 +183,30 @@ bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout );
171# define THREAD_PRIO_MIN (-2) 183# define THREAD_PRIO_MIN (-2)
172# define THREAD_PRIO_MAX (+2) 184# define THREAD_PRIO_MAX (+2)
173# endif 185# endif
174#endif 186#endif // THREADAPI == THREADAPI_WINDOWS
175 187
176/* 188/*
177* Win32 and PTHREAD_TIMEDJOIN allow waiting for a thread with a timeout. 189* Win32 and PTHREAD_TIMEDJOIN allow waiting for a thread with a timeout.
178* Posix without PTHREAD_TIMEDJOIN needs to use a condition variable approach. 190* Posix without PTHREAD_TIMEDJOIN needs to use a condition variable approach.
179*/ 191*/
180#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) 192#define THREADWAIT_TIMEOUT 1
181 bool_t THREAD_WAIT( THREAD_T *ref, double secs ); 193#define THREADWAIT_CONDVAR 2
182#else 194
183 bool_t THREAD_WAIT( THREAD_T *ref, SIGNAL_T *signal_ref, MUTEX_T *mu_ref, volatile enum e_status *st_ref, double secs ); 195#if THREADAPI == THREADAPI_WINDOWS || (defined PTHREAD_TIMEDJOIN)
184#endif 196#define THREADWAIT_METHOD THREADWAIT_TIMEOUT
197#else // THREADAPI == THREADAPI_WINDOWS || (defined PTHREAD_TIMEDJOIN)
198#define THREADWAIT_METHOD THREADWAIT_CONDVAR
199#endif // THREADAPI == THREADAPI_WINDOWS || (defined PTHREAD_TIMEDJOIN)
200
201
202#if THREADWAIT_METHOD == THREADWAIT_TIMEOUT
203bool_t THREAD_WAIT_IMPL( THREAD_T *ref, double secs);
204#define THREAD_WAIT( a, b, c, d, e) THREAD_WAIT_IMPL( a, b)
205#else // THREADWAIT_METHOD == THREADWAIT_CONDVAR
206bool_t THREAD_WAIT_IMPL( THREAD_T *ref, double secs, SIGNAL_T *signal_ref, MUTEX_T *mu_ref, volatile enum e_status *st_ref);
207#define THREAD_WAIT THREAD_WAIT_IMPL
208#endif // // THREADWAIT_METHOD == THREADWAIT_CONDVAR
185 209
186void THREAD_KILL( THREAD_T *ref ); 210void THREAD_KILL( THREAD_T *ref );
187 211
188#endif 212#endif // __threading_h__
189 // THREADING_H
diff --git a/src/tools.c b/src/tools.c
index d475fc0..212cf52 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 //
@@ -90,7 +91,7 @@ void luaG_dump( lua_State* L ) {
90} 91}
91 92
92 93
93/*---=== luaG_openlibs ===---*/ 94/*---=== luaG_newstate ===---*/
94 95
95static const luaL_Reg libs[] = { 96static const luaL_Reg libs[] = {
96 { LUA_LOADLIBNAME, luaopen_package }, 97 { LUA_LOADLIBNAME, luaopen_package },
@@ -108,21 +109,303 @@ 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 (void)L; (void)p; (void)sz; (void) ud; // unused
140 return 666;
141}
142
143
144/*
145 * differentiation between C, bytecode and JIT-fast functions
146 *
147 *
148 * +----------+------------+----------+
149 * | bytecode | C function | JIT-fast |
150 * +-----------------+----------+------------+----------+
151 * | lua_topointer | | | |
152 * +-----------------+----------+------------+----------+
153 * | lua_tocfunction | NULL | | NULL |
154 * +-----------------+----------+------------+----------+
155 * | lua_dump | 666 | 1 | 1 |
156 * +-----------------+----------+------------+----------+
157 */
158
159typedef enum
160{
161 FST_Bytecode,
162 FST_Native,
163 FST_FastJIT
164} FuncSubType;
165
166FuncSubType luaG_getfuncsubtype( lua_State *L, int _i)
167{
168 if( lua_tocfunction( L, _i))
169 {
170 return FST_Native;
171 }
172 {
173 int mustpush = 0, dumpres;
174 if( STACK_ABS( L, _i) != lua_gettop( L))
175 {
176 lua_pushvalue( L, _i);
177 mustpush = 1;
178 }
179 // the provided writer fails with code 666
180 // therefore, anytime we get 666, this means that lua_dump() attempted a dump
181 // all other cases mean this is either a C or LuaJIT-fast function
182 dumpres = lua_dump( L, dummy_writer, NULL);
183 lua_pop( L, mustpush);
184 if( dumpres == 666)
185 {
186 return FST_Bytecode;
187 }
188 }
189 return FST_FastJIT;
190}
191
192static lua_CFunction luaG_tocfunction( lua_State *L, int _i, FuncSubType *_out)
193{
194 lua_CFunction p = lua_tocfunction( L, _i);
195 *_out = luaG_getfuncsubtype( L, _i);
196 return p;
197}
198
199
200#define LOOKUP_KEY "ddea37aa-50c7-4d3f-8e0b-fb7a9d62bac5"
201#define LOOKUP_KEY_CACHE "d1059270-4976-4193-a55b-c952db5ab7cd"
202
203
204// inspired from tconcat() in ltablib.c
205static char const * luaG_pushFQN(lua_State *L, int t, int last)
206{
207 int i = 1;
208 luaL_Buffer b;
209 STACK_CHECK( L)
210 luaL_buffinit(L, &b);
211 for( ; i < last; i++)
212 {
213 lua_rawgeti( L, t, i);
214 luaL_addvalue( &b);
215 luaL_addlstring(&b, ".", 1);
216 }
217 if (i == last) /* add last value (if interval was not empty) */
218 {
219 lua_rawgeti( L, t, i);
220 luaL_addvalue( &b);
221 }
222 luaL_pushresult( &b);
223 STACK_END( L, 1)
224 return lua_tostring( L, -1);
225}
226
227
228static void populate_func_lookup_table_recur( lua_State *L, int _ctx_base, int _i, int _depth)
229{
230 lua_Integer visit_count;
231 // slot 1 in the stack contains the table that receives everything we found
232 int const dest = _ctx_base;
233 // slot 2 contains a table that, when concatenated, produces the fully qualified name of scanned elements in the table provided at slot _i
234 int const fqn = _ctx_base + 1;
235 // slot 3 contains a cache that stores all already visited tables to avoid infinite recursion loops
236 int const cache = _ctx_base + 2;
237 // we need to remember subtables to process them after functions encountered at the current depth (breadth-first search)
238 int const breadth_first_cache = lua_gettop( L) + 1;
239
240 STACK_GROW( L, 6);
241 // slot _i contains a table where we search for functions
242 STACK_CHECK( L) // ... {_i}
243
244 // if table is already visited, we are done
245 lua_pushvalue( L, _i); // ... {_i} {}
246 lua_rawget( L, cache); // ... {_i} nil|n
247 visit_count = lua_tointeger( L, -1); // 0 if nil, else n
248 lua_pop( L, 1); // ... {_i}
249 STACK_MID( L, 0)
250 if( visit_count > 0)
251 {
252 return;
253 }
254
255 // remember we visited this table (1-visit count)
256 lua_pushvalue( L, _i); // ... {_i} {}
257 lua_pushinteger( L, visit_count + 1); // ... {_i} {} 1
258 lua_rawset( L, cache); // ... {_i}
259 STACK_MID( L, 0)
260
261 // this table is at breadth_first_cache index
262 lua_newtable( L); // ... {_i} {bfc}
263 ASSERT_L( lua_gettop( L) == breadth_first_cache);
264 // iterate over all entries in the processed table
265 lua_pushnil( L); // ... {_i} {bfc} nil
266 while( lua_next( L, _i) != 0) // ... {_i} {bfc} k v
267 {
268 // just for debug, not actually needed
269 //char const * key = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : "not a string";
270 // subtable: process it recursively
271 if( lua_istable( L, -1)) // ... {_i} {bfc} k {}
272 {
273 // increment visit count to make sure we will actually scan it at this recursive level
274 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {}
275 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {} {}
276 lua_rawget( L, cache); // ... {_i} {bfc} k {} {} n?
277 visit_count = lua_tointeger( L, -1) + 1; // 1 if we got nil, else n+1
278 lua_pop( L, 1); // ... {_i} {bfc} k {} {}
279 lua_pushinteger( L, visit_count); // ... {_i} {bfc} k {} {} n
280 lua_rawset( L, cache); // ... {_i} {bfc} k {}
281 // store the table in the breadth-first cache
282 lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k
283 lua_insert( L, -2); // ... {_i} {bfc} k k {}
284 lua_rawset( L, breadth_first_cache); // ... {_i} {bfc} k
285 STACK_MID( L, 2)
286 }
287 else if( lua_isfunction( L, -1)) // ... {_i} {bfc} k func
288 {
289 if( luaG_getfuncsubtype( L, -1) != FST_Bytecode)
290 {
291 //char const *fqnString; for debugging
292 bool_t not_registered;
293 // first, skip everything if the function is already known
294 lua_pushvalue( L, -1); // ... {_i} {bfc} k func func
295 lua_rawget( L, dest); // ... {_i} {bfc} k func name?
296 not_registered = lua_isnil( L, -1);
297 lua_pop( L, 1); // ... {_i} {bfc} k func
298 if( not_registered)
299 {
300 ++ _depth;
301 // push function name in fqn stack (note that concatenation will crash if name is a not string!)
302 lua_pushvalue( L, -2); // ... {_i} {bfc} k func k
303 lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} k func
304 // generate name
305 /*fqnString =*/ (void) luaG_pushFQN( L, fqn, _depth); // ... {_i} {bfc} k func "f.q.n"
306 //puts( fqnString);
307 // prepare the stack for database feed
308 lua_pushvalue( L, -1); // ... {_i} {bfc} k func "f.q.n" "f.q.n"
309 lua_pushvalue( L, -3); // ... {_i} {bfc} k func "f.q.n" "f.q.n" func
310 // t["f.q.n"] = func
311 lua_rawset( L, dest); // ... {_i} {bfc} k func "f.q.n"
312 // t[func] = "f.q.n"
313 lua_rawset( L, dest); // ... {_i} {bfc} k
314 // remove table name from fqn stack
315 lua_pushnil( L); // ... {_i} {bfc} k nil
316 lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} k
317 -- _depth;
318 }
319 else
320 {
321 lua_pop( L, 1); // ... {_i} {bfc} k
322 }
323 }
324 else
325 {
326 lua_pop( L, 1); // ... {_i} {bfc} k
327 }
328 }
329 else
330 {
331 lua_pop( L, 1); // ... {_i} {bfc} k
332 }
333 STACK_MID( L, 2)
334 }
335 // now process the tables we encountered at that depth
336 ++ _depth;
337 lua_pushnil( L); // ... {_i} {bfc} nil
338 while( lua_next( L, breadth_first_cache) != 0) // ... {_i} {bfc} k {}
339 {
340 // un-visit this table in case we do need to process it
341 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {}
342 lua_rawget( L, cache); // ... {_i} {bfc} k {} n
343 ASSERT_L( lua_type( L, -1) == LUA_TNUMBER);
344 visit_count = lua_tointeger( L, -1) - 1;
345 lua_pop( L, 1); // ... {_i} {bfc} k {}
346 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {}
347 if( visit_count > 0)
348 {
349 lua_pushinteger( L, visit_count); // ... {_i} {bfc} k {} {} n
350 }
351 else
352 {
353 lua_pushnil( L); // ... {_i} {bfc} k {} {} nil
354 }
355 lua_rawset( L, cache); // ... {_i} {bfc} k {}
356 // push table name in fqn stack (note that concatenation will crash if name is a not string!)
357 lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k
358 lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} k {}
359 populate_func_lookup_table_recur( L, _ctx_base, lua_gettop( L), _depth); // ... {_i} {bfc} k {}
360 lua_pop( L, 1); // ... {_i} {bfc} k
361 STACK_MID( L, 2)
362 }
363 // remove table name from fqn stack
364 lua_pushnil( L); // ... {_i} {bfc} nil
365 lua_rawseti( L, fqn, _depth); // ... {_i} {bfc}
366 -- _depth;
367 // we are done with our cache
368 lua_pop( L, 1); // ... {_i}
369 STACK_END( L, 0)
370 // we are done // ... {_i} {bfc}
371}
372
373/*
374 * create a "fully.qualified.name" <-> function equivalence database
375 */
376void populate_func_lookup_table( lua_State *L, int _i, char const *_name)
377{
378 int const ctx_base = lua_gettop( L) + 1;
379 int const in_base = STACK_ABS( L, _i);
380 int const start_depth = _name ? 1 : 0;
381 //printf( "%p: populate_func_lookup_table('%s')\n", L, _name ? _name : "NULL");
382 STACK_GROW( L, 3);
383 STACK_CHECK( L)
384 lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // {}?
385 if( lua_isnil( L, -1)) // nil
386 {
387 lua_pop( L, 1); //
388 lua_newtable( L); // {}
389 lua_pushvalue( L, -1); // {} {}
390 lua_setfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // {}
391 }
392 lua_newtable( L); // {} {fqn}
393 if( _name)
394 {
395 lua_pushstring( L, _name); // {} {fqn} "name"
396 lua_rawseti( L, -2, start_depth); // {} {fqn}
397 }
398 lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY_CACHE); // {} {fqn} {cache}?
399 if( lua_isnil( L, -1))
400 {
401 lua_pop( L, 1); // {} {fqn}
402 lua_newtable( L); // {} {fqn} {cache}
403 lua_pushvalue( L, -1); // {} {fqn} {cache} {cache}
404 lua_setfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY_CACHE); // {} {fqn} {cache}
405 }
406 populate_func_lookup_table_recur( L, ctx_base, in_base, start_depth); // {...} {fqn} {cache}
407 lua_pop( L, 3);
408 STACK_END( L, 0)
126} 409}
127 410
128/* 411/*
@@ -139,33 +422,52 @@ static bool_t openlib( lua_State *L, const char *name, size_t len ) {
139*/ 422*/
140#define is_name_char(c) (isalpha(c) || (c)=='*') 423#define is_name_char(c) (isalpha(c) || (c)=='*')
141 424
142const char *luaG_openlibs( lua_State *L, const char *libs ) { 425lua_State* luaG_newstate( char const* libs, lua_CFunction _on_state_create)
143 const char *p; 426{
144 unsigned len; 427 char const* p;
145 428 unsigned int len;
146 if (!libs) return NULL; // no libs, not even 'base' 429 lua_State* const L = luaL_newstate();
147
148 // 'lua.c' stops GC during initialization so perhaps its a good idea. :)
149 //
150 lua_gc(L, LUA_GCSTOP, 0);
151 430
152 // Anything causes 'base' to be taken in 431 // no libs, or special init func (not even 'base')
153 // 432 if (libs || _on_state_create)
154 STACK_GROW(L,2); 433 {
155 lua_pushcfunction( L, luaopen_base ); 434 // 'lua.c' stops GC during initialization so perhaps its a good idea. :)
156 lua_pushliteral( L, "" ); 435 //
157 lua_call( L, 1, 0 ); 436 lua_gc( L, LUA_GCSTOP, 0);
158
159 for( p= libs; *p; p+=len ) {
160 len=0;
161 while (*p && !is_name_char(*p)) p++; // bypass delimiters
162 while (is_name_char(p[len])) len++; // bypass name
163 if (len && (!openlib( L, p, len )))
164 break;
165 }
166 lua_gc(L, LUA_GCRESTART, 0);
167 437
168 return *p ? p : NULL; 438 // Anything causes 'base' to be taken in
439 //
440 STACK_GROW( L, 2);
441 STACK_CHECK( L)
442 if( _on_state_create)
443 {
444 lua_pushcfunction( L, _on_state_create);
445 lua_call( L, 0, 0);
446 }
447 if( libs)
448 {
449 lua_pushcfunction( L, luaopen_base);
450 lua_call( L, 0, 0);
451 }
452 // after opening base, register the functions it exported in our name<->function database
453 populate_func_lookup_table( L, LUA_GLOBALSINDEX, NULL);
454 STACK_MID( L, 0);
455 if( libs)
456 {
457 for( p = libs; *p; p += len)
458 {
459 len=0;
460 while (*p && !is_name_char(*p)) p++; // bypass delimiters
461 while (is_name_char(p[len])) len++; // bypass name
462 if (len && (!openlib( L, p, len )))
463 break;
464 }
465 serialize_require( L);
466 }
467 STACK_END(L,0)
468 lua_gc( L, LUA_GCRESTART, 0);
469 }
470 return L;
169} 471}
170 472
171 473
@@ -284,7 +586,7 @@ luaG_IdFunction get_idfunc( lua_State *L, int index )
284{ 586{
285 luaG_IdFunction ret; 587 luaG_IdFunction ret;
286 588
287 index= STACK_ABS(L,index); 589 index = STACK_ABS( L, index);
288 590
289 STACK_GROW(L,1); 591 STACK_GROW(L,1);
290 592
@@ -696,7 +998,7 @@ uint_t get_mt_id( lua_State *L, int i ) {
696 static uint_t last_id= 0; 998 static uint_t last_id= 0;
697 uint_t id; 999 uint_t id;
698 1000
699 i= STACK_ABS(L,i); 1001 i = STACK_ABS( L, i);
700 1002
701 STACK_GROW(L,3); 1003 STACK_GROW(L,3);
702 1004
@@ -819,8 +1121,8 @@ static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uin
819 1121
820static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) 1122static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i )
821{ 1123{
1124 void * const aspointer = (void*)lua_topointer( L, i );
822 // TBD: Merge this and same code for tables 1125 // TBD: Merge this and same code for tables
823
824 ASSERT_L( L2_cache_i != 0 ); 1126 ASSERT_L( L2_cache_i != 0 );
825 1127
826 STACK_GROW(L2,3); 1128 STACK_GROW(L2,3);
@@ -832,7 +1134,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 1134 // 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). 1135 // is only for the duration of a copy (both states are locked).
834 // 1136 //
835 lua_pushlightuserdata( L2, (void*)lua_topointer( L, i )); // push a light userdata uniquely representing the function 1137 lua_pushlightuserdata( L2, aspointer); // push a light userdata uniquely representing the function
836 1138
837 //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); 1139 //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) );
838 1140
@@ -886,9 +1188,48 @@ static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, ui
886 // 1188 //
887 // L2 [-1]: function 1189 // L2 [-1]: function
888 1190
889 ASSERT_L( lua_isfunction(L2,-1) ); 1191 ASSERT_L( lua_isfunction(L2,-1));
1192}
1193
1194/*
1195* Push a looked-up native/LuaJIT function.
1196*/
1197static void lookup_native_func( lua_State *L2, lua_State *L, uint_t i)
1198{
1199 char const *fqn;
1200 size_t len;
1201 _ASSERT_L( L, lua_isfunction( L, i));
1202 STACK_CHECK( L)
1203 STACK_CHECK( L2)
1204 // fetch the name from the source state's lookup table
1205 lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // {}
1206 _ASSERT_L( L, lua_istable( L, -1));
1207 lua_pushvalue( L, i); // {} f
1208 lua_rawget( L, -2); // {} "f.q.n"
1209 fqn = lua_tolstring( L, -1, &len);
1210 if( !fqn)
1211 {
1212 luaL_error( L, "function not found in origin transfer database.");
1213 }
1214 // push the equivalent function in the destination's stack, retrieved from the lookup table
1215 lua_getfield( L2, LUA_REGISTRYINDEX, LOOKUP_KEY); // {}
1216 _ASSERT_L( L2, lua_istable( L2, -1));
1217 lua_pushlstring( L2, fqn, len); // {} "f.q.n"
1218 lua_pop( L, 2); //
1219 lua_rawget( L2, -2); // {} f
1220 if( !lua_isfunction( L2, -1))
1221 {
1222 // yarglah: luaL_error formatting doesn't support string width modifier!
1223 char message[256];
1224 sprintf( message, "function %*s not found in destination transfer database.", len, fqn);
1225 luaL_error( L, message);
1226 }
1227 lua_remove( L2, -2); // f
1228 STACK_END( L2, 1)
1229 STACK_END( L, 0)
890} 1230}
891 1231
1232#define LOG_FUNC_INFO 0
892 1233
893/* 1234/*
894* Copy a function over, which has not been found in the cache. 1235* Copy a function over, which has not been found in the cache.
@@ -898,107 +1239,131 @@ enum e_vt {
898}; 1239};
899static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i, enum e_vt value_type ); 1240static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i, enum e_vt value_type );
900 1241
901static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) { 1242static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i )
902 1243{
903 lua_CFunction cfunc= lua_tocfunction( L,i ); 1244 FuncSubType funcSubType;
904 unsigned n; 1245 lua_CFunction cfunc = luaG_tocfunction( L, i, &funcSubType); // NULL for LuaJIT-fast && bytecode functions
905
906 ASSERT_L( L2_cache_i != 0 );
907
908 STACK_GROW(L,2);
909
910 STACK_CHECK(L)
911 if (!cfunc) { // Lua function
912 luaL_Buffer b;
913 const char *s;
914 size_t sz;
915 int tmp;
916 const char *name= NULL;
917
918#if 0
919 // "To get information about a function you push it onto the
920 // stack and start the what string with the character '>'."
921 //
922 { lua_Debug ar;
923 lua_pushvalue( L, i );
924 lua_getinfo(L, ">n", &ar); // fills 'name' and 'namewhat', pops function
925 name= ar.namewhat;
926
927 fprintf( stderr, "NAME: %s\n", name ); // just gives NULL
928 }
929#endif
930 // 'lua_dump()' needs the function at top of stack
931 //
932 if (i!=-1) lua_pushvalue( L, i );
933
934 luaL_buffinit(L,&b);
935 tmp= lua_dump(L, buf_writer, &b);
936 ASSERT_L(tmp==0);
937 //
938 // "value returned is the error code returned by the last call
939 // to the writer" (and we only return 0)
940
941 luaL_pushresult(&b); // pushes dumped string on 'L'
942 s= lua_tolstring(L,-1,&sz);
943 ASSERT_L( s && sz );
944
945 if (i!=-1) lua_remove( L, -2 );
946 1246
947 // Note: Line numbers seem to be taken precisely from the 1247 ASSERT_L( L2_cache_i != 0 );
948 // original function. 'name' is not used since the chunk 1248 STACK_GROW(L,2);
949 // is precompiled (it seems...). 1249 STACK_CHECK(L)
950 //
951 // TBD: Can we get the function's original name through, as well?
952 //
953 if (luaL_loadbuffer(L2, s, sz, name) != 0) {
954 // chunk is precompiled so only LUA_ERRMEM can happen
955 // "Otherwise, it pushes an error message"
956 //
957 STACK_GROW( L,1 );
958 luaL_error( L, "%s", lua_tostring(L2,-1) );
959 }
960 lua_pop(L,1); // remove the dumped string
961 STACK_MID(L,0)
962 }
963 1250
964 /* push over any upvalues; references to this function will come from 1251 if( funcSubType == FST_Bytecode)
965 * cache so we don't end up in eternal loop. 1252 {
966 */ 1253 unsigned n;
967 for( n=0; lua_getupvalue( L, i, 1+n ) != NULL; n++ ) { 1254 luaL_Buffer b;
968 if ((!cfunc) && lua_equal(L,i,-1)) { 1255 // 'lua_dump()' needs the function at top of stack
969 /* Lua closure that has a (recursive) upvalue to itself 1256 // if already on top of the stack, no need to push again
970 */ 1257 int needToPush = (i != (uint_t)lua_gettop( L));
971 lua_pushvalue( L2, -((int)n)-1 ); 1258 if( needToPush)
972 } else { 1259 lua_pushvalue( L, i);
973 if (!inter_copy_one_( L2, L2_cache_i, L, lua_gettop(L), VT_NORMAL )) 1260
974 luaL_error( L, "Cannot copy upvalue type '%s'", luaG_typename(L,-1) ); 1261 luaL_buffinit( L, &b);
975 } 1262 //
976 lua_pop(L,1); 1263 // "value returned is the error code returned by the last call
977 } 1264 // to the writer" (and we only return 0)
978 // L2: function + 'n' upvalues (>=0) 1265 // not sure this could ever fail but for memory shortage reasons
1266 if( lua_dump( L, buf_writer, &b) != 0)
1267 {
1268 luaL_error( L, "internal error: function dump failed.");
1269 }
979 1270
980 STACK_MID(L,0) 1271 luaL_pushresult( &b); // pushes dumped string on 'L'
981 1272
982 if (cfunc) { 1273 // if not pushed, no need to pop
983 lua_pushcclosure( L2, cfunc, n ); // eats up upvalues 1274 if( needToPush)
984 } else { 1275 {
985 // Set upvalues (originally set to 'nil' by 'lua_load') 1276 lua_remove( L, -2);
986 // 1277 }
987 int func_index= lua_gettop(L2)-n;
988 1278
989 for( ; n>0; n-- ) { 1279 // transfer the bytecode, then the upvalues, to create a similar closure
990 const char *rc= lua_setupvalue( L2, func_index, n ); 1280 {
991 // 1281 const char *name= NULL;
992 // "assigns the value at the top of the stack to the upvalue and returns its name. 1282
993 // It also pops the value from the stack." 1283 #if LOG_FUNC_INFO
994 1284 // "To get information about a function you push it onto the
995 ASSERT_L(rc); // not having enough slots? 1285 // stack and start the what string with the character '>'."
996 } 1286 //
997 } 1287 {
998 STACK_END(L,0) 1288 lua_Debug ar;
1289 lua_pushvalue( L, i );
1290 lua_getinfo(L, ">nS", &ar); // fills 'name' 'namewhat' and 'linedefined', pops function
1291 name= ar.namewhat;
1292 fprintf( stderr, "NAME: %s @ %d\n", ar.short_src, ar.linedefined); // just gives NULL
1293 }
1294 #endif // LOG_FUNC_INFO
1295 {
1296 const char *s;
1297 size_t sz;
1298 s = lua_tolstring( L, -1, &sz);
1299 ASSERT_L( s && sz);
1300
1301 // Note: Line numbers seem to be taken precisely from the
1302 // original function. 'name' is not used since the chunk
1303 // is precompiled (it seems...).
1304 //
1305 // TBD: Can we get the function's original name through, as well?
1306 //
1307 if (luaL_loadbuffer(L2, s, sz, name) != 0)
1308 {
1309 // chunk is precompiled so only LUA_ERRMEM can happen
1310 // "Otherwise, it pushes an error message"
1311 //
1312 STACK_GROW( L,1);
1313 luaL_error( L, "%s", lua_tostring(L2,-1));
1314 }
1315 lua_pop( L, 1); // remove the dumped string
1316 }
1317 STACK_MID( L, 0)
1318
1319 /* push over any upvalues; references to this function will come from
1320 * cache so we don't end up in eternal loop.
1321 */
1322 for( n=0; lua_getupvalue( L, i, 1+n ) != NULL; n++ )
1323 {
1324 if ((!cfunc) && lua_equal(L,i,-1))
1325 {
1326 /* Lua closure that has a (recursive) upvalue to itself
1327 */
1328 lua_pushvalue( L2, -((int)n)-1 );
1329 }
1330 else
1331 {
1332 if( !inter_copy_one_( L2, L2_cache_i, L, lua_gettop(L), VT_NORMAL))
1333 luaL_error( L, "Cannot copy upvalue type '%s'", luaL_typename( L, -1));
1334 }
1335 lua_pop( L, 1);
1336 }
1337 // L2: function + 'n' upvalues (>=0)
1338
1339 STACK_MID(L,0)
1340
1341 // Set upvalues (originally set to 'nil' by 'lua_load')
1342 {
1343 int func_index = lua_gettop( L2) - n;
1344 for( ; n > 0; -- n)
1345 {
1346 char const *rc = lua_setupvalue( L2, func_index, n);
1347 //
1348 // "assigns the value at the top of the stack to the upvalue and returns its name.
1349 // It also pops the value from the stack."
1350
1351 ASSERT_L(rc); // not having enough slots?
1352 }
1353 }
1354 }
1355 }
1356 else // C function OR LuaJIT fast function!!!
1357 {
1358#if LOG_FUNC_INFO
1359 fprintf( stderr, "NAME: [C] function %p \n", cfunc);
1360#endif // LOG_FUNC_INFO
1361 // No need to transfer upvalues for C/JIT functions since they weren't actually copied, only looked up
1362 lookup_native_func( L2, L, i);
1363 }
1364 STACK_END(L,0)
999} 1365}
1000 1366
1001
1002/* 1367/*
1003* Copies a value from 'L' state (at index 'i') to 'L2' state. Does not remove 1368* Copies a value from 'L' state (at index 'i') to 'L2' state. Does not remove
1004* the original value. 1369* the original value.
@@ -1123,7 +1488,7 @@ static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, u
1123 lua_rawset( L2, -3 ); // add to table (pops key & val) 1488 lua_rawset( L2, -3 ); // add to table (pops key & val)
1124 } else { 1489 } else {
1125 luaL_error( L, "Unable to copy over type '%s' (in %s)", 1490 luaL_error( L, "Unable to copy over type '%s' (in %s)",
1126 luaG_typename(L,val_i), 1491 luaL_typename(L,val_i),
1127 vt==VT_NORMAL ? "table":"metatable" ); 1492 vt==VT_NORMAL ? "table":"metatable" );
1128 } 1493 }
1129 } 1494 }
@@ -1291,32 +1656,36 @@ MUTEX_T require_cs;
1291// 1656//
1292// Upvalues: [1]: original 'require' function 1657// Upvalues: [1]: original 'require' function
1293// 1658//
1294static int new_require( lua_State *L ) 1659static int new_require( lua_State *L)
1295{ 1660{
1296 int rc; 1661 int rc, i;
1297 int args= lua_gettop(L); 1662 int args = lua_gettop( L);
1663 //char const* modname = luaL_checkstring( L, 1);
1298 1664
1299 STACK_GROW(L,1); 1665 STACK_GROW( L, args + 1);
1300 STACK_CHECK(L) 1666 STACK_CHECK( L)
1667
1668 lua_pushvalue( L, lua_upvalueindex(1));
1669 for( i = 1; i <= args; ++ i)
1670 lua_pushvalue( L, i);
1301 1671
1302 // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would 1672 // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would
1303 // leave us locked, blocking any future 'require' calls from other lanes. 1673 // leave us locked, blocking any future 'require' calls from other lanes.
1304 // 1674 //
1305 MUTEX_LOCK( &require_cs); 1675 MUTEX_LOCK( &require_cs);
1306 { 1676 {
1307 lua_pushvalue( L, lua_upvalueindex(1) ); 1677 rc = lua_pcall( L, args, 1 /*retvals*/, 0 /*errfunc*/ );
1308 lua_insert( L, 1 );
1309
1310 rc= lua_pcall( L, args, 1 /*retvals*/, 0 /*errfunc*/ );
1311 // 1678 //
1312 // LUA_ERRRUN / LUA_ERRMEM 1679 // LUA_ERRRUN / LUA_ERRMEM
1313 } 1680 }
1314 MUTEX_UNLOCK( &require_cs); 1681 MUTEX_UNLOCK( &require_cs);
1315 1682
1683 // the required module (or an error message) is left on the stack as returned value by original require function
1684 STACK_END( L, 1)
1685
1316 if (rc) 1686 if (rc)
1317 lua_error(L); // error message already at [-1] 1687 lua_error(L); // error message already at [-1]
1318 1688
1319 STACK_END(L,0)
1320 return 1; 1689 return 1;
1321} 1690}
1322 1691
diff --git a/src/tools.h b/src/tools.h
index a080257..67f9874 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
@@ -46,13 +46,11 @@
46 46
47#define luaG_isany(L,i) (!lua_isnil(L,i)) 47#define luaG_isany(L,i) (!lua_isnil(L,i))
48 48
49#define luaG_typename( L, index ) lua_typename( L, lua_type(L,index) )
50
51typedef void (*luaG_IdFunction)( lua_State *L, char const * const which); 49typedef void (*luaG_IdFunction)( lua_State *L, char const * const which);
52 50
53void luaG_dump( lua_State* L ); 51void luaG_dump( lua_State* L );
54 52
55const char *luaG_openlibs( lua_State *L, const char *libs ); 53lua_State* luaG_newstate( char const* libs, lua_CFunction _on_state_create);
56 54
57int luaG_deep_userdata( lua_State *L, luaG_IdFunction idfunc); 55int luaG_deep_userdata( lua_State *L, luaG_IdFunction idfunc);
58void *luaG_todeep( lua_State *L, luaG_IdFunction idfunc, int index ); 56void *luaG_todeep( lua_State *L, luaG_IdFunction idfunc, int index );
@@ -72,6 +70,7 @@ int luaG_inter_move( lua_State *L, lua_State *L2, uint_t n);
72extern MUTEX_T deep_lock; 70extern MUTEX_T deep_lock;
73extern MUTEX_T mtid_lock; 71extern MUTEX_T mtid_lock;
74 72
73void populate_func_lookup_table( lua_State *L, int _i, char const *_name);
75void serialize_require( lua_State *L); 74void serialize_require( lua_State *L);
76extern MUTEX_T require_cs; 75extern MUTEX_T require_cs;
77 76
diff --git a/tests/appendud.lua b/tests/appendud.lua
index eb1f768..d0efb26 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()
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..31e1199 100644
--- a/tests/atexit.lua
+++ b/tests/atexit.lua
@@ -1,4 +1,5 @@
1require "lanes" 1local lanes = require "lanes"
2lanes.configure()
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..12bdf02 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()
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..eb8c04b 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()
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..656fde3 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()
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..fff008c 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()
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..5974005 100644
--- a/tests/errhangtest.lua
+++ b/tests/errhangtest.lua
@@ -1,4 +1,5 @@
1lanes = require('lanes') 1local lanes = require "lanes"
2lanes.configure()
2 3
3local linda = lanes.linda() 4local linda = lanes.linda()
4 5
diff --git a/tests/error.lua b/tests/error.lua
index 4922846..c4a64e3 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()
8 9
9local function lane() 10local function lane()
10 11
diff --git a/tests/fibonacci.lua b/tests/fibonacci.lua
index 5db1757..452a770 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{ nb_keepers =1, with_timers = false}
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..3c403fa 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()
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..6f186ab 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()
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..d7cf0b2 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()
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..6a9f7aa 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()
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..3278d57 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()
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..f6fe87e 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{ nb_keepers = 1, with_timers = false}
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..40c9e11 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()
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..f3a6740 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()
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
new file mode 100644
index 0000000..fff2670
--- /dev/null
+++ b/tests/linda_perf.lua
@@ -0,0 +1,224 @@
1local lanes = require "lanes"
2lanes.configure()
3
4-- this lane eats items in the linda one by one
5local eater = function( l, loop)
6 local val, key = l:receive( "go")
7 for i = 1, loop do
8 local val, key = l:receive( "key")
9 --print( val)
10 end
11 -- print "loop is over"
12 val, key = l:receive( "done")
13 -- print( val)
14end
15
16-- this lane eats items in the linda in batches
17local batched = function( l, loop, batch)
18 local val, key = l:receive( "go")
19 for i = 1, loop/batch do
20 l:receive( l.batched, "key", batch)
21 end
22 print "loop is over"
23 val, key = l:receive( "done")
24 print( val)
25end
26
27local lane_eater_gen = lanes.gen( "*", eater)
28local lane_batched_gen = lanes.gen( "*", batched)
29
30local function ziva( preloop, loop, batch)
31 -- prefill the linda a bit to increase fifo stress
32 local top = math.max( preloop, loop)
33 local l, lane = lanes.linda()
34 local t1 = os.time()
35 for i = 1, preloop do
36 l:send( "key", i)
37 end
38 print( l:count( "key"))
39 if batch then
40 if l.batched then
41 lane = lane_batched_gen( l, top, batch)
42 else
43 print "no batch support in this version of Lanes"
44 lane = lane_eater_gen( l, top)
45 end
46 else
47 lane = lane_eater_gen( l, top)
48 end
49 -- tell the lanes they can start eating data
50 l:send("go", "go")
51 -- send the remainder of the elements while they are consumed
52 if loop > preloop then
53 for i = preloop + 1, loop do
54 l:send( "key", i)
55 end
56 end
57 l:send( "done" ,"are you happy?")
58 lane:join()
59 return os.difftime(os.time(), t1)
60end
61
62local tests =
63{
64 --[[{ 2000000, 0},
65 { 3000000, 0},
66 { 4000000, 0},
67 { 5000000, 0},
68 { 6000000, 0},]]
69 --[[{ 1000000, 2000000},
70 { 2000000, 3000000},
71 { 3000000, 4000000},
72 { 4000000, 5000000},
73 { 5000000, 6000000},]]
74 --[[{ 4000000, 0},
75 { 4000000, 0, 1},
76 { 4000000, 0, 2},
77 { 4000000, 0, 3},
78 { 4000000, 0, 5},
79 { 4000000, 0, 8},
80 { 4000000, 0, 13},
81 { 4000000, 0, 21},
82 { 4000000, 0, 44},]]
83}
84for k, v in pairs( tests) do
85 local pre, loop, batch = v[1], v[2], v[3]
86 print( pre, loop, batch, "duration = " .. ziva( pre, loop, batch))
87end
88
89--[[
90 V 2.1.0:
91 ziva( 20000, 0) -> 4s ziva( 10000, 20000) -> 3s
92 ziva( 30000, 0) -> 8s ziva( 20000, 30000) -> 7s
93 ziva( 40000, 0) -> 15s ziva( 30000, 40000) -> 15s
94 ziva( 50000, 0) -> 24s ziva( 40000, 50000) -> 23s
95 ziva( 60000, 0) -> 34s ziva( 50000, 60000) -> 33s
96
97 SIMPLIFIED:
98 ziva( 20000, 0) -> 4s ziva( 10000, 20000) -> 3s
99 ziva( 30000, 0) -> 9s ziva( 20000, 30000) -> 8s
100 ziva( 40000, 0) -> 15s ziva( 30000, 40000) -> 15s
101 ziva( 50000, 0) -> 25s ziva( 40000, 50000) -> 24s
102 ziva( 60000, 0) -> 35s ziva( 50000, 60000) -> 35s
103
104 FIFO:
105 ziva( 2000000, 0) -> 9s ziva( 1000000, 2000000) -> 33s
106 ziva( 3000000, 0) -> 14s ziva( 2000000, 3000000) -> 40s
107 ziva( 4000000, 0) -> 20s ziva( 3000000, 4000000) -> 27s
108 ziva( 5000000, 0) -> 24s ziva( 4000000, 5000000) -> 42s
109 ziva( 6000000, 0) -> 29s ziva( 5000000, 6000000) -> 55s
110
111 FIFO BATCHED:
112 ziva( 4000000, 0, 1) -> 20s
113 ziva( 4000000, 0, 2) -> 11s
114 ziva( 4000000, 0, 3) -> 7s
115 ziva( 4000000, 0, 5) -> 5s
116 ziva( 4000000, 0, 8) -> 3s
117 ziva( 4000000, 0, 13) -> 3s
118 ziva( 4000000, 0, 21) -> 3s
119 ziva( 4000000, 0, 44) -> 2s
120]]
121
122local function ziva2( preloop, loop, batch)
123 local l = lanes.linda()
124 -- prefill the linda a bit to increase fifo stress
125 local top, step = math.max( preloop, loop), (l.batched and batch) and batch or 1
126 local batch_send, batch_read
127 if l.batched and batch then
128 local batch_values = {}
129 for i = 1, batch do
130 table.insert( batch_values, i)
131 end
132 -- create a function that can send several values in one shot
133 batch_send = function()
134 l:send( "key", unpack( batch_values))
135 end
136 batch_read = function()
137 l:receive( l.batched, "key", batch)
138 end
139 else -- not batched
140 batch_send = function()
141 l:send( "key", top)
142 end
143 batch_read = function()
144 l:receive( "key")
145 end
146 end
147 local t1 = os.time()
148 -- first, prime the linda with some data
149 for i = 1, preloop, step do
150 batch_send()
151 end
152 -- loop that alternatively sends and reads data off the linda
153 if loop > preloop then
154 for i = preloop + 1, loop, step do
155 batch_send()
156 batch_read()
157 end
158 end
159 -- here, we have preloop elements still waiting inside the linda
160 for i = 1, preloop, step do
161 batch_read()
162 end
163 return os.difftime(os.time(), t1)
164end
165
166local tests2 =
167{
168 --[[{ 2000000, 0},
169 { 3000000, 0},
170 { 4000000, 0},
171 { 5000000, 0},
172 { 6000000, 0},
173 { 1000000, 2000000},
174 { 2000000, 3000000},
175 { 3000000, 4000000},
176 { 4000000, 5000000},
177 { 5000000, 6000000},]]
178 { 4000000, 0},
179 { 4000000, 0, 1},
180 { 4000000, 0, 2},
181 { 4000000, 0, 3},
182 { 4000000, 0, 5},
183 { 4000000, 0, 8},
184 { 4000000, 0, 13},
185 { 4000000, 0, 21},
186 { 4000000, 0, 44},
187}
188for k, v in pairs( tests2) do
189 local pre, loop, batch = v[1], v[2], v[3]
190 print( pre, loop, batch, "duration = " .. ziva2( pre, loop, batch))
191end
192
193--[[
194 V 2.1.0:
195 ziva( 20000, 0) -> 3s ziva( 10000, 20000) -> 3s
196 ziva( 30000, 0) -> 8s ziva( 20000, 30000) -> 7s
197 ziva( 40000, 0) -> 15s ziva( 30000, 40000) -> 14s
198 ziva( 50000, 0) -> 24s ziva( 40000, 50000) -> 22s
199 ziva( 60000, 0) -> 34s ziva( 50000, 60000) -> 33s
200
201 SIMPLIFIED:
202 ziva( 20000, 0) -> 4s ziva( 10000, 20000) -> 3s
203 ziva( 30000, 0) -> 8s ziva( 20000, 30000) -> 7s
204 ziva( 40000, 0) -> 14s ziva( 30000, 40000) -> 14s
205 ziva( 50000, 0) -> 23s ziva( 40000, 50000) -> 22s
206 ziva( 60000, 0) -> 33s ziva( 50000, 60000) -> 32s
207
208 FIFO:
209 ziva( 2000000, 0) -> 9s ziva( 1000000, 2000000) -> 14s
210 ziva( 3000000, 0) -> 14s ziva( 2000000, 3000000) -> 23s
211 ziva( 4000000, 0) -> 19s ziva( 3000000, 4000000) -> 23s
212 ziva( 5000000, 0) -> 24s ziva( 4000000, 5000000) -> 32s
213 ziva( 6000000, 0) -> 29s ziva( 5000000, 6000000) -> 33s
214
215 FIFO BATCHED:
216 ziva( 4000000, 0, 1) -> 19s
217 ziva( 4000000, 0, 2) -> 11s
218 ziva( 4000000, 0, 3) -> s
219 ziva( 4000000, 0, 5) -> s
220 ziva( 4000000, 0, 8) -> s
221 ziva( 4000000, 0, 13) -> s
222 ziva( 4000000, 0, 21) -> s
223 ziva( 4000000, 0, 44) -> s
224]]
diff --git a/tests/objects.lua b/tests/objects.lua
index 8f56a5f..14cf9ba 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()
8 9
9local linda= lanes.linda() 10local linda= lanes.linda()
10 11
diff --git a/tests/perftest.lua b/tests/perftest.lua
index 8ce1b3c..4df2ad8 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()
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..571fc1e 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()
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..f538dc6 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()
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..c6f510b 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()
12 13
13local linda= lanes.linda() 14local linda= lanes.linda()
14 15