diff options
43 files changed, 2268 insertions, 599 deletions
@@ -14,4 +14,6 @@ in the manual). | |||
14 | 14 | ||
15 | Lua Lanes has been optimized for performance, and provides around 50-60% | 15 | Lua Lanes has been optimized for performance, and provides around 50-60% |
16 | speed increase when running heavily threaded applications on dual core | 16 | speed increase when running heavily threaded applications on dual core |
17 | processors (compared to running a non-threaded plain Lua implementation). | 17 | processors (compared to running a non-threaded plain Lua implementation). |
18 | |||
19 | Starting with version 3.0, Lanes is compatible with LuaJIT 2. \ No newline at end of file | ||
@@ -3,6 +3,49 @@ CHANGES: | |||
3 | 3 | ||
4 | CHANGE X: | 4 | CHANGE X: |
5 | 5 | ||
6 | CHANGE 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 | |||
10 | CHANGE 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 | |||
15 | CHANGE 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 | |||
33 | CHANGE 32 BGe 14-May-2011 | ||
34 | * raise an error when linda:send() has nothing to send | ||
35 | |||
36 | CHANGE 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 | |||
44 | CHANGE 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 | |||
6 | CHANGE 29 BGe 1-Mar-2011 | 49 | CHANGE 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. |
@@ -7,6 +7,7 @@ For details and rationale, see http://www.lua.org/license.html | |||
7 | =============================================================================== | 7 | =============================================================================== |
8 | 8 | ||
9 | Copyright (C) 2007-11 Asko Kauppi, <akauppi@gmail.com> | 9 | Copyright (C) 2007-11 Asko Kauppi, <akauppi@gmail.com> |
10 | Copyright (C) 2010-11 Benoit Germain, <bnt.germain@gmail.com> | ||
10 | 11 | ||
11 | Permission is hereby granted, free of charge, to any person obtaining a copy | 12 | Permission is hereby granted, free of charge, to any person obtaining a copy |
12 | of this software and associated documentation files (the "Software"), to deal | 13 | of this software and associated documentation files (the "Software"), to deal |
@@ -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) |
37 | endif | 37 | endif |
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 | #--- |
42 | all: $(_TARGET_SO) | 42 | all: $(_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 | ||
89 | basic: tests/basic.lua $(_TARGET_SO) | 90 | basic: 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 | # |
95 | REP_ARGS=-llanes -e "print'say aaa'; for i=1,10 do print(i) end" | 96 | REP_ARGS=-llanes -e "print'say aaa'; for i=1,10 do print(i) end" |
@@ -155,6 +156,9 @@ appendud: tests/appendud.lua $(_TARGET_SO) | |||
155 | func_is_string: tests/func_is_string.lua $(_TARGET_SO) | 156 | func_is_string: tests/func_is_string.lua $(_TARGET_SO) |
156 | $(_PREFIX) $(LUA) $< | 157 | $(_PREFIX) $(LUA) $< |
157 | 158 | ||
159 | linda_perf: tests/linda_perf.lua $(_TARGET_SO) | ||
160 | $(_PREFIX) $(LUA) $< | ||
161 | |||
158 | atexit: tests/atexit.lua $(_TARGET_SO) | 162 | atexit: tests/atexit.lua $(_TARGET_SO) |
159 | $(_PREFIX) $(LUA) $< | 163 | $(_PREFIX) $(LUA) $< |
160 | 164 | ||
@@ -190,7 +194,7 @@ LUA_LIBDIR=$(DESTDIR)/lib/lua/5.1 | |||
190 | LUA_SHAREDIR=$(DESTDIR)/share/lua/5.1 | 194 | LUA_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 | # |
195 | install: $(_TARGET_SO) src/lanes.lua | 199 | install: $(_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: | |||
211 | ifeq "$(VERSION)" "" | 215 | ifeq "$(VERSION)" "" |
212 | echo "Usage: make tar VERSION=x.x"; false | 216 | echo "Usage: make tar VERSION=x.x"; false |
213 | else | 217 | else |
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 |
225 | endif | 229 | endif |
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 © 2007-11 Asko Kauppi. All rights reserved.</i> | 57 | <p><br/><font size="-1"><i>Copyright © 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. |
268 | Supported debuggers are Microsoft Visual Studio (for the C side) and Decoda (for the Lua side). | 361 | Supported 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 | |||
394 | generally be the last resort. | 487 | generally 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 |
397 | and after executing <tt>cancelstep</tt> Lua statements. A currently pending <tt>receive()</tt> | 490 | and after executing <tt>cancelstep</tt> Lua statements. Starting with version 3.0-beta, a pending <tt>receive()</tt> |
398 | or <tt>send()</tt> call is currently not awakened, and may be a reason for a non-detected cancel. | 491 | or <tt>send()</tt> call is awakened. This means the execution of the lane will resume although the operation has |
492 | not completed, to give the lane a chance to detect cancellation. The code should be able to handle this situation appropriately if required. | ||
399 | It is also possible to manually test for cancel requests with <tt>cancel_test()</tt>. | 493 | It 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 | |||
548 | entries. Table access and <tt>send</tt>/<tt>receive</tt> can be used together; | 642 | entries. Table access and <tt>send</tt>/<tt>receive</tt> can be used together; |
549 | reading a slot essentially peeks the next outcoming value of a queue. | 643 | reading 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> | ||
613 | Timers can be run once, or in a reoccurring fashion (<tt>period_secs > 0</tt>). | 730 | Timers can be run once, or in a reoccurring fashion (<tt>period_secs > 0</tt>). |
614 | The first occurrence can be given either as a date or as a relative delay in seconds. | 731 | The first occurrence can be given either as a date or as a relative delay in seconds. |
615 | The <tt>date</tt> table is like what <tt>os.date("*t")</tt> returns, in the | 732 | The <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 | ||
964 | Mar-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 | |||
14 | package = "Lanes" | ||
15 | |||
16 | version = "3.0-beta" | ||
17 | |||
18 | source= { | ||
19 | url= "git://github.com/LuaLanes/lanes.git", | ||
20 | branch= "v3.0-beta" | ||
21 | } | ||
22 | |||
23 | description = { | ||
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 | -- | ||
37 | supported_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 | |||
44 | dependencies= { | ||
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 | -- | ||
61 | build = { | ||
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 | |||
14 | package = "Lanes" | ||
15 | |||
16 | version = "3.0.0-1" | ||
17 | |||
18 | source= { | ||
19 | url= "git://github.com/LuaLanes/lanes.git", | ||
20 | branch= "v3.0.0" | ||
21 | } | ||
22 | |||
23 | description = { | ||
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 | -- | ||
37 | supported_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 | |||
44 | dependencies= { | ||
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 | -- | ||
61 | build = { | ||
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 | |||
8 | package = "Lanes" | ||
9 | |||
10 | version = "3.1.0-1" | ||
11 | |||
12 | source= { | ||
13 | url= "git://github.com/LuaLanes/lanes.git", | ||
14 | branch= "v3.1.0" | ||
15 | } | ||
16 | |||
17 | description = { | ||
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 | -- | ||
31 | supported_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 | |||
38 | dependencies= { | ||
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 | -- | ||
55 | build = { | ||
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 |
32 | set VSINSTALLDIR=%ProgramFiles%\Microsoft Visual Studio 9.0 | 32 | set VSINSTALLDIR=%ProgramFiles%\Microsoft Visual Studio 9.0 |
33 | if not exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto ERR_NOVC | 33 | if exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto WARN_VC |
34 | set VSINSTALLDIR=%ProgramFiles(x86)%\Microsoft Visual Studio 9.0 | ||
35 | if exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto WARN_VC | ||
34 | 36 | ||
37 | :TRY_VC10 | ||
38 | set VSINSTALLDIR=%ProgramFiles%\Microsoft Visual Studio 10.0 | ||
39 | if exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto WARN_VC | ||
40 | set VSINSTALLDIR=%ProgramFiles(x86)%\Microsoft Visual Studio 10.0 | ||
41 | if exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto WARN_VC | ||
42 | |||
43 | :WARN_VC | ||
35 | echo. | 44 | echo. |
36 | echo *** Warning: Visual C++ 2008 in use *** | 45 | echo *** Warning: Visual C++ 2008/2010 in use *** |
37 | echo. | 46 | echo. |
38 | echo Using VC++2005 is recommended for runtime compatibility issues | 47 | echo Using VC++2005 is recommended for runtime compatibility issues |
39 | echo (LuaBinaries and LfW use it; if you compile everything from | 48 | echo (LuaBinaries and LfW use it; if you compile everything from |
@@ -70,7 +79,7 @@ goto EXIT | |||
70 | REM --- | 79 | REM --- |
71 | :ERR_NOVC | 80 | :ERR_NOVC |
72 | echo. | 81 | echo. |
73 | echo ** ERROR: Visual C++ 2005/08 Express - not detected | 82 | echo ** ERROR: Visual C++ 2005/08/10 Express - not detected |
74 | echo You can set the environment variables separately, and run 'make-vc.cmd' | 83 | echo You can set the environment variables separately, and run 'make-vc.cmd' |
75 | echo or download the compiler from: | 84 | echo or download the compiler from: |
76 | echo http://msdn.microsoft.com/vstudio/express/downloads/ | 85 | echo 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 | # |
126 | lua51-$(MODULE)$(_SO): $(OBJ) | 126 | lua51-$(MODULE)$(_SO): $(OBJ) |
127 | $(CC) $(LIBFLAG) $(LIBS) $^ $(LUA_LIBS) -o $@ | 127 | $(CC) $(LIBFLAG) $^ $(LIBS) $(LUA_LIBS) -o $@ |
128 | 128 | ||
129 | clean: | 129 | clean: |
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 | */ |
81 | char const *init_keepers( int const _nbKeepers) | 81 | char 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 | ||
126 | void 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 | |||
133 | struct s_Keeper *keeper_acquire( const void *ptr) | 174 | struct 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 | ||
149 | void keeper_release( struct s_Keeper *K) | 198 | void 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 | |||
204 | void 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 | ||
11 | const char *init_keepers( int const _nbKeepers); | 11 | char const* init_keepers( int const _nbKeepers, lua_CFunction _on_state_create); |
12 | void populate_keepers( lua_State *L); | ||
12 | struct s_Keeper *keeper_acquire( const void *ptr); | 13 | struct s_Keeper *keeper_acquire( const void *ptr); |
13 | void keeper_release( struct s_Keeper *K); | 14 | void keeper_release( struct s_Keeper *K); |
15 | void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, int _nil_to_sentinel); | ||
14 | int keeper_call( lua_State *K, char const *func_name, lua_State *L, void *linda, uint_t starting_index); | 16 | int keeper_call( lua_State *K, char const *func_name, lua_State *L, void *linda, uint_t starting_index); |
15 | void close_keepers(void); | 17 | void 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 | -- | ||
39 | assert( 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 | -- |
43 | local table_remove= assert( table.remove ) | 39 | local table_concat = assert( table.concat) |
44 | local table_concat= assert( table.concat ) | 40 | local table_insert = assert( table.insert) |
41 | local table_remove = assert( table.remove) | ||
42 | local select, unpack = assert( select), assert( unpack) | ||
45 | 43 | ||
46 | --[[ | 44 | --[[ |
47 | local function WR(...) | 45 | local 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 |
51 | end | 49 | end |
52 | 50 | ||
53 | local function DEBUG(title,ud,key) | 51 | local 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 | -- |
73 | local _data= {} | 67 | |
68 | local fifo_new = function() | ||
69 | return { first = 1, count = 0} | ||
70 | end | ||
71 | |||
72 | local 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 | ||
79 | end | ||
80 | |||
81 | local 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 | ||
87 | end | ||
88 | |||
89 | local 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) | ||
100 | end | ||
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 | -- |
82 | local _incoming= {} | 110 | local _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 | -- |
91 | local _limits= {} | 119 | local _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] |
107 | end | 134 | end |
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 | -- |
121 | function send( ud, key, ... ) | 148 | function 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 |
158 | end | 170 | end |
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 | -- |
167 | function receive( ud, ... ) | 179 | function 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 | 193 | end |
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 | -- | ||
202 | receive_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 | ||
189 | end | 210 | end |
190 | 211 | ||
191 | 212 | ||
192 | ----- | 213 | ----- |
193 | -- = limit( linda_deep_ud, key, uint ) | 214 | -- = limit( linda_deep_ud, key, uint ) |
194 | -- | 215 | -- |
195 | function limit( ud, key, n ) | 216 | function 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 |
200 | end | 221 | end |
201 | 222 | ||
202 | 223 | ||
203 | ----- | 224 | ----- |
204 | -- void= set( linda_deep_ud, key, [val] ) | 225 | -- void= set( linda_deep_ud, key, [val] ) |
205 | -- | 226 | -- |
206 | function set( ud, key, val ) | 227 | function 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 | ||
214 | end | 240 | end |
215 | 241 | ||
216 | 242 | ||
217 | ----- | 243 | ----- |
218 | -- [val]= get( linda_deep_ud, key ) | 244 | -- [val]= get( linda_deep_ud, key ) |
219 | -- | 245 | -- |
220 | function get( ud, key ) | 246 | function get( ud, key) |
247 | local data, _ = tables( ud) | ||
248 | local fifo = data[key] | ||
249 | return fifo and fifo_peek( fifo, 1) | ||
250 | end | ||
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 | ||
260 | function 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 | ||
229 | end | 288 | end |
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 | -- |
237 | function clear( ud ) | 296 | function clear( ud) |
238 | 297 | ||
239 | _data[ud]= nil | 298 | _data[ud]= nil |
240 | _incoming[ud]= nil | ||
241 | _limits[ud]= nil | 299 | _limits[ud]= nil |
242 | end | 300 | end |
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 | ||
54 | const char *VERSION= "2.1.0"; | 54 | char 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 | ||
252 | static 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" | ||
389 | LUAG_FUNC( linda_receive) | 411 | LUAG_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 | */ | ||
584 | LUAG_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 | */ |
544 | LUAG_FUNC( linda_get) | 612 | LUAG_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) | |||
571 | LUAG_FUNC( linda_limit) | 646 | LUAG_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 | */ | ||
695 | LUAG_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 | */ | ||
713 | LUAG_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 | */ | ||
717 | LUAG_FUNC( linda) | 866 | LUAG_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 | */ |
848 | static void selfdestruct_remove( struct s_lane *s ) { | 997 | static 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 | */ |
883 | static void selfdestruct_atexit( void ) | 1033 | static 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 | ||
1206 | LUAG_FUNC( set_debug_threadname) | 1369 | LUAG_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) | 1387 | static 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 | ||
1532 | static 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 | |||
1366 | LUAG_FUNC( thread_new ) | 1554 | LUAG_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 | ||
1628 | LUAG_FUNC( thread_cancel) | 1886 | LUAG_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 ) | |||
1915 | LUAG_FUNC( wakeup_conv ) | 2177 | LUAG_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 | */ |
1971 | static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ref, int const nbKeepers) | 2234 | static 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 | ||
2055 | int | 2328 | static volatile long s_initCount = 0; |
2056 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | 2329 | |
2057 | __declspec(dllexport) | 2330 | LUAG_FUNC( configure ) |
2058 | #endif | ||
2059 | luaopen_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 | |||
2429 | int | ||
2430 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | ||
2431 | __declspec(dllexport) | ||
2432 | #endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | ||
2433 | luaopen_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 | ||
42 | module( "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 | ||
46 | local lanes = {} | ||
47 | |||
48 | lanes.configure = function( _params) | ||
49 | _params = _params or { nb_keepers = 1, with_timers = true, on_state_create = nil} | ||
50 | if type( _params) ~= "table" then | ||
51 | error( "Bad parameter #1 to lanes.configure(), should be a table") | ||
52 | end | ||
53 | -- on_state_create may be nil or a function | ||
54 | if _params.on_state_create and (type( _params.on_state_create) ~= "function") then | ||
55 | error( "Bad on_state_create: " .. tostring( _params.on_state_create), 2) | ||
56 | end | ||
43 | 57 | ||
44 | local mm = require "lua51-lanes" | 58 | local mm = require "lua51-lanes" |
45 | assert( type(mm)=="table" ) | 59 | assert( 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 | ||
62 | if mm.configure then mm.configure( _params.nb_keepers, _params.on_state_create) end | ||
47 | 63 | ||
48 | local thread_new = assert(mm.thread_new) | 64 | local thread_new = assert(mm.thread_new) |
49 | 65 | ||
@@ -74,7 +90,7 @@ local pairs= assert( pairs ) | |||
74 | local tostring= assert( tostring ) | 90 | local tostring= assert( tostring ) |
75 | local error= assert( error ) | 91 | local error= assert( error ) |
76 | 92 | ||
77 | ABOUT= | 93 | lanes.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 | ||
164 | function gen( ... ) | 181 | -- PUBLIC LANES API |
182 | local 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 |
227 | end | 251 | end |
228 | 252 | ||
@@ -233,11 +257,17 @@ end | |||
233 | ----- | 257 | ----- |
234 | -- lanes.linda() -> linda_ud | 258 | -- lanes.linda() -> linda_ud |
235 | -- | 259 | -- |
236 | linda = mm.linda | 260 | -- PUBLIC LANES API |
261 | local linda = mm.linda | ||
237 | 262 | ||
238 | 263 | ||
239 | ---=== Timers ===--- | 264 | ---=== Timers ===--- |
240 | 265 | ||
266 | -- PUBLIC LANES API | ||
267 | local timer = function() error "timers are not active" end | ||
268 | |||
269 | if _params.with_timers ~= false then | ||
270 | |||
241 | local timer_gateway= assert( mm.timer_gateway ) | 271 | local 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 | -- |
431 | function timer( linda, key, a, period ) | 463 | -- PUBLIC LANES API |
464 | timer = 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 ) |
453 | end | 486 | end |
454 | 487 | ||
488 | end -- _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 | -- |
471 | function genlock( linda, key, N ) | 505 | -- PUBLIC LANES API |
506 | local 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 | -- |
504 | function genatomic( linda, key, initial_val ) | 539 | -- PUBLIC LANES API |
540 | local 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 | ||
577 | end -- lanes.configure | ||
578 | |||
520 | --the end | 579 | --the end |
580 | return 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 |
75 | static void FAIL( const char *funcname, int rc ) { | 75 | static 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 | */ |
88 | time_d now_secs(void) { | 91 | time_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 ) { | 285 | bool_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 | 664 | bool_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 | ||
726 | static const lua_Alloc alloc_f= 0; | 728 | static 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 | */ |
39 | enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED }; | 39 | enum 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 | ||
112 | void SIGNAL_INIT( SIGNAL_T *ref ); | 122 | void SIGNAL_INIT( SIGNAL_T *ref ); |
113 | void SIGNAL_FREE( SIGNAL_T *ref ); | 123 | void 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 | ||
203 | bool_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 | ||
206 | bool_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 | ||
186 | void THREAD_KILL( THREAD_T *ref ); | 210 | void 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 | ||
95 | static const luaL_Reg libs[] = { | 96 | static 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 | ||
109 | static bool_t openlib( lua_State *L, const char *name, size_t len ) { | 110 | static 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 | |||
137 | static 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 | |||
159 | typedef enum | ||
160 | { | ||
161 | FST_Bytecode, | ||
162 | FST_Native, | ||
163 | FST_FastJIT | ||
164 | } FuncSubType; | ||
165 | |||
166 | FuncSubType 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 | |||
192 | static 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 | ||
205 | static 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 | |||
228 | static 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 | */ | ||
376 | void 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 | ||
142 | const char *luaG_openlibs( lua_State *L, const char *libs ) { | 425 | lua_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 | ||
820 | static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) | 1122 | static 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 | */ | ||
1197 | static 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 | }; |
899 | static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i, enum e_vt value_type ); | 1240 | static 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 | ||
901 | static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) { | 1242 | static 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 | // |
1294 | static int new_require( lua_State *L ) | 1659 | static 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 | |||
51 | typedef void (*luaG_IdFunction)( lua_State *L, char const * const which); | 49 | typedef void (*luaG_IdFunction)( lua_State *L, char const * const which); |
52 | 50 | ||
53 | void luaG_dump( lua_State* L ); | 51 | void luaG_dump( lua_State* L ); |
54 | 52 | ||
55 | const char *luaG_openlibs( lua_State *L, const char *libs ); | 53 | lua_State* luaG_newstate( char const* libs, lua_CFunction _on_state_create); |
56 | 54 | ||
57 | int luaG_deep_userdata( lua_State *L, luaG_IdFunction idfunc); | 55 | int luaG_deep_userdata( lua_State *L, luaG_IdFunction idfunc); |
58 | void *luaG_todeep( lua_State *L, luaG_IdFunction idfunc, int index ); | 56 | void *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); | |||
72 | extern MUTEX_T deep_lock; | 70 | extern MUTEX_T deep_lock; |
73 | extern MUTEX_T mtid_lock; | 71 | extern MUTEX_T mtid_lock; |
74 | 72 | ||
73 | void populate_func_lookup_table( lua_State *L, int _i, char const *_name); | ||
75 | void serialize_require( lua_State *L); | 74 | void serialize_require( lua_State *L); |
76 | extern MUTEX_T require_cs; | 75 | extern 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 | -- |
9 | require "lanes" | 9 | local lanes = require "lanes" |
10 | lanes.configure() | ||
10 | 11 | ||
11 | local _tab = { | 12 | local _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 @@ | |||
1 | require "lanes" | 1 | local lanes = require "lanes" |
2 | lanes.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 | ||
7 | require "lanes" | 7 | local lanes = require "lanes" |
8 | lanes.configure() | ||
8 | 9 | ||
9 | local linda= lanes.linda() | 10 | local linda= lanes.linda() |
10 | local key= "$" | 11 | local 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 | ||
10 | require "lanes" | 10 | local lanes = require "lanes" |
11 | lanes.configure() | ||
11 | require "assert" -- assert.fails() | 12 | require "assert" -- assert.fails() |
12 | 13 | ||
13 | local lanes_gen= assert( lanes.gen ) | 14 | local 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 | ||
7 | require "lanes" | 7 | local lanes = require "lanes" |
8 | lanes.configure() | ||
8 | 9 | ||
9 | local table_concat= assert(table.concat) | 10 | local 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 | -- |
4 | require 'lanes' | 4 | local lanes = require "lanes" |
5 | lanes.configure() | ||
5 | 6 | ||
6 | local function PRINT_FMT( fmt, ... ) | 7 | local 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 @@ | |||
1 | lanes = require('lanes') | 1 | local lanes = require "lanes" |
2 | lanes.configure() | ||
2 | 3 | ||
3 | local linda = lanes.linda() | 4 | local 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 | ||
7 | require "lanes" | 7 | local lanes = require "lanes" |
8 | lanes.configure() | ||
8 | 9 | ||
9 | local function lane() | 10 | local 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 | -- |
15 | local lanes= require "lanes" | 15 | local lanes = require "lanes" |
16 | lanes.configure{ nb_keepers =1, with_timers = false} | ||
16 | 17 | ||
17 | local function WR(str) | 18 | local 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 | -- |
67 | local 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 | 70 | local 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 | } |
69 | assert( #right==99 ) | 87 | assert( #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 | ||
7 | require "lanes" | 7 | local lanes = require "lanes" |
8 | lanes.configure() | ||
8 | 9 | ||
9 | local linda= lanes.linda() | 10 | local linda= lanes.linda() |
10 | local atomic_inc= lanes.genatomic( linda, "FIFO_n" ) | 11 | local 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 | } |
24 | end | 29 | end |
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 | ||
11 | require "lanes" | 11 | local lanes = require "lanes" |
12 | lanes.configure() | ||
12 | 13 | ||
13 | local FN= "finalizer-test.tmp" | 14 | local 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 @@ | |||
1 | require "lanes" | 1 | local lanes = require "lanes" |
2 | lanes.configure() | ||
2 | 3 | ||
3 | local options = {globals = { b = 666 }} | 4 | local 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 | ||
5 | require "lanes" | 5 | local lanes = require "lanes" |
6 | lanes.configure() | ||
6 | 7 | ||
7 | local function ret(b) | 8 | local 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. | |||
10 | e.g. { globals = { data = 1, func = function() useclosurehere() end } }" | 10 | e.g. { globals = { data = 1, func = function() useclosurehere() end } }" |
11 | ]] | 11 | ]] |
12 | 12 | ||
13 | require "lanes" | 13 | local lanes = require "lanes" |
14 | lanes.configure() | ||
14 | 15 | ||
15 | local function testrun() | 16 | local function testrun() |
16 | assert( print ) | 17 | assert( print ) |
@@ -20,7 +21,7 @@ local function testrun() | |||
20 | return true | 21 | return true |
21 | end | 22 | end |
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 | ||
7 | require "lanes" | 7 | local lanes = require "lanes" |
8 | lanes.configure() | ||
8 | 9 | ||
9 | local function keeper(linda) | 10 | local 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 |
46 | end | 46 | end |
47 | 47 | ||
48 | require "lanes" | 48 | local lanes = require "lanes" |
49 | lanes.configure() | ||
49 | 50 | ||
50 | local g= lanes.gen( LIBS, function(i) | 51 | local 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 @@ | |||
1 | local lanes = require "lanes" | ||
2 | lanes.configure() | ||
3 | |||
4 | -- this lane eats items in the linda one by one | ||
5 | local 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) | ||
14 | end | ||
15 | |||
16 | -- this lane eats items in the linda in batches | ||
17 | local 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) | ||
25 | end | ||
26 | |||
27 | local lane_eater_gen = lanes.gen( "*", eater) | ||
28 | local lane_batched_gen = lanes.gen( "*", batched) | ||
29 | |||
30 | local 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) | ||
60 | end | ||
61 | |||
62 | local 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 | } | ||
84 | for 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)) | ||
87 | end | ||
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 | |||
122 | local 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) | ||
164 | end | ||
165 | |||
166 | local 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 | } | ||
188 | for 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)) | ||
191 | end | ||
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 | ||
7 | require "lanes" | 7 | local lanes = require "lanes" |
8 | lanes.configure() | ||
8 | 9 | ||
9 | local linda= lanes.linda() | 10 | local 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 @@ | |||
27 | local MSYS= os.getenv("OSTYPE")=="msys" | 27 | local MSYS= os.getenv("OSTYPE")=="msys" |
28 | 28 | ||
29 | 29 | ||
30 | require "lanes" | 30 | local lanes = require "lanes" |
31 | lanes.configure() | ||
31 | 32 | ||
32 | local m= require "argtable" | 33 | local m= require "argtable" |
33 | local argtable= assert( m.argtable ) | 34 | local 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 @@ | |||
1 | require "lanes" | 1 | local lanes = require "lanes" |
2 | lanes.configure( 1) | ||
2 | 3 | ||
3 | local body = function( param) | 4 | local 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] |
17 | end | 19 | end |
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 | -- |
6 | require 'lanes' | 6 | local lanes = require "lanes" |
7 | lanes.configure() | ||
7 | 8 | ||
8 | local function a_lane() | 9 | local 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 @@ | |||
8 | io.stderr:setvbuf "no" | 8 | io.stderr:setvbuf "no" |
9 | 9 | ||
10 | 10 | ||
11 | require "lanes" | 11 | local lanes = require "lanes" |
12 | lanes.configure() | ||
12 | 13 | ||
13 | local linda= lanes.linda() | 14 | local linda= lanes.linda() |
14 | 15 | ||