diff options
author | Benoit Germain <bnt period germain arrobase gmail period com> | 2017-06-05 11:25:31 +0200 |
---|---|---|
committer | Benoit Germain <bnt period germain arrobase gmail period com> | 2017-06-05 11:25:31 +0200 |
commit | c2ca1fce531e29f8209660b18ef0a493722813f1 (patch) | |
tree | 3afa59fb910b2aaee79cbccc9a58fc961b3b8868 | |
parent | 0ede50e2da00f2915ceb50184425c42bda83adfd (diff) | |
download | lanes-c2ca1fce531e29f8209660b18ef0a493722813f1.tar.gz lanes-c2ca1fce531e29f8209660b18ef0a493722813f1.tar.bz2 lanes-c2ca1fce531e29f8209660b18ef0a493722813f1.zip |
Table transfer improvements
* new API function lanes.register( "name", module) to manually register
a module table after it was required
* Transfering registered module tables will link the equivalent in the
destination state instead of cloning it
* bumped version to 3.11
-rw-r--r-- | CHANGES | 5 | ||||
-rw-r--r-- | docs/index.html | 7 | ||||
-rw-r--r-- | src/lanes.c | 42 | ||||
-rw-r--r-- | src/lanes.lua | 18 | ||||
-rw-r--r-- | src/tools.c | 318 | ||||
-rw-r--r-- | tests/parallel_os_calls.lua | 13 |
6 files changed, 286 insertions, 117 deletions
@@ -1,5 +1,10 @@ | |||
1 | CHANGES: | 1 | CHANGES: |
2 | 2 | ||
3 | CHANGE 120: BGe 5-Jun-17 | ||
4 | * new API function lanes.register( "name", module) to manually register a module table after it was required | ||
5 | * Transfering registered module tables will link the equivalent in the destination state instead of cloning it | ||
6 | * bumped version to 3.11 | ||
7 | |||
3 | CHANGE 119: BGe 10-May-17 | 8 | CHANGE 119: BGe 10-May-17 |
4 | * Fixed some compilation warnings | 9 | * Fixed some compilation warnings |
5 | * Improved LuaJIT support | 10 | * Improved LuaJIT support |
diff --git a/docs/index.html b/docs/index.html index 154355d..1eb1d71 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -64,13 +64,13 @@ | |||
64 | <font size="-1"> | 64 | <font size="-1"> |
65 | <p> | 65 | <p> |
66 | <br/> | 66 | <br/> |
67 | <i>Copyright © 2007-16 Asko Kauppi, Benoit Germain. All rights reserved.</i> | 67 | <i>Copyright © 2007-17 Asko Kauppi, Benoit Germain. All rights reserved.</i> |
68 | <br/> | 68 | <br/> |
69 | Lua Lanes is published under the same <a href="http://en.wikipedia.org/wiki/MIT_License">MIT license</a> as Lua 5.1, 5.2, and 5.3. | 69 | Lua Lanes is published under the same <a href="http://en.wikipedia.org/wiki/MIT_License">MIT license</a> as Lua 5.1, 5.2, and 5.3. |
70 | </p> | 70 | </p> |
71 | 71 | ||
72 | <p> | 72 | <p> |
73 | This document was revised on 21-Nov-16, and applies to version <tt>3.10.1</tt>. | 73 | This document was revised on 5-Jun-17, and applies to version <tt>3.11</tt>. |
74 | </p> | 74 | </p> |
75 | </font> | 75 | </font> |
76 | </center> | 76 | </center> |
@@ -396,12 +396,15 @@ | |||
396 | (Since v3.5.0) 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>. | 396 | (Since v3.5.0) 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>. |
397 | <br/> | 397 | <br/> |
398 | Use <tt>lanes.require()</tt> for this purpose. This will call the original <tt>require()</tt>, then add the result to the lookup databases. | 398 | Use <tt>lanes.require()</tt> for this purpose. This will call the original <tt>require()</tt>, then add the result to the lookup databases. |
399 | <br/> | ||
400 | (Since version 3.11) It is also possible to register a given module with <tt>lanes.register()</tt>. This function will raise an error if the registered module is not a function or table. | ||
399 | </p> | 401 | </p> |
400 | 402 | ||
401 | <table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"> | 403 | <table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"> |
402 | <tr> | 404 | <tr> |
403 | <td> | 405 | <td> |
404 | <pre> local m = lanes.require "modname"</pre> | 406 | <pre> local m = lanes.require "modname"</pre> |
407 | <pre> lanes.register( "modname", module)</pre> | ||
405 | </td> | 408 | </td> |
406 | </tr> | 409 | </tr> |
407 | </table> | 410 | </table> |
diff --git a/src/lanes.c b/src/lanes.c index d0ffee1..8410ca2 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * LANES.C Copyright (c) 2007-08, Asko Kauppi | 2 | * LANES.C Copyright (c) 2007-08, Asko Kauppi |
3 | * Copyright (C) 2009-14, Benoit Germain | 3 | * Copyright (C) 2009-17, Benoit Germain |
4 | * | 4 | * |
5 | * Multithreading in Lua. | 5 | * Multithreading in Lua. |
6 | * | 6 | * |
@@ -52,13 +52,13 @@ | |||
52 | * ... | 52 | * ... |
53 | */ | 53 | */ |
54 | 54 | ||
55 | char const* VERSION = "3.10.1"; | 55 | char const* VERSION = "3.11"; |
56 | 56 | ||
57 | /* | 57 | /* |
58 | =============================================================================== | 58 | =============================================================================== |
59 | 59 | ||
60 | Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com> | 60 | Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com> |
61 | 2011-14 Benoit Germain <bnt.germain@gmail.com> | 61 | 2011-17 Benoit Germain <bnt.germain@gmail.com> |
62 | 62 | ||
63 | Permission is hereby granted, free of charge, to any person obtaining a copy | 63 | Permission is hereby granted, free of charge, to any person obtaining a copy |
64 | of this software and associated documentation files (the "Software"), to deal | 64 | of this software and associated documentation files (the "Software"), to deal |
@@ -162,7 +162,8 @@ struct s_lane | |||
162 | // lane status changes to DONE/ERROR_ST/CANCELLED. | 162 | // lane status changes to DONE/ERROR_ST/CANCELLED. |
163 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | 163 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
164 | 164 | ||
165 | volatile enum { | 165 | volatile enum |
166 | { | ||
166 | NORMAL, // normal master side state | 167 | NORMAL, // normal master side state |
167 | KILLED // issued an OS kill | 168 | KILLED // issued an OS kill |
168 | } mstatus; | 169 | } mstatus; |
@@ -307,7 +308,7 @@ static bool_t push_registry_table( lua_State* L, void* key, bool_t create) | |||
307 | return FALSE; | 308 | return FALSE; |
308 | } | 309 | } |
309 | 310 | ||
310 | lua_newtable(L); // t | 311 | lua_newtable( L); // t |
311 | lua_pushlightuserdata( L, key); // t key | 312 | lua_pushlightuserdata( L, key); // t key |
312 | lua_pushvalue( L, -2); // t key t | 313 | lua_pushvalue( L, -2); // t key t |
313 | lua_rawset( L, LUA_REGISTRYINDEX); // t | 314 | lua_rawset( L, LUA_REGISTRYINDEX); // t |
@@ -351,7 +352,7 @@ static bool_t tracking_remove( struct s_lane* s) | |||
351 | // still (at process exit they will remove us from chain and then | 352 | // still (at process exit they will remove us from chain and then |
352 | // cancel/kill). | 353 | // cancel/kill). |
353 | // | 354 | // |
354 | if (s->tracking_next != NULL) | 355 | if( s->tracking_next != NULL) |
355 | { | 356 | { |
356 | struct s_lane** ref = (struct s_lane**) &s->U->tracking_first; | 357 | struct s_lane** ref = (struct s_lane**) &s->U->tracking_first; |
357 | 358 | ||
@@ -539,7 +540,7 @@ LUAG_FUNC( linda_send) | |||
539 | break; | 540 | break; |
540 | } | 541 | } |
541 | 542 | ||
542 | // instant timout to bypass the | 543 | // instant timout to bypass the wait syscall |
543 | if( timeout == 0.0) | 544 | if( timeout == 0.0) |
544 | { | 545 | { |
545 | break; /* no wait; instant timeout */ | 546 | break; /* no wait; instant timeout */ |
@@ -2157,6 +2158,30 @@ LUAG_FUNC( require) | |||
2157 | return 1; | 2158 | return 1; |
2158 | } | 2159 | } |
2159 | 2160 | ||
2161 | |||
2162 | // --- If a client wants to transfer stuff of a previously required module from the current state to another Lane, the module must be registered | ||
2163 | // to populate the lookup database in the source lane (and in the destination too, of course) | ||
2164 | // lanes.register( "modname", module) | ||
2165 | LUAG_FUNC( register) | ||
2166 | { | ||
2167 | char const* name = luaL_checkstring( L, 1); | ||
2168 | int const nargs = lua_gettop( L); | ||
2169 | int const mod_type = lua_type( L, 2); | ||
2170 | // ignore extra parameters, just in case | ||
2171 | lua_settop( L, 2); | ||
2172 | luaL_argcheck( L, (mod_type == LUA_TTABLE) || (mod_type == LUA_TFUNCTION), 2, "unexpected module type"); | ||
2173 | DEBUGSPEW_CODE( struct s_Universe* U = get_universe( L)); | ||
2174 | STACK_CHECK( L); // "name" mod_table | ||
2175 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.register %s BEGIN\n" INDENT_END, name)); | ||
2176 | DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); | ||
2177 | populate_func_lookup_table( L, -1, name); | ||
2178 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.register %s END\n" INDENT_END, name)); | ||
2179 | DEBUGSPEW_CODE( -- U->debugspew_indent_depth); | ||
2180 | STACK_END( L, 0); | ||
2181 | return 0; | ||
2182 | } | ||
2183 | |||
2184 | |||
2160 | LUAG_FUNC( thread_gc); | 2185 | LUAG_FUNC( thread_gc); |
2161 | #define GCCB_KEY (void*)LG_thread_gc | 2186 | #define GCCB_KEY (void*)LG_thread_gc |
2162 | //--- | 2187 | //--- |
@@ -2945,6 +2970,7 @@ static const struct luaL_Reg lanes_functions [] = { | |||
2945 | {"wakeup_conv", LG_wakeup_conv}, | 2970 | {"wakeup_conv", LG_wakeup_conv}, |
2946 | {"set_thread_priority", LG_set_thread_priority}, | 2971 | {"set_thread_priority", LG_set_thread_priority}, |
2947 | {"nameof", luaG_nameof}, | 2972 | {"nameof", luaG_nameof}, |
2973 | {"register", LG_register}, | ||
2948 | {"set_singlethreaded", LG_set_singlethreaded}, | 2974 | {"set_singlethreaded", LG_set_singlethreaded}, |
2949 | {NULL, NULL} | 2975 | {NULL, NULL} |
2950 | }; | 2976 | }; |
@@ -3201,7 +3227,7 @@ LUAG_FUNC( configure) | |||
3201 | { | 3227 | { |
3202 | // don't do this when called during the initialization of a new lane, | 3228 | // don't do this when called during the initialization of a new lane, |
3203 | // because we will do it after on_state_create() is called, | 3229 | // because we will do it after on_state_create() is called, |
3204 | // and we don't want skip _G because of caching in case globals are created then | 3230 | // and we don't want to skip _G because of caching in case globals are created then |
3205 | lua_pushglobaltable( L); // settings M _G | 3231 | lua_pushglobaltable( L); // settings M _G |
3206 | populate_func_lookup_table( L, -1, NULL); | 3232 | populate_func_lookup_table( L, -1, NULL); |
3207 | lua_pop( L, 1); // settings M | 3233 | lua_pop( L, 1); // settings M |
diff --git a/src/lanes.lua b/src/lanes.lua index affbd7d..35071ae 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
@@ -40,9 +40,8 @@ local core = require "lanes.core" | |||
40 | -- Lua 5.2: module() is gone | 40 | -- Lua 5.2: module() is gone |
41 | -- almost everything module() does is done by require() anyway | 41 | -- almost everything module() does is done by require() anyway |
42 | -- -> simply create a table, populate it, return it, and be done | 42 | -- -> simply create a table, populate it, return it, and be done |
43 | local lanes = {} | ||
44 | local lanesMeta = {} | 43 | local lanesMeta = {} |
45 | setmetatable(lanes,lanesMeta) | 44 | local lanes = setmetatable( {}, lanesMeta) |
46 | 45 | ||
47 | -- this function is available in the public interface until it is called, after which it disappears | 46 | -- this function is available in the public interface until it is called, after which it disappears |
48 | lanes.configure = function( settings_) | 47 | lanes.configure = function( settings_) |
@@ -55,7 +54,7 @@ lanes.configure = function( settings_) | |||
55 | error( "To use 'lanes', you will also need to have 'string' available.", 2) | 54 | error( "To use 'lanes', you will also need to have 'string' available.", 2) |
56 | end | 55 | end |
57 | -- Configure called so remove metatable from lanes | 56 | -- Configure called so remove metatable from lanes |
58 | setmetatable(lanes,nil) | 57 | setmetatable( lanes, nil) |
59 | -- | 58 | -- |
60 | -- Cache globals for code that might run under sandboxing | 59 | -- Cache globals for code that might run under sandboxing |
61 | -- | 60 | -- |
@@ -139,7 +138,7 @@ lanes.configure = function( settings_) | |||
139 | author= "Asko Kauppi <akauppi@gmail.com>, Benoit Germain <bnt.germain@gmail.com>", | 138 | author= "Asko Kauppi <akauppi@gmail.com>, Benoit Germain <bnt.germain@gmail.com>", |
140 | description= "Running multiple Lua states in parallel", | 139 | description= "Running multiple Lua states in parallel", |
141 | license= "MIT/X11", | 140 | license= "MIT/X11", |
142 | copyright= "Copyright (c) 2007-10, Asko Kauppi; (c) 2011-13, Benoit Germain", | 141 | copyright= "Copyright (c) 2007-10, Asko Kauppi; (c) 2011-17, Benoit Germain", |
143 | version = assert( core.version) | 142 | version = assert( core.version) |
144 | } | 143 | } |
145 | 144 | ||
@@ -716,6 +715,7 @@ lanes.configure = function( settings_) | |||
716 | 715 | ||
717 | -- activate full interface | 716 | -- activate full interface |
718 | lanes.require = core.require | 717 | lanes.require = core.require |
718 | lanes.register = core.register | ||
719 | lanes.gen = gen | 719 | lanes.gen = gen |
720 | lanes.linda = core.linda | 720 | lanes.linda = core.linda |
721 | lanes.cancel_error = core.cancel_error | 721 | lanes.cancel_error = core.cancel_error |
@@ -734,20 +734,18 @@ lanes.configure = function( settings_) | |||
734 | return lanes | 734 | return lanes |
735 | end -- lanes.configure | 735 | end -- lanes.configure |
736 | 736 | ||
737 | lanesMeta.__index = function(t,k) | 737 | lanesMeta.__index = function( t, k) |
738 | -- This is called when some functionality is accessed without calling configure() | 738 | -- This is called when some functionality is accessed without calling configure() |
739 | lanes.configure() -- Initialize with default settings | 739 | lanes.configure() -- initialize with default settings |
740 | -- Access the required key | 740 | -- Access the required key |
741 | return lanes[k] | 741 | return lanes[k] |
742 | end | 742 | end |
743 | 743 | ||
744 | -- no need to force calling configure() excepted the first time | 744 | -- no need to force calling configure() manually excepted the first time (other times will reuse the internally stored settings of the first call) |
745 | if core.settings then | 745 | if core.settings then |
746 | return lanes.configure() | 746 | return lanes.configure() |
747 | else | 747 | else |
748 | return lanes | 748 | return lanes |
749 | end | 749 | end |
750 | 750 | ||
751 | 751 | --the end | |
752 | --the end (Unreachable Code) | ||
753 | --return lanes | ||
diff --git a/src/tools.c b/src/tools.c index d97b231..a4d781f 100644 --- a/src/tools.c +++ b/src/tools.c | |||
@@ -85,7 +85,7 @@ void luaG_dump( lua_State* L) | |||
85 | { | 85 | { |
86 | int type = lua_type( L, i); | 86 | int type = lua_type( L, i); |
87 | 87 | ||
88 | fprintf( stderr, "\t[%d]= (%s) ", i, lua_typename(L, type)); | 88 | fprintf( stderr, "\t[%d]= (%s) ", i, lua_typename( L, type)); |
89 | 89 | ||
90 | // Print item contents here... | 90 | // Print item contents here... |
91 | // | 91 | // |
@@ -99,7 +99,7 @@ void luaG_dump( lua_State* L) | |||
99 | // | 99 | // |
100 | // [-1]: tostring function, or nil | 100 | // [-1]: tostring function, or nil |
101 | 101 | ||
102 | if( !lua_isfunction(L, -1)) | 102 | if( !lua_isfunction( L, -1)) |
103 | { | 103 | { |
104 | fprintf( stderr, "('tostring' not available)"); | 104 | fprintf( stderr, "('tostring' not available)"); |
105 | } | 105 | } |
@@ -110,7 +110,7 @@ void luaG_dump( lua_State* L) | |||
110 | 110 | ||
111 | // Don't trust the string contents | 111 | // Don't trust the string contents |
112 | // | 112 | // |
113 | fprintf( stderr, "%s", lua_tostring(L, -1)); | 113 | fprintf( stderr, "%s", lua_tostring( L, -1)); |
114 | } | 114 | } |
115 | lua_pop( L, 1); | 115 | lua_pop( L, 1); |
116 | STACK_END( L, 0); | 116 | STACK_END( L, 0); |
@@ -258,7 +258,8 @@ static void open1lib( struct s_Universe* U, lua_State* L, char const* name_, siz | |||
258 | } | 258 | } |
259 | } | 259 | } |
260 | 260 | ||
261 | static int dummy_writer(lua_State *L, const void* p, size_t sz, void* ud) | 261 | |
262 | static int dummy_writer( lua_State* L, void const* p, size_t sz, void* ud) | ||
262 | { | 263 | { |
263 | (void)L; (void)p; (void)sz; (void) ud; // unused | 264 | (void)L; (void)p; (void)sz; (void) ud; // unused |
264 | return 666; | 265 | return 666; |
@@ -326,12 +327,12 @@ static lua_CFunction luaG_tocfunction( lua_State *L, int _i, FuncSubType *_out) | |||
326 | 327 | ||
327 | 328 | ||
328 | // inspired from tconcat() in ltablib.c | 329 | // inspired from tconcat() in ltablib.c |
329 | static char const* luaG_pushFQN(lua_State *L, int t, int last, size_t* length) | 330 | static char const* luaG_pushFQN( lua_State* L, int t, int last, size_t* length) |
330 | { | 331 | { |
331 | int i = 1; | 332 | int i = 1; |
332 | luaL_Buffer b; | 333 | luaL_Buffer b; |
333 | STACK_CHECK( L); | 334 | STACK_CHECK( L); |
334 | luaL_buffinit(L, &b); | 335 | luaL_buffinit( L, &b); |
335 | for( ; i < last; ++ i) | 336 | for( ; i < last; ++ i) |
336 | { | 337 | { |
337 | lua_rawgeti( L, t, i); | 338 | lua_rawgeti( L, t, i); |
@@ -349,10 +350,12 @@ static char const* luaG_pushFQN(lua_State *L, int t, int last, size_t* length) | |||
349 | } | 350 | } |
350 | 351 | ||
351 | /* | 352 | /* |
353 | * receives 2 arguments: a name k and an object o | ||
352 | * add two entries ["fully.qualified.name"] = o | 354 | * add two entries ["fully.qualified.name"] = o |
353 | * and [o] = "fully.qualified.name" | 355 | * and [o] = "fully.qualified.name" |
354 | * where <o> is either a table or a function | 356 | * where <o> is either a table or a function |
355 | * if we already had an entry of type [o] = ..., replace the name if the new one is shorter | 357 | * if we already had an entry of type [o] = ..., replace the name if the new one is shorter |
358 | * pops the processed object from the stack | ||
356 | */ | 359 | */ |
357 | static void update_lookup_entry( lua_State* L, int _ctx_base, int _depth) | 360 | static void update_lookup_entry( lua_State* L, int _ctx_base, int _depth) |
358 | { | 361 | { |
@@ -364,12 +367,16 @@ static void update_lookup_entry( lua_State* L, int _ctx_base, int _depth) | |||
364 | size_t prevNameLength, newNameLength; | 367 | size_t prevNameLength, newNameLength; |
365 | char const* prevName; | 368 | char const* prevName; |
366 | DEBUGSPEW_CODE( char const *newName); | 369 | DEBUGSPEW_CODE( char const *newName); |
370 | DEBUGSPEW_CODE( struct s_Universe* U = get_universe( L)); | ||
371 | |||
372 | STACK_CHECK( L); | ||
367 | // first, raise an error if the function is already known | 373 | // first, raise an error if the function is already known |
368 | lua_pushvalue( L, -1); // ... {bfc} k o o | 374 | lua_pushvalue( L, -1); // ... {bfc} k o o |
369 | lua_rawget( L, dest); // ... {bfc} k o name? | 375 | lua_rawget( L, dest); // ... {bfc} k o name? |
370 | prevName = lua_tolstring( L, -1, &prevNameLength); // NULL if we got nil (first encounter of this object) | 376 | prevName = lua_tolstring( L, -1, &prevNameLength); // NULL if we got nil (first encounter of this object) |
371 | // push name in fqn stack (note that concatenation will crash if name is a not string or a number) | 377 | // push name in fqn stack (note that concatenation will crash if name is a not string or a number) |
372 | lua_pushvalue( L, -3); // ... {bfc} k o name? k | 378 | lua_pushvalue( L, -3); // ... {bfc} k o name? k |
379 | ASSERT_L( lua_type( L, -1) == LUA_TNUMBER || lua_type( L, -1) == LUA_TSTRING); | ||
373 | ++ _depth; | 380 | ++ _depth; |
374 | lua_rawseti( L, fqn, _depth); // ... {bfc} k o name? | 381 | lua_rawseti( L, fqn, _depth); // ... {bfc} k o name? |
375 | // generate name | 382 | // generate name |
@@ -385,7 +392,7 @@ static void update_lookup_entry( lua_State* L, int _ctx_base, int _depth) | |||
385 | // based on some sorting order so that we end up with the same name in all databases whatever order the table walk yielded | 392 | // based on some sorting order so that we end up with the same name in all databases whatever order the table walk yielded |
386 | if( prevName != NULL && (prevNameLength < newNameLength || lua_lessthan( L, -2, -1))) | 393 | if( prevName != NULL && (prevNameLength < newNameLength || lua_lessthan( L, -2, -1))) |
387 | { | 394 | { |
388 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s '%s' remained named '%s'\n" INDENT_END, lua_typename( L, -3), newName, prevName)); | 395 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s '%s' remained named '%s'\n" INDENT_END, lua_typename( L, lua_type( L, -3)), newName, prevName)); |
389 | // the previous name is 'smaller' than the one we just generated: keep it! | 396 | // the previous name is 'smaller' than the one we just generated: keep it! |
390 | lua_pop( L, 3); // ... {bfc} k | 397 | lua_pop( L, 3); // ... {bfc} k |
391 | } | 398 | } |
@@ -404,7 +411,7 @@ static void update_lookup_entry( lua_State* L, int _ctx_base, int _depth) | |||
404 | { | 411 | { |
405 | lua_remove( L, -2); // ... {bfc} k o "f.q.n" | 412 | lua_remove( L, -2); // ... {bfc} k o "f.q.n" |
406 | } | 413 | } |
407 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s '%s'\n" INDENT_END, lua_typename( L, -2), newName)); | 414 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s '%s'\n" INDENT_END, lua_typename( L, lua_type( L, -2)), newName)); |
408 | // prepare the stack for database feed | 415 | // prepare the stack for database feed |
409 | lua_pushvalue( L, -1); // ... {bfc} k o "f.q.n" "f.q.n" | 416 | lua_pushvalue( L, -1); // ... {bfc} k o "f.q.n" "f.q.n" |
410 | lua_pushvalue( L, -3); // ... {bfc} k o "f.q.n" "f.q.n" o | 417 | lua_pushvalue( L, -3); // ... {bfc} k o "f.q.n" "f.q.n" o |
@@ -419,6 +426,7 @@ static void update_lookup_entry( lua_State* L, int _ctx_base, int _depth) | |||
419 | lua_rawseti( L, fqn, _depth); // ... {bfc} k | 426 | lua_rawseti( L, fqn, _depth); // ... {bfc} k |
420 | } | 427 | } |
421 | -- _depth; | 428 | -- _depth; |
429 | STACK_END( L, -1); | ||
422 | } | 430 | } |
423 | 431 | ||
424 | static void populate_func_lookup_table_recur( lua_State* L, int _ctx_base, int _i, int _depth) | 432 | static void populate_func_lookup_table_recur( lua_State* L, int _ctx_base, int _i, int _depth) |
@@ -547,7 +555,7 @@ void populate_func_lookup_table( lua_State* L, int _i, char const* name_) | |||
547 | { | 555 | { |
548 | int const ctx_base = lua_gettop( L) + 1; | 556 | int const ctx_base = lua_gettop( L) + 1; |
549 | int const in_base = lua_absindex( L, _i); | 557 | int const in_base = lua_absindex( L, _i); |
550 | int const start_depth = name_ ? 1 : 0; | 558 | int start_depth = 0; |
551 | DEBUGSPEW_CODE( struct s_Universe* U = get_universe( L)); | 559 | DEBUGSPEW_CODE( struct s_Universe* U = get_universe( L)); |
552 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: populate_func_lookup_table('%s')\n" INDENT_END, L, name_ ? name_ : "NULL")); | 560 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: populate_func_lookup_table('%s')\n" INDENT_END, L, name_ ? name_ : "NULL")); |
553 | DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); | 561 | DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); |
@@ -571,9 +579,17 @@ void populate_func_lookup_table( lua_State* L, int _i, char const* name_) | |||
571 | lua_newtable( L); // {} {fqn} | 579 | lua_newtable( L); // {} {fqn} |
572 | if( name_) | 580 | if( name_) |
573 | { | 581 | { |
582 | STACK_MID( L, 2); | ||
574 | lua_pushstring( L, name_); // {} {fqn} "name" | 583 | lua_pushstring( L, name_); // {} {fqn} "name" |
584 | // generate a name, and if we already had one name, keep whichever is the shorter | ||
585 | lua_pushvalue( L, in_base); // {} {fqn} "name" t | ||
586 | update_lookup_entry( L, ctx_base, start_depth); // {} {fqn} "name" | ||
587 | // don't forget to store the name at the bottom of the fqn stack | ||
588 | ++ start_depth; | ||
575 | lua_rawseti( L, -2, start_depth); // {} {fqn} | 589 | lua_rawseti( L, -2, start_depth); // {} {fqn} |
590 | STACK_MID( L, 2); | ||
576 | } | 591 | } |
592 | // retrieve the cache, create it if we haven't done it yet | ||
577 | lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY_CACHE); // {} {fqn} {cache}? | 593 | lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY_CACHE); // {} {fqn} {cache}? |
578 | if( lua_isnil( L, -1)) | 594 | if( lua_isnil( L, -1)) |
579 | { | 595 | { |
@@ -582,6 +598,7 @@ void populate_func_lookup_table( lua_State* L, int _i, char const* name_) | |||
582 | lua_pushvalue( L, -1); // {} {fqn} {cache} {cache} | 598 | lua_pushvalue( L, -1); // {} {fqn} {cache} {cache} |
583 | lua_setfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY_CACHE); // {} {fqn} {cache} | 599 | lua_setfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY_CACHE); // {} {fqn} {cache} |
584 | } | 600 | } |
601 | // process everything we find in that table, filling in lookup data for all functions and tables we see there | ||
585 | populate_func_lookup_table_recur( L, ctx_base, in_base, start_depth); // {...} {fqn} {cache} | 602 | populate_func_lookup_table_recur( L, ctx_base, in_base, start_depth); // {...} {fqn} {cache} |
586 | lua_pop( L, 3); | 603 | lua_pop( L, 3); |
587 | } | 604 | } |
@@ -823,6 +840,158 @@ static int buf_writer( lua_State *L, const void* b, size_t n, void* B ) { | |||
823 | } | 840 | } |
824 | 841 | ||
825 | 842 | ||
843 | // function sentinel used to transfer native functions from/to keeper states | ||
844 | static int func_lookup_sentinel( lua_State* L) | ||
845 | { | ||
846 | return luaL_error( L, "function lookup sentinel for %s, should never be called", lua_tostring( L, lua_upvalueindex( 1))); | ||
847 | } | ||
848 | |||
849 | |||
850 | // function sentinel used to transfer native table from/to keeper states | ||
851 | static int table_lookup_sentinel( lua_State* L) | ||
852 | { | ||
853 | return luaL_error( L, "table lookup sentinel for %s, should never be called", lua_tostring( L, lua_upvalueindex(1))); | ||
854 | } | ||
855 | |||
856 | /* | ||
857 | * retrieve the name of a function/table in the lookup database | ||
858 | */ | ||
859 | static char const* find_lookup_name( lua_State* L, uint_t i, enum eLookupMode mode_, char const* upName_, size_t* len_) | ||
860 | { | ||
861 | DEBUGSPEW_CODE( struct s_Universe* const U = get_universe( L)); | ||
862 | char const* fqn; | ||
863 | ASSERT_L( lua_isfunction( L, i) || lua_istable( L, i)); // ... v ... | ||
864 | STACK_CHECK( L); | ||
865 | STACK_GROW( L, 3); // up to 3 slots are necessary on error | ||
866 | if( mode_ == eLM_FromKeeper) | ||
867 | { | ||
868 | lua_CFunction f = lua_tocfunction( L, i); // should *always* be func_lookup_sentinel or table_lookup_sentinel! | ||
869 | if( f == func_lookup_sentinel || f == table_lookup_sentinel) | ||
870 | { | ||
871 | lua_getupvalue( L, i, 1); // ... v ... "f.q.n" | ||
872 | } | ||
873 | else | ||
874 | { | ||
875 | // if this is not a sentinel, this is some user-created table we wanted to lookup | ||
876 | ASSERT_L( NULL == f && lua_istable( L, i)); | ||
877 | // push anything that will convert to NULL string | ||
878 | lua_pushnil( L); // ... v ... nil | ||
879 | } | ||
880 | } | ||
881 | else | ||
882 | { | ||
883 | // fetch the name from the source state's lookup table | ||
884 | lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_REGKEY); // ... v ... {} | ||
885 | ASSERT_L( lua_istable( L, -1)); | ||
886 | lua_pushvalue( L, i); // ... v ... {} v | ||
887 | lua_rawget( L, -2); // ... v ... {} "f.q.n" | ||
888 | } | ||
889 | fqn = lua_tolstring( L, -1, len_); | ||
890 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "function [C] %s \n" INDENT_END, fqn)); | ||
891 | // popping doesn't invalidate the pointer since this is an interned string gotten from the lookup database | ||
892 | lua_pop( L, (mode_ == eLM_FromKeeper) ? 1 : 2); // ... v ... | ||
893 | STACK_MID( L, 0); | ||
894 | if( NULL == fqn && !lua_istable( L, i)) // raise an error if we try to send an unknown function (but not for tables) | ||
895 | { | ||
896 | char const *from, *typewhat, *what, *gotchaA, *gotchaB; | ||
897 | // try to discover the name of the function we want to send | ||
898 | lua_getglobal( L, "decoda_name"); // ... v ... decoda_name | ||
899 | from = lua_tostring( L, -1); | ||
900 | lua_pushcfunction( L, luaG_nameof); // ... v ... decoda_name luaG_nameof | ||
901 | lua_pushvalue( L, i); // ... v ... decoda_name luaG_nameof t | ||
902 | lua_call( L, 1, 2); // ... v ... decoda_name "type" "name"|nil | ||
903 | typewhat = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : luaL_typename( L, -2); | ||
904 | // second return value can be nil if the table was not found | ||
905 | // probable reason: the function was removed from the source Lua state before Lanes was required. | ||
906 | if( lua_isnil( L, -1)) | ||
907 | { | ||
908 | gotchaA = " referenced by"; | ||
909 | gotchaB = "\n(did you remove it from the source Lua state before requiring Lanes?)"; | ||
910 | what = upName_; | ||
911 | } | ||
912 | else | ||
913 | { | ||
914 | gotchaA = ""; | ||
915 | gotchaB = ""; | ||
916 | what = (lua_type( L, -1) == LUA_TSTRING) ? lua_tostring( L, -1) : luaL_typename( L, -1); | ||
917 | } | ||
918 | (void) luaL_error( L, "%s%s '%s' not found in %s origin transfer database.%s", typewhat, gotchaA, what, from ? from : "main", gotchaB); | ||
919 | *len_ = 0; | ||
920 | return NULL; | ||
921 | } | ||
922 | STACK_END( L, 0); | ||
923 | return fqn; | ||
924 | } | ||
925 | |||
926 | |||
927 | /* | ||
928 | * Push a looked-up table, or nothing if we found nothing | ||
929 | */ | ||
930 | static bool_t lookup_table( lua_State* L2, lua_State* L, uint_t i, enum eLookupMode mode_, char const* upName_) | ||
931 | { | ||
932 | // get the name of the table we want to send | ||
933 | size_t len; | ||
934 | char const* fqn = find_lookup_name( L, i, mode_, upName_, &len); | ||
935 | if( NULL == fqn) // name not found, it is some user-created table | ||
936 | { | ||
937 | return FALSE; | ||
938 | } | ||
939 | // push the equivalent table in the destination's stack, retrieved from the lookup table | ||
940 | STACK_CHECK( L2); // L // L2 | ||
941 | STACK_GROW( L2, 3); // up to 3 slots are necessary on error | ||
942 | switch( mode_) | ||
943 | { | ||
944 | default: // shouldn't happen, in theory... | ||
945 | (void) luaL_error( L, "internal error: unknown lookup mode"); | ||
946 | return FALSE; | ||
947 | |||
948 | case eLM_ToKeeper: | ||
949 | // push a sentinel closure that holds the lookup name as upvalue | ||
950 | lua_pushlstring( L2, fqn, len); // "f.q.n" | ||
951 | lua_pushcclosure( L2, table_lookup_sentinel, 1); // f | ||
952 | break; | ||
953 | |||
954 | case eLM_LaneBody: | ||
955 | case eLM_FromKeeper: | ||
956 | lua_getfield( L2, LUA_REGISTRYINDEX, LOOKUP_REGKEY); // {} | ||
957 | ASSERT_L( lua_istable( L2, -1)); | ||
958 | lua_pushlstring( L2, fqn, len); // {} "f.q.n" | ||
959 | lua_rawget( L2, -2); // {} t | ||
960 | // we accept destination lookup failures in the case of transfering the Lanes body function (this will result in the source table being cloned instead) | ||
961 | // but not when we extract something out of a keeper, as there is nothing to clone! | ||
962 | if( lua_isnil( L2, -1) && mode_ == eLM_LaneBody) | ||
963 | { | ||
964 | lua_pop( L2, 2); // | ||
965 | STACK_MID( L2, 0); | ||
966 | return FALSE; | ||
967 | } | ||
968 | else if( !lua_istable( L2, -1)) | ||
969 | { | ||
970 | char const* from, *to; | ||
971 | lua_getglobal( L, "decoda_name"); // ... t ... decoda_name | ||
972 | from = lua_tostring( L, -1); | ||
973 | lua_pop( L, 1); // ... t ... | ||
974 | lua_getglobal( L2, "decoda_name"); // {} t decoda_name | ||
975 | to = lua_tostring( L2, -1); | ||
976 | lua_pop( L2, 1); // {} t | ||
977 | // when mode_ == eLM_FromKeeper, L is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error | ||
978 | (void) luaL_error( | ||
979 | (mode_ == eLM_FromKeeper) ? L2 : L | ||
980 | , "INTERNAL ERROR IN %s: table '%s' not found in %s destination transfer database." | ||
981 | , from ? from : "main" | ||
982 | , fqn | ||
983 | , to ? to : "main" | ||
984 | ); | ||
985 | return FALSE; | ||
986 | } | ||
987 | lua_remove( L2, -2); // t | ||
988 | break; | ||
989 | } | ||
990 | STACK_END( L2, 1); | ||
991 | return TRUE; | ||
992 | } | ||
993 | |||
994 | |||
826 | /* | 995 | /* |
827 | * Check if we've already copied the same table from 'L', and | 996 | * Check if we've already copied the same table from 'L', and |
828 | * reuse the old copy. This allows table upvalues shared by multiple | 997 | * reuse the old copy. This allows table upvalues shared by multiple |
@@ -847,7 +1016,7 @@ static bool_t push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L, | |||
847 | // push a light userdata uniquely representing the table | 1016 | // push a light userdata uniquely representing the table |
848 | lua_pushlightuserdata( L2, p); // ... p | 1017 | lua_pushlightuserdata( L2, p); // ... p |
849 | 1018 | ||
850 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); | 1019 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1)); |
851 | 1020 | ||
852 | lua_rawget( L2, L2_cache_i); // ... {cached|nil} | 1021 | lua_rawget( L2, L2_cache_i); // ... {cached|nil} |
853 | not_found_in_cache = lua_isnil( L2, -1); | 1022 | not_found_in_cache = lua_isnil( L2, -1); |
@@ -1002,6 +1171,7 @@ static int discover_object_name_recur( lua_State* L, int shortest_, int depth_) | |||
1002 | return shortest_; | 1171 | return shortest_; |
1003 | } | 1172 | } |
1004 | 1173 | ||
1174 | |||
1005 | /* | 1175 | /* |
1006 | * "type", "name" = lanes.nameof( o) | 1176 | * "type", "name" = lanes.nameof( o) |
1007 | */ | 1177 | */ |
@@ -1049,72 +1219,17 @@ int luaG_nameof( lua_State* L) | |||
1049 | return 2; | 1219 | return 2; |
1050 | } | 1220 | } |
1051 | 1221 | ||
1052 | // function sentinel used to transfer native functions from/to keeper states | ||
1053 | static int sentinelfunc( lua_State* L) | ||
1054 | { | ||
1055 | return luaL_error( L, "transfer sentinel function for %s, should never be called", lua_tostring( L, lua_upvalueindex( 1))); | ||
1056 | } | ||
1057 | 1222 | ||
1058 | /* | 1223 | /* |
1059 | * Push a looked-up native/LuaJIT function. | 1224 | * Push a looked-up native/LuaJIT function. |
1060 | */ | 1225 | */ |
1061 | static void lookup_native_func( struct s_Universe* U, lua_State* L2, lua_State* L, uint_t i, enum eLookupMode mode_, char const* upName_) | 1226 | static void lookup_native_func( lua_State* L2, lua_State* L, uint_t i, enum eLookupMode mode_, char const* upName_) |
1062 | { | 1227 | { |
1063 | char const* fqn; // L // L2 | 1228 | // get the name of the function we want to send |
1064 | size_t len; | 1229 | size_t len; |
1065 | ASSERT_L( lua_isfunction( L, i)); // ... f ... | 1230 | char const* fqn = find_lookup_name( L, i, mode_, upName_, &len); |
1066 | STACK_CHECK( L); | ||
1067 | STACK_GROW( L, 3); // up to 3 slots are necessary on error | ||
1068 | if( mode_ == eLM_FromKeeper) | ||
1069 | { | ||
1070 | lua_CFunction f = lua_tocfunction( L, i); // should *always* be sentinelfunc! | ||
1071 | ASSERT_L( f == sentinelfunc); | ||
1072 | lua_getupvalue( L, i, 1); // ... f ... "f.q.n" | ||
1073 | fqn = lua_tolstring( L, -1, &len); | ||
1074 | } | ||
1075 | else | ||
1076 | { | ||
1077 | // fetch the name from the source state's lookup table | ||
1078 | lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_REGKEY); // ... f ... {} | ||
1079 | ASSERT_L( lua_istable( L, -1)); | ||
1080 | lua_pushvalue( L, i); // ... f ... {} f | ||
1081 | lua_rawget( L, -2); // ... f ... {} "f.q.n" | ||
1082 | fqn = lua_tolstring( L, -1, &len); | ||
1083 | } | ||
1084 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "function [C] %s \n" INDENT_END, fqn)); | ||
1085 | // popping doesn't invalidate the pointer since this is an interned string gotten from the lookup database | ||
1086 | lua_pop( L, (mode_ == eLM_FromKeeper) ? 1 : 2); // ... f ... | ||
1087 | STACK_MID( L, 0); | ||
1088 | if( !fqn) | ||
1089 | { | ||
1090 | char const *from, *typewhat, *what, *gotchaA, *gotchaB; | ||
1091 | // try to discover the name of the function we want to send | ||
1092 | lua_getglobal( L, "decoda_name"); // ... f ... decoda_name | ||
1093 | from = lua_tostring( L, -1); | ||
1094 | lua_pushcfunction( L, luaG_nameof); // ... f ... decoda_name luaG_nameof | ||
1095 | lua_pushvalue( L, i); // ... f ... decoda_name luaG_nameof f | ||
1096 | lua_call( L, 1, 2); // ... f ... decoda_name "type" "name"|nil | ||
1097 | typewhat = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : luaL_typename( L, -2); | ||
1098 | // second return value can be nil if the function was not found | ||
1099 | // probable reason: the function was removed from the source Lua state before Lanes was required. | ||
1100 | if( lua_isnil( L, -1)) | ||
1101 | { | ||
1102 | gotchaA = " referenced by"; | ||
1103 | gotchaB = "\n(did you remove it from the source Lua state before requiring Lanes?)"; | ||
1104 | what = upName_; | ||
1105 | } | ||
1106 | else | ||
1107 | { | ||
1108 | gotchaA = ""; | ||
1109 | gotchaB = ""; | ||
1110 | what = (lua_type( L, -1) == LUA_TSTRING) ? lua_tostring( L, -1) : luaL_typename( L, -1); | ||
1111 | } | ||
1112 | (void) luaL_error( L, "%s%s '%s' not found in %s origin transfer database.%s", typewhat, gotchaA, what, from ? from : "main", gotchaB); | ||
1113 | return; | ||
1114 | } | ||
1115 | STACK_END( L, 0); | ||
1116 | // push the equivalent function in the destination's stack, retrieved from the lookup table | 1231 | // push the equivalent function in the destination's stack, retrieved from the lookup table |
1117 | STACK_CHECK( L2); | 1232 | STACK_CHECK( L2); // L // L2 |
1118 | STACK_GROW( L2, 3); // up to 3 slots are necessary on error | 1233 | STACK_GROW( L2, 3); // up to 3 slots are necessary on error |
1119 | switch( mode_) | 1234 | switch( mode_) |
1120 | { | 1235 | { |
@@ -1125,7 +1240,7 @@ static void lookup_native_func( struct s_Universe* U, lua_State* L2, lua_State* | |||
1125 | case eLM_ToKeeper: | 1240 | case eLM_ToKeeper: |
1126 | // push a sentinel closure that holds the lookup name as upvalue | 1241 | // push a sentinel closure that holds the lookup name as upvalue |
1127 | lua_pushlstring( L2, fqn, len); // "f.q.n" | 1242 | lua_pushlstring( L2, fqn, len); // "f.q.n" |
1128 | lua_pushcclosure( L2, sentinelfunc, 1); // f | 1243 | lua_pushcclosure( L2, func_lookup_sentinel, 1); // f |
1129 | break; | 1244 | break; |
1130 | 1245 | ||
1131 | case eLM_LaneBody: | 1246 | case eLM_LaneBody: |
@@ -1134,15 +1249,26 @@ static void lookup_native_func( struct s_Universe* U, lua_State* L2, lua_State* | |||
1134 | ASSERT_L( lua_istable( L2, -1)); | 1249 | ASSERT_L( lua_istable( L2, -1)); |
1135 | lua_pushlstring( L2, fqn, len); // {} "f.q.n" | 1250 | lua_pushlstring( L2, fqn, len); // {} "f.q.n" |
1136 | lua_rawget( L2, -2); // {} f | 1251 | lua_rawget( L2, -2); // {} f |
1137 | if( !lua_isfunction( L2, -1)) | 1252 | // nil means we don't know how to transfer stuff: user should do something |
1253 | // anything other than function or table should not happen! | ||
1254 | if( !lua_isfunction( L2, -1) && !lua_istable( L2, -1)) | ||
1138 | { | 1255 | { |
1139 | char const* from, * to; | 1256 | char const* from, * to; |
1140 | lua_getglobal( L, "decoda_name"); // ... f ... decoda_name | 1257 | lua_getglobal( L, "decoda_name"); // ... f ... decoda_name |
1141 | from = lua_tostring( L, -1); | 1258 | from = lua_tostring( L, -1); |
1259 | lua_pop( L, 1); // ... f ... | ||
1142 | lua_getglobal( L2, "decoda_name"); // {} f decoda_name | 1260 | lua_getglobal( L2, "decoda_name"); // {} f decoda_name |
1143 | to = lua_tostring( L2, -1); | 1261 | to = lua_tostring( L2, -1); |
1262 | lua_pop( L2, 1); // {} f | ||
1144 | // when mode_ == eLM_FromKeeper, L is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error | 1263 | // when mode_ == eLM_FromKeeper, L is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error |
1145 | (void) luaL_error( (mode_ == eLM_FromKeeper) ? L2 : L, "%s: function '%s' not found in %s destination transfer database.", from ? from : "main", fqn, to ? to : "main"); | 1264 | (void) luaL_error( |
1265 | (mode_ == eLM_FromKeeper) ? L2 : L | ||
1266 | , "%s%s: function '%s' not found in %s destination transfer database." | ||
1267 | , lua_isnil( L2, -1) ? "" : "INTERNAL ERROR IN " | ||
1268 | , from ? from : "main" | ||
1269 | , fqn | ||
1270 | , to ? to : "main" | ||
1271 | ); | ||
1146 | return; | 1272 | return; |
1147 | } | 1273 | } |
1148 | lua_remove( L2, -2); // f | 1274 | lua_remove( L2, -2); // f |
@@ -1167,6 +1293,7 @@ static void lookup_native_func( struct s_Universe* U, lua_State* L2, lua_State* | |||
1167 | STACK_END( L2, 1); | 1293 | STACK_END( L2, 1); |
1168 | } | 1294 | } |
1169 | 1295 | ||
1296 | |||
1170 | /* | 1297 | /* |
1171 | * Copy a function over, which has not been found in the cache. | 1298 | * Copy a function over, which has not been found in the cache. |
1172 | * L2 has the cache key for this function at the top of the stack | 1299 | * L2 has the cache key for this function at the top of the stack |
@@ -1184,7 +1311,7 @@ static void inter_copy_func( struct s_Universe* U, lua_State* L2, uint_t L2_cach | |||
1184 | int n, needToPush; | 1311 | int n, needToPush; |
1185 | luaL_Buffer b; | 1312 | luaL_Buffer b; |
1186 | ASSERT_L( L2_cache_i != 0); // ... {cache} ... p | 1313 | ASSERT_L( L2_cache_i != 0); // ... {cache} ... p |
1187 | STACK_GROW(L,2); | 1314 | STACK_GROW( L, 2); |
1188 | STACK_CHECK( L); | 1315 | STACK_CHECK( L); |
1189 | 1316 | ||
1190 | // 'lua_dump()' needs the function at top of stack | 1317 | // 'lua_dump()' needs the function at top of stack |
@@ -1226,7 +1353,7 @@ static void inter_copy_func( struct s_Universe* U, lua_State* L2, uint_t L2_cach | |||
1226 | lua_Debug ar; | 1353 | lua_Debug ar; |
1227 | lua_pushvalue( L, i); // ... b f | 1354 | lua_pushvalue( L, i); // ... b f |
1228 | // fills 'name' 'namewhat' and 'linedefined', pops function | 1355 | // fills 'name' 'namewhat' and 'linedefined', pops function |
1229 | lua_getinfo(L, ">nS", &ar); // ... b | 1356 | lua_getinfo( L, ">nS", &ar); // ... b |
1230 | name = ar.namewhat; | 1357 | name = ar.namewhat; |
1231 | fprintf( stderr, INDENT_BEGIN "FNAME: %s @ %d\n", i, s_indent, ar.short_src, ar.linedefined); // just gives NULL | 1358 | fprintf( stderr, INDENT_BEGIN "FNAME: %s @ %d\n", i, s_indent, ar.short_src, ar.linedefined); // just gives NULL |
1232 | } | 1359 | } |
@@ -1351,12 +1478,12 @@ static void push_cached_func( struct s_Universe* U, lua_State* L2, uint_t L2_cac | |||
1351 | // push a light userdata uniquely representing the function | 1478 | // push a light userdata uniquely representing the function |
1352 | lua_pushlightuserdata( L2, aspointer); // ... {cache} ... p | 1479 | lua_pushlightuserdata( L2, aspointer); // ... {cache} ... p |
1353 | 1480 | ||
1354 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); | 1481 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1)); |
1355 | 1482 | ||
1356 | lua_pushvalue( L2, -1); // ... {cache} ... p p | 1483 | lua_pushvalue( L2, -1); // ... {cache} ... p p |
1357 | lua_rawget( L2, L2_cache_i); // ... {cache} ... p function|nil|true | 1484 | lua_rawget( L2, L2_cache_i); // ... {cache} ... p function|nil|true |
1358 | 1485 | ||
1359 | if( lua_isnil(L2,-1)) // function is unknown | 1486 | if( lua_isnil( L2, -1)) // function is unknown |
1360 | { | 1487 | { |
1361 | lua_pop( L2, 1); // ... {cache} ... p | 1488 | lua_pop( L2, 1); // ... {cache} ... p |
1362 | 1489 | ||
@@ -1371,15 +1498,14 @@ static void push_cached_func( struct s_Universe* U, lua_State* L2, uint_t L2_cac | |||
1371 | lua_remove( L2, -2); // ... {cache} ... function | 1498 | lua_remove( L2, -2); // ... {cache} ... function |
1372 | } | 1499 | } |
1373 | STACK_END( L2, 1); | 1500 | STACK_END( L2, 1); |
1501 | ASSERT_L( lua_isfunction( L2, -1)); | ||
1374 | } | 1502 | } |
1375 | else // function is native/LuaJIT: no need to cache | 1503 | else // function is native/LuaJIT: no need to cache |
1376 | { | 1504 | { |
1377 | lookup_native_func( U, L2, L, i, mode_, upName_); // ... {cache} ... function | 1505 | lookup_native_func( L2, L, i, mode_, upName_); // ... {cache} ... function |
1506 | // if the function was in fact a lookup sentinel, we can either get a function or a table here | ||
1507 | ASSERT_L( lua_isfunction( L2, -1) || lua_istable( L2, -1)); | ||
1378 | } | 1508 | } |
1379 | |||
1380 | // | ||
1381 | // L2 [-1]: function | ||
1382 | ASSERT_L( lua_isfunction( L2, -1)); | ||
1383 | } | 1509 | } |
1384 | 1510 | ||
1385 | /* | 1511 | /* |
@@ -1396,12 +1522,12 @@ static bool_t inter_copy_one_( struct s_Universe* U, lua_State* L2, uint_t L2_ca | |||
1396 | { | 1522 | { |
1397 | bool_t ret = TRUE; | 1523 | bool_t ret = TRUE; |
1398 | bool_t ignore = FALSE; | 1524 | bool_t ignore = FALSE; |
1399 | int val_type = lua_type(L, i); | 1525 | int val_type = lua_type( L, i); |
1400 | STACK_GROW( L2, 1); | 1526 | STACK_GROW( L2, 1); |
1401 | STACK_CHECK( L2); | 1527 | STACK_CHECK( L2); |
1402 | 1528 | ||
1403 | /* Skip the object if it has metatable with { __lanesignore = true } */ | 1529 | /* Skip the object if it has metatable with { __lanesignore = true } */ |
1404 | if( lua_getmetatable( L, i)) // ... mt | 1530 | if( lua_getmetatable( L, i)) // ... mt |
1405 | { | 1531 | { |
1406 | lua_getfield( L, -1, "__lanesignore"); // ... mt ignore? | 1532 | lua_getfield( L, -1, "__lanesignore"); // ... mt ignore? |
1407 | if( lua_isboolean( L, -1) && lua_toboolean( L, -1)) | 1533 | if( lua_isboolean( L, -1) && lua_toboolean( L, -1)) |
@@ -1426,7 +1552,7 @@ static bool_t inter_copy_one_( struct s_Universe* U, lua_State* L2, uint_t L2_ca | |||
1426 | if( lua_isinteger( L, i)) | 1552 | if( lua_isinteger( L, i)) |
1427 | { | 1553 | { |
1428 | lua_Integer v = lua_tointeger( L, i); | 1554 | lua_Integer v = lua_tointeger( L, i); |
1429 | DEBUGSPEW_CODE( if( vt == VT_KEY) fprintf( stderr, INDENT_BEGIN "KEY: %d\n" INDENT_END, v)); | 1555 | DEBUGSPEW_CODE( if( vt == VT_KEY) fprintf( stderr, INDENT_BEGIN "KEY: " LUA_INTEGER_FMT "\n" INDENT_END, v)); |
1430 | lua_pushinteger( L2, v); | 1556 | lua_pushinteger( L2, v); |
1431 | break; | 1557 | break; |
1432 | } | 1558 | } |
@@ -1449,7 +1575,7 @@ static bool_t inter_copy_one_( struct s_Universe* U, lua_State* L2, uint_t L2_ca | |||
1449 | break; | 1575 | break; |
1450 | 1576 | ||
1451 | case LUA_TLIGHTUSERDATA: | 1577 | case LUA_TLIGHTUSERDATA: |
1452 | lua_pushlightuserdata( L2, lua_touserdata(L, i)); | 1578 | lua_pushlightuserdata( L2, lua_touserdata( L, i)); |
1453 | break; | 1579 | break; |
1454 | 1580 | ||
1455 | /* The following types are not allowed as table keys */ | 1581 | /* The following types are not allowed as table keys */ |
@@ -1525,6 +1651,16 @@ static bool_t inter_copy_one_( struct s_Universe* U, lua_State* L2, uint_t L2_ca | |||
1525 | STACK_CHECK( L); | 1651 | STACK_CHECK( L); |
1526 | STACK_CHECK( L2); | 1652 | STACK_CHECK( L2); |
1527 | 1653 | ||
1654 | /* | ||
1655 | * First, let's try to see if this table is special (aka is it some table that we registered in our lookup databases during module registration?) | ||
1656 | * Note that this table CAN be a module table, but we just didn't register it, in which case we'll send it through the table cloning mechanism | ||
1657 | */ | ||
1658 | if( lookup_table( L2, L, i, mode_, upName_)) | ||
1659 | { | ||
1660 | ASSERT_L( lua_istable( L2, -1) || (lua_tocfunction( L2, -1) == table_lookup_sentinel)); // from lookup datables // can also be table_lookup_sentinel if this is a table we know | ||
1661 | break; | ||
1662 | } | ||
1663 | |||
1528 | /* Check if we've already copied the same table from 'L' (during this transmission), and | 1664 | /* Check if we've already copied the same table from 'L' (during this transmission), and |
1529 | * reuse the old copy. This allows table upvalues shared by multiple | 1665 | * reuse the old copy. This allows table upvalues shared by multiple |
1530 | * local functions to point to the same table, also in the target. | 1666 | * local functions to point to the same table, also in the target. |
@@ -1539,7 +1675,7 @@ static bool_t inter_copy_one_( struct s_Universe* U, lua_State* L2, uint_t L2_ca | |||
1539 | ASSERT_L( lua_istable( L2, -1)); // from cache | 1675 | ASSERT_L( lua_istable( L2, -1)); // from cache |
1540 | break; | 1676 | break; |
1541 | } | 1677 | } |
1542 | ASSERT_L( lua_istable( L2,-1)); | 1678 | ASSERT_L( lua_istable( L2, -1)); |
1543 | 1679 | ||
1544 | STACK_GROW( L, 2); | 1680 | STACK_GROW( L, 2); |
1545 | STACK_GROW( L2, 2); | 1681 | STACK_GROW( L2, 2); |
@@ -1547,7 +1683,7 @@ static bool_t inter_copy_one_( struct s_Universe* U, lua_State* L2, uint_t L2_ca | |||
1547 | lua_pushnil( L); // start iteration | 1683 | lua_pushnil( L); // start iteration |
1548 | while( lua_next( L, i)) | 1684 | while( lua_next( L, i)) |
1549 | { | 1685 | { |
1550 | uint_t val_i = lua_gettop(L); | 1686 | uint_t val_i = lua_gettop( L); |
1551 | uint_t key_i = val_i - 1; | 1687 | uint_t key_i = val_i - 1; |
1552 | 1688 | ||
1553 | // Only basic key types are copied over; others ignored | 1689 | // Only basic key types are copied over; others ignored |
@@ -1613,8 +1749,8 @@ static bool_t inter_copy_one_( struct s_Universe* U, lua_State* L2, uint_t L2_ca | |||
1613 | { /* L2 did not know the metatable */ | 1749 | { /* L2 did not know the metatable */ |
1614 | lua_pop( L2, 1); | 1750 | lua_pop( L2, 1); |
1615 | STACK_MID( L2, 2); | 1751 | STACK_MID( L2, 2); |
1616 | ASSERT_L( lua_istable(L,-1)); | 1752 | ASSERT_L( lua_istable( L,-1)); |
1617 | if( inter_copy_one_( U, L2, L2_cache_i /*for function cacheing*/, L, lua_gettop(L) /*[-1]*/, VT_METATABLE, mode_, upName_)) | 1753 | if( inter_copy_one_( U, L2, L2_cache_i /*for function cacheing*/, L, lua_gettop( L) /*[-1]*/, VT_METATABLE, mode_, upName_)) |
1618 | { | 1754 | { |
1619 | // | 1755 | // |
1620 | // L2 ([-3]: copied table) | 1756 | // L2 ([-3]: copied table) |
diff --git a/tests/parallel_os_calls.lua b/tests/parallel_os_calls.lua index b8cffa1..7119272 100644 --- a/tests/parallel_os_calls.lua +++ b/tests/parallel_os_calls.lua | |||
@@ -1,11 +1,12 @@ | |||
1 | local lanes = require "lanes".configure(1) | 1 | local lanes = require "lanes".configure{with_timers = false, nb_keepers = 1} |
2 | print( os.date()) | 2 | print( os.date()) |
3 | local linda = lanes.linda() | 3 | local linda = lanes.linda() |
4 | lanes.gen("os,base", function() linda:receive(10, "null") print("finished_sleeping " .. os.date()) end)() | 4 | local l1 = lanes.gen("os,base", function() print "start sleeping" linda:receive(10, "null") print("finished_sleeping " .. os.date()) end)() |
5 | lanes.gen("os,base", function() linda:receive(10, "null") print("finished_sleeping " .. os.date()) end)() | 5 | lanes.gen("os,base", function() print "start sleeping" linda:receive(9, "null") print("finished_sleeping " .. os.date()) end)() |
6 | lanes.gen("os,base", function() linda:receive(10, "null") print("finished_sleeping " .. os.date()) end)() | 6 | lanes.gen("os,base", function() print "start sleeping" linda:receive(9, "null") print("finished_sleeping " .. os.date()) end)() |
7 | lanes.gen("os,base", function() linda:receive(10, "null") print("finished_sleeping " .. os.date()) end)() | 7 | lanes.gen("os,base", function() print "start sleeping" linda:receive(9, "null") print("finished_sleeping " .. os.date()) end)() |
8 | 8 | -- wait, else all lanes will get hard-cancelled at stat shutdown | |
9 | l1:join() | ||
9 | --[[ | 10 | --[[ |
10 | lanes.gen("os,base", function() os.execute('sleep 10 && echo finished_sleeping') print( os.date()) end)() | 11 | lanes.gen("os,base", function() os.execute('sleep 10 && echo finished_sleeping') print( os.date()) end)() |
11 | lanes.gen("os,base", function() os.execute('sleep 10 && echo finished_sleeping') print( os.date()) end)() | 12 | lanes.gen("os,base", function() os.execute('sleep 10 && echo finished_sleeping') print( os.date()) end)() |