diff options
-rw-r--r-- | CHANGES | 16 | ||||
-rw-r--r-- | Makefile | 8 | ||||
-rw-r--r-- | docs/index.html | 39 | ||||
-rw-r--r-- | src/keeper.c | 15 | ||||
-rw-r--r-- | src/keeper.h | 2 | ||||
-rw-r--r-- | src/lanes.c | 134 | ||||
-rw-r--r-- | src/lanes.lua | 323 | ||||
-rw-r--r-- | src/tools.c | 92 | ||||
-rw-r--r-- | src/tools.h | 5 | ||||
-rw-r--r-- | tests/fibonacci.lua | 9 | ||||
-rw-r--r-- | tests/package.lua | 20 | ||||
-rw-r--r-- | tests/pingpong.lua | 31 | ||||
-rw-r--r-- | tests/timer.lua | 5 |
13 files changed, 421 insertions, 278 deletions
@@ -1,5 +1,19 @@ | |||
1 | CHANGES: | 1 | CHANGES: |
2 | 2 | ||
3 | CHANGE 58: BGe 30-Jan-13 | ||
4 | * version 3.5.0 | ||
5 | * new: API lanes.require(), use it instead of regular require() for modules that export C functions you need to send over. | ||
6 | * new: lanes no longer require 'lanes.core' by default in every created state. Use {required={"lanes.core"}} if you need to transfer lanes functions. | ||
7 | * internal: because of the above, reworked the timer implementation to remove upvalue-dependency on lanes.core | ||
8 | * new: API lanes.timer_lane, to be able to operate on timer lane if need be | ||
9 | * improved: if a module is a full userdata, scan its metatable for function database population | ||
10 | * improved: on_state_create can be a Lua function | ||
11 | * changed: on_state_create is called after the base libraries are loaded | ||
12 | * package[loaders|searchers] is no longer transfered as function naming depends on slot order | ||
13 | * internal: changed separator from '.' to '/' in lookup databases to be able to distinguish search levels and dot coming from module names | ||
14 | * added some mode debug spew | ||
15 | * updated tests to reflect the above changes | ||
16 | |||
3 | CHANGE 57: BGe 28-Jan-13 | 17 | CHANGE 57: BGe 28-Jan-13 |
4 | * More detailed DEBUG_SPEW logs | 18 | * More detailed DEBUG_SPEW logs |
5 | * A bit of code cosmetics | 19 | * A bit of code cosmetics |
@@ -210,7 +224,7 @@ CHANGE 19 BGe 2-Dec-2010: | |||
210 | 224 | ||
211 | CHANGE 18 BGe 6-Oct-2010: | 225 | CHANGE 18 BGe 6-Oct-2010: |
212 | Fixed 'memory leak' in some situations where a free running lane is collected before application shutdown | 226 | Fixed 'memory leak' in some situations where a free running lane is collected before application shutdown |
213 | A bit of code cleanup | 227 | A bit of code cleanup |
214 | 228 | ||
215 | CHANGE 17 BGe 21-Sept-2010: | 229 | CHANGE 17 BGe 21-Sept-2010: |
216 | Fixed stupid compilation errors. | 230 | Fixed stupid compilation errors. |
@@ -88,6 +88,8 @@ test: | |||
88 | $(MAKE) atexit | 88 | $(MAKE) atexit |
89 | $(MAKE) linda_perf | 89 | $(MAKE) linda_perf |
90 | $(MAKE) rupval | 90 | $(MAKE) rupval |
91 | $(MAKE) package | ||
92 | $(MAKE) pingpong | ||
91 | 93 | ||
92 | basic: tests/basic.lua $(_TARGET_SO) | 94 | basic: tests/basic.lua $(_TARGET_SO) |
93 | $(_PREFIX) $(LUA) $< | 95 | $(_PREFIX) $(LUA) $< |
@@ -167,6 +169,12 @@ atexit: tests/atexit.lua $(_TARGET_SO) | |||
167 | rupval: tests/rupval.lua $(_TARGET_SO) | 169 | rupval: tests/rupval.lua $(_TARGET_SO) |
168 | $(_PREFIX) $(LUA) $< | 170 | $(_PREFIX) $(LUA) $< |
169 | 171 | ||
172 | package: tests/package.lua $(_TARGET_SO) | ||
173 | $(_PREFIX) $(LUA) $< | ||
174 | |||
175 | pingpong: tests/pingpong.lua $(_TARGET_SO) | ||
176 | $(_PREFIX) $(LUA) $< | ||
177 | |||
170 | #--- | 178 | #--- |
171 | perftest-plain: tests/perftest.lua $(_TARGET_SO) | 179 | perftest-plain: tests/perftest.lua $(_TARGET_SO) |
172 | $(MAKE) _perftest ARGS="$< $(N) -plain" | 180 | $(MAKE) _perftest ARGS="$< $(N) -plain" |
diff --git a/docs/index.html b/docs/index.html index 4f31923..8b21a97 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -69,7 +69,7 @@ | |||
69 | </p> | 69 | </p> |
70 | 70 | ||
71 | <p> | 71 | <p> |
72 | This document was revised on 26-Jan-13, and applies to version <tt>3.4.4</tt>. | 72 | This document was revised on 30-Jan-13, and applies to version <tt>3.5.0</tt>. |
73 | </p> | 73 | </p> |
74 | </font> | 74 | </font> |
75 | </center> | 75 | </center> |
@@ -249,10 +249,17 @@ | |||
249 | <code>.on_state_create</code> | 249 | <code>.on_state_create</code> |
250 | </td> | 250 | </td> |
251 | <td> | 251 | <td> |
252 | C function/<tt>nil</tt> | 252 | function/<tt>nil</tt> |
253 | </td> | 253 | </td> |
254 | <td> | 254 | <td> |
255 | If provided, will be called in every created Lua state (keepers and lanes) right after it is created, and *before* any library is loaded. That way, all C functions it loads in the state can be added to the function lookup database. Default is <tt>nil</tt>. | 255 | If provided, will be called in every created Lua state (keepers and lanes) right after initializing the base libraries. |
256 | <br> | ||
257 | Typical usage is twofold: | ||
258 | <ul> | ||
259 | <li>Tweak <tt>package.loaders</tt></li> | ||
260 | <li>Load some additional C functions in the global space (of course only a C function will be able to do this).</li> | ||
261 | </ul> | ||
262 | That way, all changes in the state can be properly taken into account when building the function lookup database. Default is <tt>nil</tt>. | ||
256 | </td> | 263 | </td> |
257 | </tr> | 264 | </tr> |
258 | 265 | ||
@@ -270,6 +277,22 @@ | |||
270 | </table> | 277 | </table> |
271 | </p> | 278 | </p> |
272 | 279 | ||
280 | <p> | ||
281 | NEW (version 3.5.0) | ||
282 | <br> | ||
283 | Once Lanes is configured, one should register with Lanes the modules exporting functions that will be transferred either during lane generation or through <a href="#lindas">lindas</a>. | ||
284 | <br> | ||
285 | Use <tt>lanes.require()</tt> for this purpose. This will call the original <tt>require()</tt>, then add the result to the lookup databases. | ||
286 | </p> | ||
287 | |||
288 | <table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"> | ||
289 | <tr> | ||
290 | <td> | ||
291 | <pre> local m = lanes.require "modname"</pre> | ||
292 | </td> | ||
293 | </tr> | ||
294 | </table> | ||
295 | |||
273 | <hr/> | 296 | <hr/> |
274 | <h2 id="creation">Creation</h2> | 297 | <h2 id="creation">Creation</h2> |
275 | 298 | ||
@@ -468,6 +491,8 @@ | |||
468 | These tables are built from the modules listed here. <tt>required</tt> must be a list of strings, each one being the name of a module to be required. Each module is required with <tt>require()</tt> before the lanes function is invoked. | 491 | These tables are built from the modules listed here. <tt>required</tt> must be a list of strings, each one being the name of a module to be required. Each module is required with <tt>require()</tt> before the lanes function is invoked. |
469 | So, from the required module's point of view, requiring it manually from inside the lane body or having it required this way doesn't change anything. From the lane body's point of view, the only difference is that a module not creating a global won't be accessible. | 492 | So, from the required module's point of view, requiring it manually from inside the lane body or having it required this way doesn't change anything. From the lane body's point of view, the only difference is that a module not creating a global won't be accessible. |
470 | Therefore, a lane body will also have to require a module manually, but this won't do anything more (see Lua's <tt>require</tt> documentation). | 493 | Therefore, a lane body will also have to require a module manually, but this won't do anything more (see Lua's <tt>require</tt> documentation). |
494 | <br> | ||
495 | ATTEMPTING TO TRANSFER A FUNCTION REGISTERED BY A MODULE NOT LISTED HERE WILL RAISE AN ERROR. | ||
471 | </td> | 496 | </td> |
472 | </tr> | 497 | </tr> |
473 | <tr valign=top> | 498 | <tr valign=top> |
@@ -567,7 +592,7 @@ | |||
567 | </td> | 592 | </td> |
568 | <td/> | 593 | <td/> |
569 | <td> | 594 | <td> |
570 | running, not suspended on a Linda call. | 595 | running, not suspended on a <a href="#lindas">Linda</a> call. |
571 | </td> | 596 | </td> |
572 | </tr> | 597 | </tr> |
573 | <tr> | 598 | <tr> |
@@ -577,7 +602,7 @@ | |||
577 | </td> | 602 | </td> |
578 | <td/> | 603 | <td/> |
579 | <td> | 604 | <td> |
580 | waiting at a Linda <tt>:receive()</tt> or <tt>:send()</tt> | 605 | waiting at a <a href="#lindas">Linda</a> <tt>:receive()</tt> or <tt>:send()</tt> |
581 | </td> | 606 | </td> |
582 | </tr> | 607 | </tr> |
583 | <tr> | 608 | <tr> |
@@ -711,7 +736,7 @@ | |||
711 | </pre></td></tr></table> | 736 | </pre></td></tr></table> |
712 | 737 | ||
713 | <p> | 738 | <p> |
714 | If you want to wait for multiple lanes to finish (any of a set of lanes), use a <a href="#lindas">Linda</a> object. Give each lane a specific id, and send that id over a Linda once that thread is done (as the last thing you do). | 739 | If you want to wait for multiple lanes to finish (any of a set of lanes), use a <a href="#lindas">Linda</a> object. Give each lane a specific id, and send that id over a <a href="#lindas">Linda</a> once that thread is done (as the last thing you do). |
715 | </p> | 740 | </p> |
716 | 741 | ||
717 | <table border=1 bgcolor="#FFFFE0" cellpadding="10" style="width:50%"><tr><td><pre> | 742 | <table border=1 bgcolor="#FFFFE0" cellpadding="10" style="width:50%"><tr><td><pre> |
@@ -750,7 +775,7 @@ | |||
750 | <p> | 775 | <p> |
751 | Cancellation is tested <u>before</u> going to sleep in <tt>receive()</tt> or <tt>send()</tt> calls and after executing <tt>cancelstep</tt> Lua statements. Starting with version 3.0-beta, a pending <tt>receive()</tt>or <tt>send()</tt> call is awakened. | 776 | Cancellation is tested <u>before</u> going to sleep in <tt>receive()</tt> or <tt>send()</tt> calls and after executing <tt>cancelstep</tt> Lua statements. Starting with version 3.0-beta, a pending <tt>receive()</tt>or <tt>send()</tt> call is awakened. |
752 | <br> | 777 | <br> |
753 | This means the execution of the lane will resume although the operation has not completed, to give the lane a chance to detect cancellation (even in the case the code waits on a linda with infinite timeout). | 778 | This means the execution of the lane will resume although the operation has not completed, to give the lane a chance to detect cancellation (even in the case the code waits on a <a href="#lindas">linda</a> with infinite timeout). |
754 | <br> | 779 | <br> |
755 | The code should be able to handle this situation appropriately if required (in other words, it should gracefully handle the fact that it didn't receive the expected values). | 780 | The code should be able to handle this situation appropriately if required (in other words, it should gracefully handle the fact that it didn't receive the expected values). |
756 | <br> | 781 | <br> |
diff --git a/src/keeper.c b/src/keeper.c index 7485110..3511b56 100644 --- a/src/keeper.c +++ b/src/keeper.c | |||
@@ -557,7 +557,7 @@ void close_keepers( void) | |||
557 | * unclosed, because it does not really matter. In production code, this | 557 | * unclosed, because it does not really matter. In production code, this |
558 | * function never fails. | 558 | * function never fails. |
559 | */ | 559 | */ |
560 | char const* init_keepers( lua_State* L, int const _nbKeepers, lua_CFunction _on_state_create) | 560 | char const* init_keepers( lua_State* L, int _on_state_create, int const _nbKeepers) |
561 | { | 561 | { |
562 | int i; | 562 | int i; |
563 | assert( _nbKeepers >= 1); | 563 | assert( _nbKeepers >= 1); |
@@ -572,7 +572,7 @@ char const* init_keepers( lua_State* L, int const _nbKeepers, lua_CFunction _on_ | |||
572 | // | 572 | // |
573 | // 'io' for debugging messages, 'package' because we need to require modules exporting idfuncs | 573 | // 'io' for debugging messages, 'package' because we need to require modules exporting idfuncs |
574 | // the others because they export functions that we may store in a keeper for transfer between lanes | 574 | // the others because they export functions that we may store in a keeper for transfer between lanes |
575 | K = luaG_newstate( L, "*", _on_state_create); | 575 | K = luaG_newstate( L, _on_state_create, "*"); |
576 | 576 | ||
577 | STACK_CHECK( K); | 577 | STACK_CHECK( K); |
578 | 578 | ||
@@ -635,8 +635,8 @@ void populate_keepers( lua_State* L) | |||
635 | char const* name = luaL_checklstring( L, -1, &name_len); | 635 | char const* name = luaL_checklstring( L, -1, &name_len); |
636 | int i; | 636 | int i; |
637 | 637 | ||
638 | STACK_CHECK( L); | 638 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "populate_keepers %s BEGIN\n" INDENT_END, name)); |
639 | STACK_GROW( L, 3); | 639 | DEBUGSPEW_CODE( ++ debugspew_indent_depth); |
640 | 640 | ||
641 | for( i = 0; i < GNbKeepers; ++ i) | 641 | for( i = 0; i < GNbKeepers; ++ i) |
642 | { | 642 | { |
@@ -647,16 +647,19 @@ void populate_keepers( lua_State* L) | |||
647 | STACK_GROW( K, 2); | 647 | STACK_GROW( K, 2); |
648 | lua_getglobal( K, "require"); | 648 | lua_getglobal( K, "require"); |
649 | lua_pushlstring( K, name, name_len); | 649 | lua_pushlstring( K, name, name_len); |
650 | res = lua_pcall( K, 1, 0, 0); | 650 | res = lua_pcall( K, 1, 1, 0); |
651 | if( res != LUA_OK) | 651 | if( res != LUA_OK) |
652 | { | 652 | { |
653 | char const* err = luaL_checkstring( K, -1); | 653 | char const* err = luaL_checkstring( K, -1); |
654 | luaL_error( L, "error requiring '%s' in keeper state: %s", name, err); | 654 | luaL_error( L, "error requiring '%s' in keeper state: %s", name, err); |
655 | } | 655 | } |
656 | // after requiring the module, register the functions it exported in our name<->function database | ||
657 | populate_func_lookup_table( K, -1, name); | ||
658 | lua_pop( K, 1); | ||
656 | STACK_END( K, 0); | 659 | STACK_END( K, 0); |
657 | MUTEX_UNLOCK( &GKeepers[i].lock_); | 660 | MUTEX_UNLOCK( &GKeepers[i].lock_); |
658 | } | 661 | } |
659 | STACK_END( L, 0); | 662 | DEBUGSPEW_CODE( -- debugspew_indent_depth); |
660 | } | 663 | } |
661 | 664 | ||
662 | struct s_Keeper* keeper_acquire( void const* ptr) | 665 | struct s_Keeper* keeper_acquire( void const* ptr) |
diff --git a/src/keeper.h b/src/keeper.h index 15a5a41..29a19a9 100644 --- a/src/keeper.h +++ b/src/keeper.h | |||
@@ -13,7 +13,7 @@ struct s_Keeper | |||
13 | // problem: maybe on some platforms (linux) atexit() is called after DLL/so are unloaded... | 13 | // problem: maybe on some platforms (linux) atexit() is called after DLL/so are unloaded... |
14 | #define HAVE_KEEPER_ATEXIT_DESINIT 0 | 14 | #define HAVE_KEEPER_ATEXIT_DESINIT 0 |
15 | 15 | ||
16 | char const* init_keepers( lua_State* L, int const _nbKeepers, lua_CFunction _on_state_create); | 16 | char const* init_keepers( lua_State* L, int _on_state_create, int const _nbKeepers); |
17 | #if !HAVE_KEEPER_ATEXIT_DESINIT | 17 | #if !HAVE_KEEPER_ATEXIT_DESINIT |
18 | void close_keepers( void); | 18 | void close_keepers( void); |
19 | #endif // HAVE_KEEPER_ATEXIT_DESINIT | 19 | #endif // HAVE_KEEPER_ATEXIT_DESINIT |
diff --git a/src/lanes.c b/src/lanes.c index 80f5b15..2b3d8ac 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -52,7 +52,7 @@ | |||
52 | * ... | 52 | * ... |
53 | */ | 53 | */ |
54 | 54 | ||
55 | char const* VERSION = "3.4.4"; | 55 | char const* VERSION = "3.5.0"; |
56 | 56 | ||
57 | /* | 57 | /* |
58 | =============================================================================== | 58 | =============================================================================== |
@@ -1529,7 +1529,6 @@ LUAG_FUNC( set_singlethreaded) | |||
1529 | # define STACK_TRACE_KEY ((void*)lane_error) // used as registry key | 1529 | # define STACK_TRACE_KEY ((void*)lane_error) // used as registry key |
1530 | # define EXTENDED_STACK_TRACE_KEY ((void*)LG_set_error_reporting) // used as registry key | 1530 | # define EXTENDED_STACK_TRACE_KEY ((void*)LG_set_error_reporting) // used as registry key |
1531 | 1531 | ||
1532 | #ifdef ERROR_FULL_STACK | ||
1533 | LUAG_FUNC( set_error_reporting) | 1532 | LUAG_FUNC( set_error_reporting) |
1534 | { | 1533 | { |
1535 | bool_t equal; | 1534 | bool_t equal; |
@@ -1554,7 +1553,6 @@ done: | |||
1554 | lua_rawset( L, LUA_REGISTRYINDEX); | 1553 | lua_rawset( L, LUA_REGISTRYINDEX); |
1555 | return 0; | 1554 | return 0; |
1556 | } | 1555 | } |
1557 | #endif // ERROR_FULL_STACK | ||
1558 | 1556 | ||
1559 | static int lane_error( lua_State* L) | 1557 | static int lane_error( lua_State* L) |
1560 | { | 1558 | { |
@@ -1735,8 +1733,8 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
1735 | // [3..top]: parameters | 1733 | // [3..top]: parameters |
1736 | // | 1734 | // |
1737 | rc= lua_pcall( L, lua_gettop(L)-2, LUA_MULTRET, 1 /*error handler*/ ); | 1735 | rc= lua_pcall( L, lua_gettop(L)-2, LUA_MULTRET, 1 /*error handler*/ ); |
1738 | // 0: no error | 1736 | // 0: no error, body return values are on the stack |
1739 | // LUA_ERRRUN: a runtime error (error pushed on stack) | 1737 | // LUA_ERRRUN: cancellation or a runtime error (error pushed on stack) |
1740 | // LUA_ERRMEM: memory allocation error | 1738 | // LUA_ERRMEM: memory allocation error |
1741 | // LUA_ERRERR: error while running the error handler (if any) | 1739 | // LUA_ERRERR: error while running the error handler (if any) |
1742 | 1740 | ||
@@ -1744,14 +1742,12 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
1744 | 1742 | ||
1745 | lua_remove(L,1); // remove error handler | 1743 | lua_remove(L,1); // remove error handler |
1746 | 1744 | ||
1747 | // Lua 5.1 error handler is limited to one return value; taking stack trace | 1745 | // Lua 5.1 error handler is limited to one return value; taking stack trace via registry |
1748 | // via registry | 1746 | if( rc != LUA_OK) |
1749 | // | ||
1750 | if( rc != 0) | ||
1751 | { | 1747 | { |
1752 | STACK_GROW(L,1); | 1748 | STACK_GROW(L,1); |
1753 | lua_pushlightuserdata( L, STACK_TRACE_KEY ); | 1749 | lua_pushlightuserdata( L, STACK_TRACE_KEY ); |
1754 | lua_gettable(L, LUA_REGISTRYINDEX); | 1750 | lua_gettable(L, LUA_REGISTRYINDEX); // yields nil if no stack was generated (in case of cancellation for example) |
1755 | 1751 | ||
1756 | // For cancellation, a stack trace isn't placed | 1752 | // For cancellation, a stack trace isn't placed |
1757 | // | 1753 | // |
@@ -1773,7 +1769,7 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
1773 | // LUA_ERRMEM(4): memory allocation error | 1769 | // LUA_ERRMEM(4): memory allocation error |
1774 | #endif | 1770 | #endif |
1775 | 1771 | ||
1776 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p body: %s\n" INDENT_END, L, get_errcode_name( rc))); | 1772 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END, L, get_errcode_name( rc), (lua_touserdata(L,1)==CANCEL_ERROR) ? "cancelled" : lua_typename( L, lua_type( L, 1)))); |
1777 | //STACK_DUMP(L); | 1773 | //STACK_DUMP(L); |
1778 | // Call finalizers, if the script has set them up. | 1774 | // Call finalizers, if the script has set them up. |
1779 | // | 1775 | // |
@@ -1831,6 +1827,26 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
1831 | return 0; // ignored | 1827 | return 0; // ignored |
1832 | } | 1828 | } |
1833 | 1829 | ||
1830 | // --- If a client wants to transfer stuff of a given module from the current state to another Lane, the module must be required | ||
1831 | // with lanes.require, that will call the regular 'require', then populate lookup databases in source and keeper states | ||
1832 | // module = lanes.require( "modname") | ||
1833 | // upvalue[1]: _G.require | ||
1834 | LUAG_FUNC( require) | ||
1835 | { | ||
1836 | char const* name = lua_tostring( L, 1); | ||
1837 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.require %s BEGIN\n" INDENT_END, name)); | ||
1838 | DEBUGSPEW_CODE( ++ debugspew_indent_depth); | ||
1839 | lua_pushvalue( L, lua_upvalueindex(1)); // "name" require | ||
1840 | lua_pushvalue( L, 1); // "name" require "name" | ||
1841 | lua_call( L, 1, 1); // "name" module | ||
1842 | populate_func_lookup_table( L, -1, name); | ||
1843 | lua_insert( L, -2); // module "name" | ||
1844 | populate_keepers( L); | ||
1845 | lua_pop( L, 1); // module | ||
1846 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.require %s END\n" INDENT_END, name)); | ||
1847 | DEBUGSPEW_CODE( -- debugspew_indent_depth); | ||
1848 | return 1; | ||
1849 | } | ||
1834 | 1850 | ||
1835 | //--- | 1851 | //--- |
1836 | // lane_ud= thread_new( function, [libs_str], | 1852 | // lane_ud= thread_new( function, [libs_str], |
@@ -1844,32 +1860,6 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
1844 | // Upvalues: metatable to use for 'lane_ud' | 1860 | // Upvalues: metatable to use for 'lane_ud' |
1845 | // | 1861 | // |
1846 | 1862 | ||
1847 | // helper function to require a module in the keeper states and in the target state | ||
1848 | // source state contains module name at the top of the stack | ||
1849 | static void require_one_module( lua_State* L, lua_State* L2, bool_t _fatal) | ||
1850 | { | ||
1851 | size_t len; | ||
1852 | char const* name = lua_tolstring( L, -1, &len); | ||
1853 | // require the module in the target lane | ||
1854 | STACK_GROW( L2, 2); | ||
1855 | lua_getglobal( L2, "require"); | ||
1856 | if( lua_isnil( L2, -1)) | ||
1857 | { | ||
1858 | lua_pop( L2, 1); | ||
1859 | if( _fatal) | ||
1860 | { | ||
1861 | luaL_error( L, "cannot pre-require modules without loading 'package' library first"); | ||
1862 | } | ||
1863 | } | ||
1864 | else | ||
1865 | { | ||
1866 | lua_pushlstring( L2, name, len); | ||
1867 | lua_pcall( L2, 1, 0, 0); | ||
1868 | // we need to require this module in the keeper states as well | ||
1869 | populate_keepers( L); | ||
1870 | } | ||
1871 | } | ||
1872 | |||
1873 | LUAG_FUNC( thread_new) | 1863 | LUAG_FUNC( thread_new) |
1874 | { | 1864 | { |
1875 | lua_State* L2; | 1865 | lua_State* L2; |
@@ -1877,17 +1867,17 @@ LUAG_FUNC( thread_new) | |||
1877 | struct s_lane** ud; | 1867 | struct s_lane** ud; |
1878 | 1868 | ||
1879 | char const* libs = lua_tostring( L, 2); | 1869 | char const* libs = lua_tostring( L, 2); |
1880 | lua_CFunction on_state_create = lua_iscfunction( L, 3) ? lua_tocfunction( L, 3) : NULL; | 1870 | int const on_state_create = lua_isfunction( L, 3) ? 3 : 0; |
1881 | uint_t cs = luaG_optunsigned( L, 4, 0); | 1871 | uint_t cs = luaG_optunsigned( L, 4, 0); |
1882 | int prio = (int) luaL_optinteger( L, 5, 0); | 1872 | int prio = (int) luaL_optinteger( L, 5, 0); |
1883 | uint_t glob = luaG_isany( L, 6) ? 6 : 0; | 1873 | uint_t glob = lua_isnoneornil( L, 6) ? 0 : 6; |
1884 | uint_t package = luaG_isany( L,7) ? 7 : 0; | 1874 | uint_t package = lua_isnoneornil( L,7) ? 0 : 7; |
1885 | uint_t required = luaG_isany( L, 8) ? 8 : 0; | 1875 | uint_t required = lua_isnoneornil( L, 8) ? 0 : 8; |
1886 | 1876 | ||
1887 | #define FIXED_ARGS 8 | 1877 | #define FIXED_ARGS 8 |
1888 | uint_t args= lua_gettop(L) - FIXED_ARGS; | 1878 | uint_t args= lua_gettop(L) - FIXED_ARGS; |
1889 | 1879 | ||
1890 | if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) | 1880 | if( prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) |
1891 | { | 1881 | { |
1892 | return luaL_error( L, "Priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio); | 1882 | return luaL_error( L, "Priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio); |
1893 | } | 1883 | } |
@@ -1898,7 +1888,7 @@ LUAG_FUNC( thread_new) | |||
1898 | 1888 | ||
1899 | // populate with selected libraries at the same time | 1889 | // populate with selected libraries at the same time |
1900 | // | 1890 | // |
1901 | L2 = luaG_newstate( L, libs, on_state_create); | 1891 | L2 = luaG_newstate( L, on_state_create, libs); |
1902 | 1892 | ||
1903 | STACK_GROW( L, 2); | 1893 | STACK_GROW( L, 2); |
1904 | STACK_GROW( L2, 3); | 1894 | STACK_GROW( L2, 3); |
@@ -1918,17 +1908,6 @@ LUAG_FUNC( thread_new) | |||
1918 | 1908 | ||
1919 | // modules to require in the target lane *before* the function is transfered! | 1909 | // modules to require in the target lane *before* the function is transfered! |
1920 | 1910 | ||
1921 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: require 'lanes.core'\n" INDENT_END)); | ||
1922 | //start by requiring lanes.core, since it is a bit special | ||
1923 | // 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) | ||
1924 | STACK_CHECK( L); | ||
1925 | STACK_CHECK( L2); | ||
1926 | lua_pushliteral( L, "lanes.core"); | ||
1927 | require_one_module( L, L2, FALSE); | ||
1928 | lua_pop( L, 1); | ||
1929 | STACK_END( L2, 0); | ||
1930 | STACK_END (L, 0); | ||
1931 | |||
1932 | STACK_CHECK( L); | 1911 | STACK_CHECK( L); |
1933 | STACK_CHECK( L2); | 1912 | STACK_CHECK( L2); |
1934 | if( required) | 1913 | if( required) |
@@ -1941,6 +1920,7 @@ LUAG_FUNC( thread_new) | |||
1941 | { | 1920 | { |
1942 | return luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required)); | 1921 | return luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required)); |
1943 | } | 1922 | } |
1923 | |||
1944 | lua_pushnil( L); | 1924 | lua_pushnil( L); |
1945 | while( lua_next( L, required) != 0) | 1925 | while( lua_next( L, required) != 0) |
1946 | { | 1926 | { |
@@ -1950,7 +1930,32 @@ LUAG_FUNC( thread_new) | |||
1950 | } | 1930 | } |
1951 | else | 1931 | else |
1952 | { | 1932 | { |
1953 | require_one_module( L, L2, TRUE); | 1933 | // require the module in the target state, and populate the lookup table there too |
1934 | size_t len; | ||
1935 | char const* name = lua_tolstring( L, -1, &len); | ||
1936 | |||
1937 | // require the module in the target lane | ||
1938 | STACK_GROW( L2, 2); | ||
1939 | STACK_CHECK( L2); | ||
1940 | lua_getglobal( L2, "require"); // require()? | ||
1941 | if( lua_isnil( L2, -1)) | ||
1942 | { | ||
1943 | lua_pop( L2, 1); // | ||
1944 | luaL_error( L, "cannot pre-require modules without loading 'package' library first"); | ||
1945 | } | ||
1946 | else | ||
1947 | { | ||
1948 | lua_pushlstring( L2, name, len); // require() name | ||
1949 | lua_pcall( L2, 1, 1, 0); // ret | ||
1950 | STACK_MID( L2, 1); | ||
1951 | // after requiring the module, register the functions it exported in our name<->function database | ||
1952 | populate_func_lookup_table( L2, -1, name); | ||
1953 | STACK_MID( L2, 1); | ||
1954 | lua_pop( L2, 1); | ||
1955 | // don't require this module in the keeper states as well, use lanes.require() for that! | ||
1956 | //populate_keepers( L); | ||
1957 | } | ||
1958 | STACK_END( L2, 0); | ||
1954 | } | 1959 | } |
1955 | lua_pop( L, 1); | 1960 | lua_pop( L, 1); |
1956 | ++ nbRequired; | 1961 | ++ nbRequired; |
@@ -2578,7 +2583,7 @@ static const struct luaL_Reg lanes_functions [] = { | |||
2578 | /* | 2583 | /* |
2579 | * One-time initializations | 2584 | * One-time initializations |
2580 | */ | 2585 | */ |
2581 | static void init_once_LOCKED( lua_State* L, int const nbKeepers, lua_CFunction _on_state_create, lua_Number _shutdown_timeout, bool_t _track_lanes) | 2586 | static void init_once_LOCKED( lua_State* L, int const _on_state_create, int const nbKeepers, lua_Number _shutdown_timeout, bool_t _track_lanes) |
2582 | { | 2587 | { |
2583 | char const* err; | 2588 | char const* err; |
2584 | 2589 | ||
@@ -2603,7 +2608,7 @@ static void init_once_LOCKED( lua_State* L, int const nbKeepers, lua_CFunction _ | |||
2603 | // | 2608 | // |
2604 | MUTEX_RECURSIVE_INIT( &require_cs ); | 2609 | MUTEX_RECURSIVE_INIT( &require_cs ); |
2605 | 2610 | ||
2606 | serialize_require( L ); | 2611 | serialize_require( L); |
2607 | 2612 | ||
2608 | // Linked chains handling | 2613 | // Linked chains handling |
2609 | // | 2614 | // |
@@ -2635,7 +2640,7 @@ static void init_once_LOCKED( lua_State* L, int const nbKeepers, lua_CFunction _ | |||
2635 | } | 2640 | } |
2636 | #endif | 2641 | #endif |
2637 | #endif | 2642 | #endif |
2638 | err = init_keepers( L, nbKeepers, _on_state_create); | 2643 | err = init_keepers( L, _on_state_create, nbKeepers); |
2639 | if (err) | 2644 | if (err) |
2640 | { | 2645 | { |
2641 | (void) luaL_error( L, "Unable to initialize: %s", err ); | 2646 | (void) luaL_error( L, "Unable to initialize: %s", err ); |
@@ -2691,7 +2696,7 @@ LUAG_FUNC( configure) | |||
2691 | char const* name = luaL_checkstring( L, lua_upvalueindex( 1)); | 2696 | char const* name = luaL_checkstring( L, lua_upvalueindex( 1)); |
2692 | // all parameter checks are done lua-side | 2697 | // all parameter checks are done lua-side |
2693 | int const nbKeepers = (int)lua_tointeger( L, 1); | 2698 | int const nbKeepers = (int)lua_tointeger( L, 1); |
2694 | lua_CFunction on_state_create = lua_iscfunction( L, 2) ? lua_tocfunction( L, 2) : NULL; | 2699 | int const on_state_create = lua_isfunction( L, 2) ? 2 : 0; |
2695 | lua_Number shutdown_timeout = lua_tonumber( L, 3); | 2700 | lua_Number shutdown_timeout = lua_tonumber( L, 3); |
2696 | bool_t track_lanes = lua_toboolean( L, 4); | 2701 | bool_t track_lanes = lua_toboolean( L, 4); |
2697 | 2702 | ||
@@ -2732,6 +2737,11 @@ LUAG_FUNC( configure) | |||
2732 | lua_pushcclosure( L, LG_thread_new, 1); // ... M LG_thread_new | 2737 | lua_pushcclosure( L, LG_thread_new, 1); // ... M LG_thread_new |
2733 | lua_setfield(L, -2, "thread_new"); // ... M | 2738 | lua_setfield(L, -2, "thread_new"); // ... M |
2734 | 2739 | ||
2740 | // we can't register 'lanes.require' normally because we want to create an upvalued closure | ||
2741 | lua_getglobal( L, "require"); // ... M require | ||
2742 | lua_pushcclosure( L, LG_require, 1); // ... M lanes.require | ||
2743 | lua_setfield( L, -2, "require"); // ... M | ||
2744 | |||
2735 | lua_pushstring(L, VERSION); // ... M VERSION | 2745 | lua_pushstring(L, VERSION); // ... M VERSION |
2736 | lua_setfield(L, -2, "version"); // ... M | 2746 | lua_setfield(L, -2, "version"); // ... M |
2737 | 2747 | ||
@@ -2743,7 +2753,7 @@ LUAG_FUNC( configure) | |||
2743 | 2753 | ||
2744 | // register all native functions found in that module in the transferable functions database | 2754 | // register all native functions found in that module in the transferable functions database |
2745 | // we process it before _G because we don't want to find the module when scanning _G (this would generate longer names) | 2755 | // we process it before _G because we don't want to find the module when scanning _G (this would generate longer names) |
2746 | // for example in package.loaded.lanes.core.* | 2756 | // for example in package.loaded["lanes.core"].* |
2747 | populate_func_lookup_table( L, -1, name); | 2757 | populate_func_lookup_table( L, -1, name); |
2748 | 2758 | ||
2749 | // record all existing C/JIT-fast functions | 2759 | // record all existing C/JIT-fast functions |
@@ -2768,7 +2778,7 @@ LUAG_FUNC( configure) | |||
2768 | static volatile int /*bool*/ go_ahead; // = 0 | 2778 | static volatile int /*bool*/ go_ahead; // = 0 |
2769 | if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0) | 2779 | if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0) |
2770 | { | 2780 | { |
2771 | init_once_LOCKED( L, nbKeepers, on_state_create, shutdown_timeout, track_lanes); | 2781 | init_once_LOCKED( L, on_state_create, nbKeepers, shutdown_timeout, track_lanes); |
2772 | go_ahead = 1; // let others pass | 2782 | go_ahead = 1; // let others pass |
2773 | } | 2783 | } |
2774 | else | 2784 | else |
@@ -2786,7 +2796,7 @@ LUAG_FUNC( configure) | |||
2786 | // | 2796 | // |
2787 | if( s_initCount == 0) | 2797 | if( s_initCount == 0) |
2788 | { | 2798 | { |
2789 | init_once_LOCKED( L, nbKeepers, on_state_create, shutdown_timeout, track_lanes); | 2799 | init_once_LOCKED( L, on_state_create, nbKeepers, shutdown_timeout, track_lanes); |
2790 | s_initCount = 1; | 2800 | s_initCount = 1; |
2791 | } | 2801 | } |
2792 | } | 2802 | } |
diff --git a/src/lanes.lua b/src/lanes.lua index 1d50f97..f4eef81 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
@@ -123,9 +123,6 @@ lanes.configure = function( _params) | |||
123 | 123 | ||
124 | local set_singlethreaded = assert( core.set_singlethreaded) | 124 | local set_singlethreaded = assert( core.set_singlethreaded) |
125 | 125 | ||
126 | local now_secs = assert( core.now_secs) | ||
127 | local wakeup_conv = assert( core.wakeup_conv) | ||
128 | |||
129 | local max_prio = assert( core.max_prio) | 126 | local max_prio = assert( core.max_prio) |
130 | 127 | ||
131 | lanes.ABOUT= | 128 | lanes.ABOUT= |
@@ -311,6 +308,7 @@ local linda = core.linda | |||
311 | 308 | ||
312 | -- PUBLIC LANES API | 309 | -- PUBLIC LANES API |
313 | local timer = function() error "timers are not active" end | 310 | local timer = function() error "timers are not active" end |
311 | local timer_lane = nil | ||
314 | local timers = timer | 312 | local timers = timer |
315 | 313 | ||
316 | if _params.with_timers ~= false then | 314 | if _params.with_timers ~= false then |
@@ -338,158 +336,161 @@ timer_gateway:set(first_time_key,true) | |||
338 | -- has 'table' always declared) | 336 | -- has 'table' always declared) |
339 | -- | 337 | -- |
340 | if first_time then | 338 | if first_time then |
341 | local table_remove= assert( table.remove ) | ||
342 | local table_insert= assert( table.insert ) | ||
343 | 339 | ||
344 | -- | 340 | ----- |
345 | -- { [deep_linda_lightuserdata]= { [deep_linda_lightuserdata]=linda_h, | 341 | -- Snore loop (run as a lane on the background) |
346 | -- [key]= { wakeup_secs [,period_secs] } [, ...] }, | 342 | -- |
347 | -- } | 343 | -- High priority, to get trustworthy timings. |
348 | -- | 344 | -- |
349 | -- Collection of all running timers, indexed with linda's & key. | 345 | -- We let the timer lane be a "free running" thread; no handle to it |
350 | -- | 346 | -- remains. |
351 | -- Note that we need to use the deep lightuserdata identifiers, instead | 347 | -- |
352 | -- of 'linda_h' themselves as table indices. Otherwise, we'd get multiple | 348 | local timer_body = function() |
353 | -- entries for the same timer. | 349 | -- require lanes.core inside the timer body to prevent pulling now_secs() through an uvpvalue |
354 | -- | 350 | local core = require "lanes.core" |
355 | -- The 'hidden' reference to Linda proxy is used in 'check_timers()' but | 351 | |
356 | -- also important to keep the Linda alive, even if all outside world threw | 352 | -- |
357 | -- away pointers to it (which would ruin uniqueness of the deep pointer). | 353 | -- { [deep_linda_lightuserdata]= { [deep_linda_lightuserdata]=linda_h, |
358 | -- Now we're safe. | 354 | -- [key]= { wakeup_secs [,period_secs] } [, ...] }, |
359 | -- | 355 | -- } |
360 | local collection= {} | 356 | -- |
361 | 357 | -- Collection of all running timers, indexed with linda's & key. | |
362 | local function get_timers() | 358 | -- |
363 | local r = {} | 359 | -- Note that we need to use the deep lightuserdata identifiers, instead |
364 | for deep, t in pairs( collection) do | 360 | -- of 'linda_h' themselves as table indices. Otherwise, we'd get multiple |
365 | -- WR( tostring( deep)) | 361 | -- entries for the same timer. |
366 | local l = t[deep] | 362 | -- |
367 | for key, timer_data in pairs( t) do | 363 | -- The 'hidden' reference to Linda proxy is used in 'check_timers()' but |
368 | if key ~= deep then | 364 | -- also important to keep the Linda alive, even if all outside world threw |
369 | table_insert( r, {l, key, timer_data}) | 365 | -- away pointers to it (which would ruin uniqueness of the deep pointer). |
370 | end | 366 | -- Now we're safe. |
371 | end | 367 | -- |
372 | end | 368 | local collection = {} |
373 | return r | 369 | local table_insert = assert( table.insert) |
374 | end | 370 | |
375 | -- | 371 | local get_timers = function() |
376 | -- set_timer( linda_h, key [,wakeup_at_secs [,period_secs]] ) | 372 | local r = {} |
377 | -- | 373 | for deep, t in pairs( collection) do |
378 | local function set_timer( linda, key, wakeup_at, period ) | 374 | -- WR( tostring( deep)) |
379 | assert( wakeup_at==nil or wakeup_at>0.0 ) | 375 | local l = t[deep] |
380 | assert( period==nil or period>0.0 ) | 376 | for key, timer_data in pairs( t) do |
377 | if key ~= deep then | ||
378 | table_insert( r, {l, key, timer_data}) | ||
379 | end | ||
380 | end | ||
381 | end | ||
382 | return r | ||
383 | end -- get_timers() | ||
381 | 384 | ||
382 | local linda_deep= linda:deep() | 385 | -- |
383 | assert( linda_deep ) | 386 | -- set_timer( linda_h, key [,wakeup_at_secs [,period_secs]] ) |
387 | -- | ||
388 | local set_timer = function( linda, key, wakeup_at, period) | ||
389 | assert( wakeup_at == nil or wakeup_at > 0.0) | ||
390 | assert( period == nil or period > 0.0) | ||
384 | 391 | ||
385 | -- Find or make a lookup for this timer | 392 | local linda_deep = linda:deep() |
386 | -- | 393 | assert( linda_deep) |
387 | local t1= collection[linda_deep] | 394 | |
388 | if not t1 then | 395 | -- Find or make a lookup for this timer |
389 | t1= { [linda_deep]= linda } -- proxy to use the Linda | 396 | -- |
390 | collection[linda_deep]= t1 | 397 | local t1 = collection[linda_deep] |
391 | end | 398 | if not t1 then |
392 | 399 | t1 = { [linda_deep] = linda} -- proxy to use the Linda | |
393 | if wakeup_at==nil then | 400 | collection[linda_deep] = t1 |
394 | -- Clear the timer | 401 | end |
395 | -- | 402 | |
396 | t1[key]= nil | 403 | if wakeup_at == nil then |
397 | 404 | -- Clear the timer | |
398 | -- Remove empty tables from collection; speeds timer checks and | 405 | -- |
399 | -- lets our 'safety reference' proxy be gc:ed as well. | 406 | t1[key]= nil |
400 | -- | 407 | |
401 | local empty= true | 408 | -- Remove empty tables from collection; speeds timer checks and |
402 | for k,_ in pairs(t1) do | 409 | -- lets our 'safety reference' proxy be gc:ed as well. |
403 | if k~= linda_deep then | 410 | -- |
404 | empty= false; break | 411 | local empty = true |
405 | end | 412 | for k, _ in pairs( t1) do |
406 | end | 413 | if k ~= linda_deep then |
407 | if empty then | 414 | empty = false |
408 | collection[linda_deep]= nil | 415 | break |
409 | end | 416 | end |
410 | 417 | end | |
411 | -- Note: any unread timer value is left at 'linda[key]' intensionally; | 418 | if empty then |
412 | -- clearing a timer just stops it. | 419 | collection[linda_deep] = nil |
413 | else | 420 | end |
414 | -- New timer or changing the timings | 421 | |
415 | -- | 422 | -- Note: any unread timer value is left at 'linda[key]' intensionally; |
416 | local t2= t1[key] | 423 | -- clearing a timer just stops it. |
417 | if not t2 then | 424 | else |
418 | t2= {}; t1[key]= t2 | 425 | -- New timer or changing the timings |
419 | end | 426 | -- |
420 | 427 | local t2 = t1[key] | |
421 | t2[1]= wakeup_at | 428 | if not t2 then |
422 | t2[2]= period -- can be 'nil' | 429 | t2= {} |
423 | end | 430 | t1[key]= t2 |
424 | end | 431 | end |
432 | |||
433 | t2[1] = wakeup_at | ||
434 | t2[2] = period -- can be 'nil' | ||
435 | end | ||
436 | end -- set_timer() | ||
437 | |||
438 | local now_secs = core.now_secs | ||
439 | assert( type( now_secs) == "function") | ||
440 | ----- | ||
441 | -- [next_wakeup_at]= check_timers() | ||
442 | -- Check timers, and wake up the ones expired (if any) | ||
443 | -- Returns the closest upcoming (remaining) wakeup time (or 'nil' if none). | ||
444 | local check_timers = function() | ||
445 | local now = now_secs() | ||
446 | local next_wakeup | ||
447 | |||
448 | for linda_deep,t1 in pairs(collection) do | ||
449 | for key,t2 in pairs(t1) do | ||
450 | -- | ||
451 | if key==linda_deep then | ||
452 | -- no 'continue' in Lua :/ | ||
453 | else | ||
454 | -- 't2': { wakeup_at_secs [,period_secs] } | ||
455 | -- | ||
456 | local wakeup_at= t2[1] | ||
457 | local period= t2[2] -- may be 'nil' | ||
458 | |||
459 | if wakeup_at <= now then | ||
460 | local linda= t1[linda_deep] | ||
461 | assert(linda) | ||
462 | |||
463 | linda:set( key, now ) | ||
464 | |||
465 | -- 'pairs()' allows the values to be modified (and even | ||
466 | -- removed) as far as keys are not touched | ||
467 | |||
468 | if not period then | ||
469 | -- one-time timer; gone | ||
470 | -- | ||
471 | t1[key]= nil | ||
472 | wakeup_at= nil -- no 'continue' in Lua :/ | ||
473 | else | ||
474 | -- repeating timer; find next wakeup (may jump multiple repeats) | ||
475 | -- | ||
476 | repeat | ||
477 | wakeup_at= wakeup_at+period | ||
478 | until wakeup_at > now | ||
479 | |||
480 | t2[1]= wakeup_at | ||
481 | end | ||
482 | end | ||
483 | |||
484 | if wakeup_at and ((not next_wakeup) or (wakeup_at < next_wakeup)) then | ||
485 | next_wakeup= wakeup_at | ||
486 | end | ||
487 | end | ||
488 | end -- t2 loop | ||
489 | end -- t1 loop | ||
490 | |||
491 | return next_wakeup -- may be 'nil' | ||
492 | end -- check_timers() | ||
425 | 493 | ||
426 | ----- | ||
427 | -- [next_wakeup_at]= check_timers() | ||
428 | -- | ||
429 | -- Check timers, and wake up the ones expired (if any) | ||
430 | -- | ||
431 | -- Returns the closest upcoming (remaining) wakeup time (or 'nil' if none). | ||
432 | -- | ||
433 | local function check_timers() | ||
434 | |||
435 | local now= now_secs() | ||
436 | local next_wakeup | ||
437 | |||
438 | for linda_deep,t1 in pairs(collection) do | ||
439 | for key,t2 in pairs(t1) do | ||
440 | -- | ||
441 | if key==linda_deep then | ||
442 | -- no 'continue' in Lua :/ | ||
443 | else | ||
444 | -- 't2': { wakeup_at_secs [,period_secs] } | ||
445 | -- | ||
446 | local wakeup_at= t2[1] | ||
447 | local period= t2[2] -- may be 'nil' | ||
448 | |||
449 | if wakeup_at <= now then | ||
450 | local linda= t1[linda_deep] | ||
451 | assert(linda) | ||
452 | |||
453 | linda:set( key, now ) | ||
454 | |||
455 | -- 'pairs()' allows the values to be modified (and even | ||
456 | -- removed) as far as keys are not touched | ||
457 | |||
458 | if not period then | ||
459 | -- one-time timer; gone | ||
460 | -- | ||
461 | t1[key]= nil | ||
462 | wakeup_at= nil -- no 'continue' in Lua :/ | ||
463 | else | ||
464 | -- repeating timer; find next wakeup (may jump multiple repeats) | ||
465 | -- | ||
466 | repeat | ||
467 | wakeup_at= wakeup_at+period | ||
468 | until wakeup_at > now | ||
469 | |||
470 | t2[1]= wakeup_at | ||
471 | end | ||
472 | end | ||
473 | |||
474 | if wakeup_at and ((not next_wakeup) or (wakeup_at < next_wakeup)) then | ||
475 | next_wakeup= wakeup_at | ||
476 | end | ||
477 | end | ||
478 | end -- t2 loop | ||
479 | end -- t1 loop | ||
480 | |||
481 | return next_wakeup -- may be 'nil' | ||
482 | end | ||
483 | |||
484 | ----- | ||
485 | -- Snore loop (run as a lane on the background) | ||
486 | -- | ||
487 | -- High priority, to get trustworthy timings. | ||
488 | -- | ||
489 | -- We let the timer lane be a "free running" thread; no handle to it | ||
490 | -- remains. | ||
491 | -- | ||
492 | local timer_body = function() | ||
493 | local timer_gateway_batched = timer_gateway.batched | 494 | local timer_gateway_batched = timer_gateway.batched |
494 | set_debug_threadname( "LanesTimer") | 495 | set_debug_threadname( "LanesTimer") |
495 | set_finalizer( function( err, stk) | 496 | set_finalizer( function( err, stk) |
@@ -502,7 +503,7 @@ if first_time then | |||
502 | end | 503 | end |
503 | end) | 504 | end) |
504 | while true do | 505 | while true do |
505 | local next_wakeup= check_timers() | 506 | local next_wakeup = check_timers() |
506 | 507 | ||
507 | -- Sleep until next timer to wake up, or a set/clear command | 508 | -- Sleep until next timer to wake up, or a set/clear command |
508 | -- | 509 | -- |
@@ -528,9 +529,9 @@ if first_time then | |||
528 | -- WR( "timer lane: no linda, aborted?") | 529 | -- WR( "timer lane: no linda, aborted?") |
529 | end | 530 | end |
530 | end | 531 | end |
531 | end | 532 | end -- timer_body() |
532 | gen( "*", { package= {}, priority = max_prio}, timer_body)() -- "*" instead of "io,package" for LuaJIT compatibility... | 533 | timer_lane = gen( "*", { package= {}, priority = max_prio}, timer_body)() -- "*" instead of "io,package" for LuaJIT compatibility... |
533 | end | 534 | end -- first_time |
534 | 535 | ||
535 | ----- | 536 | ----- |
536 | -- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] ) | 537 | -- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] ) |
@@ -540,13 +541,13 @@ timer = function( linda, key, a, period ) | |||
540 | if getmetatable( linda) ~= "Linda" then | 541 | if getmetatable( linda) ~= "Linda" then |
541 | error "expecting a Linda" | 542 | error "expecting a Linda" |
542 | end | 543 | end |
543 | if a==0.0 then | 544 | if a == 0.0 then |
544 | -- Caller expects to get current time stamp in Linda, on return | 545 | -- Caller expects to get current time stamp in Linda, on return |
545 | -- (like the timer had expired instantly); it would be good to set this | 546 | -- (like the timer had expired instantly); it would be good to set this |
546 | -- as late as possible (to give most current time) but also we want it | 547 | -- as late as possible (to give most current time) but also we want it |
547 | -- to precede any possible timers that might start striking. | 548 | -- to precede any possible timers that might start striking. |
548 | -- | 549 | -- |
549 | linda:set( key, now_secs() ) | 550 | linda:set( key, core.now_secs()) |
550 | 551 | ||
551 | if not period or period==0.0 then | 552 | if not period or period==0.0 then |
552 | timer_gateway:send( TGW_KEY, linda, key, nil, nil ) -- clear the timer | 553 | timer_gateway:send( TGW_KEY, linda, key, nil, nil ) -- clear the timer |
@@ -555,8 +556,8 @@ timer = function( linda, key, a, period ) | |||
555 | a= period | 556 | a= period |
556 | end | 557 | end |
557 | 558 | ||
558 | local wakeup_at= type(a)=="table" and wakeup_conv(a) -- given point of time | 559 | local wakeup_at= type(a)=="table" and core.wakeup_conv(a) -- given point of time |
559 | or (a and now_secs()+a or nil) | 560 | or (a and core.now_secs()+a or nil) |
560 | -- queue to timer | 561 | -- queue to timer |
561 | -- | 562 | -- |
562 | timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period ) | 563 | timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period ) |
@@ -639,15 +640,17 @@ local function genatomic( linda, key, initial_val ) | |||
639 | end | 640 | end |
640 | 641 | ||
641 | -- activate full interface | 642 | -- activate full interface |
643 | lanes.require = core.require | ||
642 | lanes.gen = gen | 644 | lanes.gen = gen |
643 | lanes.linda = core.linda | 645 | lanes.linda = core.linda |
644 | lanes.cancel_error = core.cancel_error | 646 | lanes.cancel_error = core.cancel_error |
645 | lanes.nameof = core.nameof | 647 | lanes.nameof = core.nameof |
646 | lanes.threads = (_params.track_lanes and core.threads) and core.threads or function() error "lane tracking is not available" end | 648 | lanes.threads = (_params.track_lanes and core.threads) and core.threads or function() error "lane tracking is not available" end |
647 | lanes.timer = timer | 649 | lanes.timer = timer |
650 | lanes.timer_lane = timer_lane | ||
648 | lanes.timers = timers | 651 | lanes.timers = timers |
649 | lanes.genlock = genlock | 652 | lanes.genlock = genlock |
650 | lanes.now_secs = now_secs | 653 | lanes.now_secs = core.now_secs |
651 | lanes.genatomic = genatomic | 654 | lanes.genatomic = genatomic |
652 | -- from now on, calling configure does nothing but checking that we don't call it with parameters that changed compared to the first invocation | 655 | -- from now on, calling configure does nothing but checking that we don't call it with parameters that changed compared to the first invocation |
653 | lanes.configure = function( _params2) | 656 | lanes.configure = function( _params2) |
diff --git a/src/tools.c b/src/tools.c index 552e61e..9961c1a 100644 --- a/src/tools.c +++ b/src/tools.c | |||
@@ -224,7 +224,7 @@ static char const* luaG_pushFQN(lua_State *L, int t, int last) | |||
224 | { | 224 | { |
225 | lua_rawgeti( L, t, i); | 225 | lua_rawgeti( L, t, i); |
226 | luaL_addvalue( &b); | 226 | luaL_addvalue( &b); |
227 | luaL_addlstring(&b, ".", 1); | 227 | luaL_addlstring(&b, "/", 1); |
228 | } | 228 | } |
229 | if( i == last) // add last value (if interval was not empty) | 229 | if( i == last) // add last value (if interval was not empty) |
230 | { | 230 | { |
@@ -250,9 +250,16 @@ static void populate_func_lookup_table_recur( lua_State* L, int _ctx_base, int _ | |||
250 | int const breadth_first_cache = lua_gettop( L) + 1; | 250 | int const breadth_first_cache = lua_gettop( L) + 1; |
251 | 251 | ||
252 | STACK_GROW( L, 6); | 252 | STACK_GROW( L, 6); |
253 | // slot _i contains a table where we search for functions | 253 | // slot _i contains a table where we search for functions (or a full userdata with a metatable) |
254 | STACK_CHECK( L); // ... {_i} | 254 | STACK_CHECK( L); // ... {_i} |
255 | 255 | ||
256 | // if object is a userdata, replace it by its metatable | ||
257 | if( lua_type( L, _i) == LUA_TUSERDATA) | ||
258 | { | ||
259 | lua_getmetatable( L, _i); // ... {_i} mt | ||
260 | lua_replace( L, _i); // ... {_i} | ||
261 | } | ||
262 | |||
256 | // if table is already visited, we are done | 263 | // if table is already visited, we are done |
257 | lua_pushvalue( L, _i); // ... {_i} {} | 264 | lua_pushvalue( L, _i); // ... {_i} {} |
258 | lua_rawget( L, cache); // ... {_i} nil|n | 265 | lua_rawget( L, cache); // ... {_i} nil|n |
@@ -437,7 +444,7 @@ void populate_func_lookup_table( lua_State* L, int _i, char const* _name) | |||
437 | * | 444 | * |
438 | */ | 445 | */ |
439 | 446 | ||
440 | lua_State* luaG_newstate( lua_State* _from, char const* libs, lua_CFunction _on_state_create) | 447 | lua_State* luaG_newstate( lua_State* _from, int const _on_state_create, char const* libs) |
441 | { | 448 | { |
442 | // reuse alloc function from the originating state | 449 | // reuse alloc function from the originating state |
443 | void* allocUD; | 450 | void* allocUD; |
@@ -450,7 +457,7 @@ lua_State* luaG_newstate( lua_State* _from, char const* libs, lua_CFunction _on_ | |||
450 | } | 457 | } |
451 | 458 | ||
452 | // neither libs (not even 'base') nor special init func: we are done | 459 | // neither libs (not even 'base') nor special init func: we are done |
453 | if( !libs && !_on_state_create) | 460 | if( libs == NULL && _on_state_create <= 0) |
454 | { | 461 | { |
455 | return L; | 462 | return L; |
456 | } | 463 | } |
@@ -460,13 +467,6 @@ lua_State* luaG_newstate( lua_State* _from, char const* libs, lua_CFunction _on_ | |||
460 | 467 | ||
461 | STACK_GROW( L, 2); | 468 | STACK_GROW( L, 2); |
462 | STACK_CHECK( L); | 469 | STACK_CHECK( L); |
463 | if( _on_state_create) | ||
464 | { | ||
465 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "calling on_state_create()\n" INDENT_END)); | ||
466 | lua_pushcfunction( L, _on_state_create); | ||
467 | lua_call( L, 0, 0); | ||
468 | } | ||
469 | |||
470 | // 'lua.c' stops GC during initialization so perhaps its a good idea. :) | 470 | // 'lua.c' stops GC during initialization so perhaps its a good idea. :) |
471 | // but do it after _on_state_create in case it does a lot of stuff... | 471 | // but do it after _on_state_create in case it does a lot of stuff... |
472 | lua_gc( L, LUA_GCSTOP, 0); | 472 | lua_gc( L, LUA_GCSTOP, 0); |
@@ -520,7 +520,29 @@ lua_State* luaG_newstate( lua_State* _from, char const* libs, lua_CFunction _on_ | |||
520 | lua_gc( L, LUA_GCRESTART, 0); | 520 | lua_gc( L, LUA_GCRESTART, 0); |
521 | 521 | ||
522 | STACK_CHECK( L); | 522 | STACK_CHECK( L); |
523 | // after opening base, register the functions it exported in our name<->function database | 523 | // call this after the base libraries are loaded! |
524 | if( _on_state_create > 0) | ||
525 | { | ||
526 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "calling on_state_create()\n" INDENT_END)); | ||
527 | if( lua_iscfunction( _from, _on_state_create)) | ||
528 | { | ||
529 | // C function: recreate a closure in the new state, bypassing the lookup scheme | ||
530 | lua_CFunction osc = lua_tocfunction( _from, _on_state_create); | ||
531 | lua_pushcfunction( L, osc); | ||
532 | } | ||
533 | else | ||
534 | { | ||
535 | STACK_CHECK( _from); | ||
536 | // Lua function: transfer as usual (should work as long as it only uses base libraries) | ||
537 | lua_pushvalue( _from, _on_state_create); | ||
538 | luaG_inter_move( _from, L, 1); | ||
539 | STACK_END( _from, 0); | ||
540 | } | ||
541 | lua_call( L, 0, 0); | ||
542 | STACK_MID( L, 0); | ||
543 | } | ||
544 | |||
545 | // after all this, register everything we find in our name<->function database | ||
524 | lua_pushglobaltable( L); // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack | 546 | lua_pushglobaltable( L); // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack |
525 | populate_func_lookup_table( L, -1, NULL); | 547 | populate_func_lookup_table( L, -1, NULL); |
526 | lua_pop( L, 1); | 548 | lua_pop( L, 1); |
@@ -1180,9 +1202,9 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin | |||
1180 | 1202 | ||
1181 | static void push_cached_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i) | 1203 | static void push_cached_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i) |
1182 | { | 1204 | { |
1183 | void * const aspointer = (void*)lua_topointer( L, i); | 1205 | void* const aspointer = (void*)lua_topointer( L, i); |
1184 | // TBD: Merge this and same code for tables | 1206 | // TBD: Merge this and same code for tables |
1185 | ASSERT_L( L2_cache_i != 0 ); | 1207 | ASSERT_L( L2_cache_i != 0); |
1186 | 1208 | ||
1187 | STACK_GROW( L2, 2); | 1209 | STACK_GROW( L2, 2); |
1188 | 1210 | ||
@@ -1199,8 +1221,8 @@ static void push_cached_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, ui | |||
1199 | 1221 | ||
1200 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); | 1222 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); |
1201 | 1223 | ||
1202 | lua_pushvalue( L2, -1 ); // ... {cache} ... p p | 1224 | lua_pushvalue( L2, -1); // ... {cache} ... p p |
1203 | lua_rawget( L2, L2_cache_i ); // ... {cache} ... p function|nil|true | 1225 | lua_rawget( L2, L2_cache_i); // ... {cache} ... p function|nil|true |
1204 | 1226 | ||
1205 | if( lua_isnil(L2,-1)) // function is unknown | 1227 | if( lua_isnil(L2,-1)) // function is unknown |
1206 | { | 1228 | { |
@@ -1209,7 +1231,7 @@ static void push_cached_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, ui | |||
1209 | // Set to 'true' for the duration of creation; need to find self-references | 1231 | // Set to 'true' for the duration of creation; need to find self-references |
1210 | // via upvalues | 1232 | // via upvalues |
1211 | // | 1233 | // |
1212 | // pushes a copy of the func, a stores a reference in the cache | 1234 | // pushes a copy of the func, stores a reference in the cache |
1213 | inter_copy_func( L2, L2_cache_i, L, i); // ... {cache} ... function | 1235 | inter_copy_func( L2, L2_cache_i, L, i); // ... {cache} ... function |
1214 | } | 1236 | } |
1215 | else // found function in the cache | 1237 | else // found function in the cache |
@@ -1426,7 +1448,9 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin | |||
1426 | // if already on top of the stack, no need to push again | 1448 | // if already on top of the stack, no need to push again |
1427 | int needToPush = (i != (uint_t)lua_gettop( L)); | 1449 | int needToPush = (i != (uint_t)lua_gettop( L)); |
1428 | if( needToPush) | 1450 | if( needToPush) |
1451 | { | ||
1429 | lua_pushvalue( L, i); // ... f | 1452 | lua_pushvalue( L, i); // ... f |
1453 | } | ||
1430 | 1454 | ||
1431 | luaL_buffinit( L, &b); | 1455 | luaL_buffinit( L, &b); |
1432 | // | 1456 | // |
@@ -1568,11 +1592,11 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin | |||
1568 | * | 1592 | * |
1569 | * Returns TRUE if value was pushed, FALSE if its type is non-supported. | 1593 | * Returns TRUE if value was pushed, FALSE if its type is non-supported. |
1570 | */ | 1594 | */ |
1571 | static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i, enum e_vt vt ) | 1595 | static bool_t inter_copy_one_( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt) |
1572 | { | 1596 | { |
1573 | bool_t ret= TRUE; | 1597 | bool_t ret = TRUE; |
1574 | 1598 | ||
1575 | STACK_GROW( L2, 1 ); | 1599 | STACK_GROW( L2, 1); |
1576 | 1600 | ||
1577 | STACK_CHECK( L2); | 1601 | STACK_CHECK( L2); |
1578 | 1602 | ||
@@ -1626,6 +1650,7 @@ static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, u | |||
1626 | } | 1650 | } |
1627 | /* Allow only deep userdata entities to be copied across | 1651 | /* Allow only deep userdata entities to be copied across |
1628 | */ | 1652 | */ |
1653 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "USERDATA\n" INDENT_END)); | ||
1629 | if( !luaG_copydeep( L, L2, i)) | 1654 | if( !luaG_copydeep( L, L2, i)) |
1630 | { | 1655 | { |
1631 | // Cannot copy it full; copy as light userdata | 1656 | // Cannot copy it full; copy as light userdata |
@@ -1669,7 +1694,6 @@ static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, u | |||
1669 | #endif | 1694 | #endif |
1670 | STACK_CHECK( L2); | 1695 | STACK_CHECK( L2); |
1671 | push_cached_func( L2, L2_cache_i, L, i); | 1696 | push_cached_func( L2, L2_cache_i, L, i); |
1672 | ASSERT_L( lua_isfunction( L2, -1)); | ||
1673 | STACK_END( L2, 1); | 1697 | STACK_END( L2, 1); |
1674 | } | 1698 | } |
1675 | break; | 1699 | break; |
@@ -1827,7 +1851,7 @@ static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, u | |||
1827 | * | 1851 | * |
1828 | * Note: Parameters are in this order ('L' = from first) to be same as 'lua_xmove'. | 1852 | * Note: Parameters are in this order ('L' = from first) to be same as 'lua_xmove'. |
1829 | */ | 1853 | */ |
1830 | int luaG_inter_copy( lua_State* L, lua_State *L2, uint_t n) | 1854 | int luaG_inter_copy( lua_State* L, lua_State* L2, uint_t n) |
1831 | { | 1855 | { |
1832 | uint_t top_L = lua_gettop( L); | 1856 | uint_t top_L = lua_gettop( L); |
1833 | uint_t top_L2 = lua_gettop( L2); | 1857 | uint_t top_L2 = lua_gettop( L2); |
@@ -1903,7 +1927,9 @@ void luaG_inter_copy_package( lua_State* L, lua_State* L2, int _idx) | |||
1903 | { | 1927 | { |
1904 | int i; | 1928 | int i; |
1905 | // package.loaders is renamed package.searchers in Lua 5.2 | 1929 | // package.loaders is renamed package.searchers in Lua 5.2 |
1906 | char const* entries[] = { "path", "cpath", "preload", (LUA_VERSION_NUM == 501) ? "loaders" : "searchers", NULL}; | 1930 | // but don't copy it anyway, as the function names change depending on the slot index! |
1931 | // users should provide an on_state_create function to setup custom loaders instead | ||
1932 | char const* entries[] = { "path", "cpath", "preload"/*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, NULL}; | ||
1907 | for( i = 0; entries[i]; ++ i) | 1933 | for( i = 0; entries[i]; ++ i) |
1908 | { | 1934 | { |
1909 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s\n" INDENT_END, entries[i])); | 1935 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s\n" INDENT_END, entries[i])); |
@@ -1944,7 +1970,7 @@ MUTEX_T require_cs; | |||
1944 | // | 1970 | // |
1945 | // Upvalues: [1]: original 'require' function | 1971 | // Upvalues: [1]: original 'require' function |
1946 | // | 1972 | // |
1947 | static int new_require( lua_State *L) | 1973 | int luaG_new_require( lua_State* L) |
1948 | { | 1974 | { |
1949 | int rc, i; | 1975 | int rc, i; |
1950 | int args = lua_gettop( L); | 1976 | int args = lua_gettop( L); |
@@ -1955,24 +1981,24 @@ static int new_require( lua_State *L) | |||
1955 | 1981 | ||
1956 | lua_pushvalue( L, lua_upvalueindex(1)); | 1982 | lua_pushvalue( L, lua_upvalueindex(1)); |
1957 | for( i = 1; i <= args; ++ i) | 1983 | for( i = 1; i <= args; ++ i) |
1984 | { | ||
1958 | lua_pushvalue( L, i); | 1985 | lua_pushvalue( L, i); |
1986 | } | ||
1959 | 1987 | ||
1960 | // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would | 1988 | // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would |
1961 | // leave us locked, blocking any future 'require' calls from other lanes. | 1989 | // leave us locked, blocking any future 'require' calls from other lanes. |
1962 | // | 1990 | // |
1963 | MUTEX_LOCK( &require_cs); | 1991 | MUTEX_LOCK( &require_cs); |
1964 | { | 1992 | rc = lua_pcall( L, args, 1 /*retvals*/, 0 /*errfunc*/ ); |
1965 | rc = lua_pcall( L, args, 1 /*retvals*/, 0 /*errfunc*/ ); | ||
1966 | // | ||
1967 | // LUA_ERRRUN / LUA_ERRMEM | ||
1968 | } | ||
1969 | MUTEX_UNLOCK( &require_cs); | 1993 | MUTEX_UNLOCK( &require_cs); |
1970 | 1994 | ||
1971 | // the required module (or an error message) is left on the stack as returned value by original require function | 1995 | // the required module (or an error message) is left on the stack as returned value by original require function |
1972 | STACK_END( L, 1); | 1996 | STACK_END( L, 1); |
1973 | 1997 | ||
1974 | if (rc) | 1998 | if( rc != LUA_OK) // LUA_ERRRUN / LUA_ERRMEM ? |
1975 | lua_error(L); // error message already at [-1] | 1999 | { |
2000 | return lua_error( L); // error message already at [-1] | ||
2001 | } | ||
1976 | 2002 | ||
1977 | return 1; | 2003 | return 1; |
1978 | } | 2004 | } |
@@ -1980,7 +2006,7 @@ static int new_require( lua_State *L) | |||
1980 | /* | 2006 | /* |
1981 | * Serialize calls to 'require', if it exists | 2007 | * Serialize calls to 'require', if it exists |
1982 | */ | 2008 | */ |
1983 | void serialize_require( lua_State *L ) | 2009 | void serialize_require( lua_State* L) |
1984 | { | 2010 | { |
1985 | STACK_GROW( L, 1); | 2011 | STACK_GROW( L, 1); |
1986 | STACK_CHECK( L); | 2012 | STACK_CHECK( L); |
@@ -1991,7 +2017,7 @@ void serialize_require( lua_State *L ) | |||
1991 | if( lua_isfunction( L, -1)) | 2017 | if( lua_isfunction( L, -1)) |
1992 | { | 2018 | { |
1993 | // [-1]: original 'require' function | 2019 | // [-1]: original 'require' function |
1994 | lua_pushcclosure( L, new_require, 1 /*upvalues*/); | 2020 | lua_pushcclosure( L, luaG_new_require, 1 /*upvalues*/); |
1995 | lua_setglobal( L, "require"); | 2021 | lua_setglobal( L, "require"); |
1996 | } | 2022 | } |
1997 | else | 2023 | else |
diff --git a/src/tools.h b/src/tools.h index f79d2ad..a06e23f 100644 --- a/src/tools.h +++ b/src/tools.h | |||
@@ -78,11 +78,9 @@ extern int debugspew_indent_depth; | |||
78 | #define luaG_optunsigned(L,i,d) ((uint_t) luaL_optinteger(L,i,d)) | 78 | #define luaG_optunsigned(L,i,d) ((uint_t) luaL_optinteger(L,i,d)) |
79 | #define luaG_tounsigned(L,i) ((uint_t) lua_tointeger(L,i)) | 79 | #define luaG_tounsigned(L,i) ((uint_t) lua_tointeger(L,i)) |
80 | 80 | ||
81 | #define luaG_isany(L,i) (!lua_isnil(L,i)) | ||
82 | |||
83 | void luaG_dump( lua_State* L ); | 81 | void luaG_dump( lua_State* L ); |
84 | 82 | ||
85 | lua_State* luaG_newstate( lua_State* _from, char const* libs, lua_CFunction _on_state_create); | 83 | lua_State* luaG_newstate( lua_State* _from, int const _on_state_create, char const* libs); |
86 | 84 | ||
87 | typedef struct { | 85 | typedef struct { |
88 | volatile int refcount; | 86 | volatile int refcount; |
@@ -96,6 +94,7 @@ int luaG_inter_copy( lua_State *L, lua_State *L2, uint_t n); | |||
96 | int luaG_inter_move( lua_State *L, lua_State *L2, uint_t n); | 94 | int luaG_inter_move( lua_State *L, lua_State *L2, uint_t n); |
97 | 95 | ||
98 | int luaG_nameof( lua_State* L); | 96 | int luaG_nameof( lua_State* L); |
97 | int luaG_new_require( lua_State* L); | ||
99 | 98 | ||
100 | // Lock for reference counter inc/dec locks (to be initialized by outside code) | 99 | // Lock for reference counter inc/dec locks (to be initialized by outside code) |
101 | // | 100 | // |
diff --git a/tests/fibonacci.lua b/tests/fibonacci.lua index 48cd8d7..a5e0b2a 100644 --- a/tests/fibonacci.lua +++ b/tests/fibonacci.lua | |||
@@ -28,6 +28,7 @@ local KNOWN= { [0]=0, 1,1,2,3,5,8,13,21,34,55,89,144 } | |||
28 | -- uint= fib( n_uint ) | 28 | -- uint= fib( n_uint ) |
29 | -- | 29 | -- |
30 | local function fib( n ) | 30 | local function fib( n ) |
31 | --local lanes = require"lanes".configure() | ||
31 | -- | 32 | -- |
32 | local sum | 33 | local sum |
33 | local floor= assert(math.floor) | 34 | local floor= assert(math.floor) |
@@ -39,9 +40,11 @@ local function fib( n ) | |||
39 | else | 40 | else |
40 | -- Splits into two; this task remains waiting for the results | 41 | -- Splits into two; this task remains waiting for the results |
41 | -- | 42 | -- |
42 | -- note that lanes is pulled in as upvalue, so we need package library to require internals properly | 43 | -- note that lanes is pulled in by upvalue, so we need lanes.core to be available |
43 | -- (because lua51-lanes is always required internally if possible, which is necessary in that case) | 44 | -- the other solution is to require "lanes" from inside the lane body, as in: |
44 | local gen_f= lanes.gen( "*", fib ) | 45 | -- local lanes = require"lanes".configure() |
46 | -- local gen_f= lanes.gen( "*", fib) | ||
47 | local gen_f= lanes.gen( "*", {required={"lanes.core"}}, fib) | ||
45 | 48 | ||
46 | local n1=floor(n/2) +1 | 49 | local n1=floor(n/2) +1 |
47 | local n2=floor(n/2) -1 + n%2 | 50 | local n2=floor(n/2) -1 + n%2 |
diff --git a/tests/package.lua b/tests/package.lua new file mode 100644 index 0000000..7c72d35 --- /dev/null +++ b/tests/package.lua | |||
@@ -0,0 +1,20 @@ | |||
1 | assert(nil == package.loaders[5]) | ||
2 | |||
3 | local configure_loaders = function() | ||
4 | table.insert(package.loaders, 4, function() end) | ||
5 | assert(package.loaders[1]) | ||
6 | assert(package.loaders[2]) | ||
7 | assert(package.loaders[3]) | ||
8 | assert(package.loaders[4]) | ||
9 | assert(package.loaders[5]) | ||
10 | print "loaders configured!" | ||
11 | end | ||
12 | |||
13 | configure_loaders() | ||
14 | |||
15 | for k,v in pairs(package.loaders) do | ||
16 | print( k, type(v)) | ||
17 | end | ||
18 | |||
19 | lanes = require "lanes" | ||
20 | lanes.configure{with_timers=false, on_state_create = configure_loaders} \ No newline at end of file | ||
diff --git a/tests/pingpong.lua b/tests/pingpong.lua new file mode 100644 index 0000000..30cd360 --- /dev/null +++ b/tests/pingpong.lua | |||
@@ -0,0 +1,31 @@ | |||
1 | local lanes = require 'lanes'.configure() | ||
2 | local q = lanes.linda() | ||
3 | |||
4 | local pingpong = function(name, qr, qs, start) | ||
5 | print("start " .. name, qr, qs, start) | ||
6 | local count = 0 | ||
7 | if start then | ||
8 | print(name .. ": sending " .. qs .. " 0") | ||
9 | q:send(qs, 0) | ||
10 | end | ||
11 | while count < 10 do | ||
12 | print(name .. ": receiving " .. qr) | ||
13 | local key, val = q:receive(qr) | ||
14 | if val == nil then | ||
15 | print(name .. ": timeout") | ||
16 | break | ||
17 | end | ||
18 | print(name .. ":" .. val) | ||
19 | val = val + 1 | ||
20 | print(name .. ": sending " .. qs .. " " .. tostring(val + 1)) | ||
21 | q:send(qs, val) | ||
22 | count = count + 1 | ||
23 | end | ||
24 | end | ||
25 | |||
26 | -- pingpong("L1", '0', '1', true) | ||
27 | local t1, err1 = lanes.gen("*", pingpong)("L1", 'a', 'b', true) | ||
28 | local t2, err2 = lanes.gen("*", pingpong)("L2", 'b', 'a', false) | ||
29 | |||
30 | t1:join() | ||
31 | t2:join() \ No newline at end of file | ||
diff --git a/tests/timer.lua b/tests/timer.lua index 953e4ed..805d85c 100644 --- a/tests/timer.lua +++ b/tests/timer.lua | |||
@@ -8,8 +8,7 @@ | |||
8 | io.stderr:setvbuf "no" | 8 | io.stderr:setvbuf "no" |
9 | 9 | ||
10 | 10 | ||
11 | local lanes = require "lanes" | 11 | local lanes = require "lanes".configure() |
12 | lanes.configure() | ||
13 | 12 | ||
14 | local linda= lanes.linda() | 13 | local linda= lanes.linda() |
15 | 14 | ||
@@ -101,3 +100,5 @@ PRINT "...making sure no ticks are coming..." | |||
101 | local k,v= linda:receive( 10, T1,T2 ) -- should not get any | 100 | local k,v= linda:receive( 10, T1,T2 ) -- should not get any |
102 | assert(v==nil) | 101 | assert(v==nil) |
103 | 102 | ||
103 | lanes.timer_lane:cancel() | ||
104 | print (lanes.timer_lane[1], lanes.timer_lane[2]) \ No newline at end of file | ||