diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-10 16:21:31 +0200 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-10 16:21:31 +0200 |
commit | 3f5c16116a3a7740ac4ac62b663661d772543c2e (patch) | |
tree | 884df73600bd680929f7ad0ebdfadd087b4841e4 | |
parent | 3763be94cdd1a5cf26fec0f09784b18188fd3054 (diff) | |
download | lanes-3f5c16116a3a7740ac4ac62b663661d772543c2e.tar.gz lanes-3f5c16116a3a7740ac4ac62b663661d772543c2e.tar.bz2 lanes-3f5c16116a3a7740ac4ac62b663661d772543c2e.zip |
Replaced __lanesignore with __lanesconvert
-rw-r--r-- | docs/index.html | 91 | ||||
-rw-r--r-- | src/intercopycontext.cpp | 87 | ||||
-rw-r--r-- | src/intercopycontext.h | 3 | ||||
-rw-r--r-- | tests/basic.lua | 4 |
4 files changed, 121 insertions, 64 deletions
diff --git a/docs/index.html b/docs/index.html index 60f4970..24c7c52 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -546,7 +546,7 @@ | |||
546 | </td> | 546 | </td> |
547 | <td /> | 547 | <td /> |
548 | <td> | 548 | <td> |
549 | root level names, <tt>print</tt>, <tt>assert</tt>, <tt>unpack</tt> etc. | 549 | <tt>_G</tt> namespace (the default function environment): <tt>print</tt>, <tt>assert</tt>, <tt>dofile</tt>, etc. |
550 | </td> | 550 | </td> |
551 | </tr> | 551 | </tr> |
552 | <tr> | 552 | <tr> |
@@ -1150,13 +1150,13 @@ | |||
1150 | <h2 id="lindas">Lindas</h2> | 1150 | <h2 id="lindas">Lindas</h2> |
1151 | 1151 | ||
1152 | <p> | 1152 | <p> |
1153 | Communications between lanes is completely detached from the lane handles themselves. By itself, a lane can only provide return values once it's finished, or throw an error. | 1153 | Communications between lanes is completely detached from the lane handles themselves. By itself, a lane can only provide return values once it is finished, or throw an error. |
1154 | Needs to communicate during runtime are handled by <a href="http://en.wikipedia.org/wiki/Linda_%28coordination_language%29" target="_blank">Linda objects</a>, which are | 1154 | Needs to communicate during runtime are handled by <a href="http://en.wikipedia.org/wiki/Linda_%28coordination_language%29" target="_blank">Linda objects</a>, which are |
1155 | <a href="#deep_userdata">deep userdata</a> instances. They can be provided to a lane as startup parameters, upvalues or in some other Linda's message. | 1155 | <a href="#deep_userdata">deep userdata</a> instances. They can be provided to a lane as startup parameters, upvalues or in some other Linda's message. |
1156 | </p> | 1156 | </p> |
1157 | 1157 | ||
1158 | <p> | 1158 | <p> |
1159 | Access to a Linda object means a lane can read or write to any of its data slots. Multiple lanes can be accessing the same Linda in parallel. No application level locking is required; each Linda operation is atomic. | 1159 | Access to a Linda object means a lane can read or write to any of its data slots. Multiple lanes can be accessing the same Linda simultaneously. Seen from Lua, no application level locking is required; each Linda operation is atomic. |
1160 | </p> | 1160 | </p> |
1161 | 1161 | ||
1162 | <table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"><tr><td><pre> | 1162 | <table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"><tr><td><pre> |
@@ -1212,35 +1212,53 @@ | |||
1212 | 1212 | ||
1213 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1213 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
1214 | h = lanes.linda([opt_name, [opt_group]]) | 1214 | h = lanes.linda([opt_name, [opt_group]]) |
1215 | </pre></td></tr></table> | ||
1215 | 1216 | ||
1216 | true|lanes.cancel_error = h:send([timeout_secs,] key, ...) | 1217 | <p> |
1218 | Converting the Linda to a string will yield the provided name prefixed by <tt>"Linda: "</tt>.<br/> | ||
1219 | If <tt>opt_name</tt> is omitted, it will evaluate to an hexadecimal number uniquely representing that Linda when the Linda is converted to a string. The value is the same as returned by <tt>linda:deep()</tt>.<br/> | ||
1220 | If <tt>opt_name</tt> is <tt>"auto"</tt>, Lanes will try to construct a name from the source location that called <tt>lanes.linda()</tt>. If that fails, the Linda name will be <tt>"<unresolved>"</tt>. | ||
1221 | </p> | ||
1217 | 1222 | ||
1218 | key, val = h:receive([timeout_secs,] key [, key...]) | 1223 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
1224 | true|lanes.cancel_error = h:limit(key, n_uint) | ||
1225 | </pre></td></tr></table> | ||
1219 | 1226 | ||
1220 | key, val [, val...] = h:receive(timeout, h.batched, key, n_uint_min[, n_uint_max]) | 1227 | <p> |
1228 | By default, queue sizes are unlimited but limits can be enforced using the <tt>limit()</tt> method. This can be useful to balance execution speeds in a producer/consumer scenario. <tt>nil</tt> removes the limit.<br/> | ||
1229 | A limit of 0 is allowed to block everything.<br/> | ||
1230 | If the key was full but the limit change added some room, <tt>limit()</tt> returns <tt>true</tt> and the Linda is signalled so that <tt>send()</tt>-blocked threads are awakened.<br/> | ||
1231 | </p> | ||
1221 | 1232 | ||
1222 | true|lanes.cancel_error = h:limit(key, n_uint) | 1233 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
1234 | true|lanes.cancel_error = h:send([timeout_secs,] key, ...) | ||
1223 | </pre></td></tr></table> | 1235 | </pre></td></tr></table> |
1224 | 1236 | ||
1225 | <p> | 1237 | <p> |
1226 | Converting the Linda to a string will yield the provided name prefixed by <tt>"Linda: "</tt>.<br/> | 1238 | Timeouts are given in seconds (>= 0, millisecond accuracy) or <tt>nil</tt>. Timeout can be omitted only if the first key is not a number (then it is equivalent to an infinite duration).<br/> |
1227 | If <tt>opt_name</tt> is omitted, it will evaluate to an hexadecimal number uniquely representing that Linda when the Linda is converted to a string. The value is the same as returned by <tt>linda:deep()</tt>.<br/> | 1239 | Each key acts as a FIFO queue. There is no limit to the number of keys a Linda may contain. Different Lindas can have identical keys, which are totally unrelated. |
1228 | If <tt>opt_name</tt> is <tt>"auto"</tt>, Lanes will try to construct a name from the source location that called <tt>lanes.linda()</tt>. If that fails, the Linda name will be <tt>"<unresolved>"</tt>. | ||
1229 | </p> | 1240 | </p> |
1230 | <p> | 1241 | <p> |
1231 | Timeouts are given in seconds (>= 0, millisecond accuracy) or <tt>nil</tt>. Timeout can be omitted only if the first key is not a number (then it's equivalent to an infinite duration). | 1242 | If no data is provided after the key, <tt>send()</tt> raises an error.<br/> |
1243 | Also, if <tt>linda.null</tt> or <tt>lanes.null</tt> is sent as data in a Linda, it will be read as a <tt>nil</tt>.<br/> | ||
1244 | <tt>send()</tt> returns <tt>true</tt> if the sending succeeded, and <tt>false</tt> if the queue limit was met, and the queue did not empty enough during the given timeout.<br/> | ||
1245 | <tt>send()</tt> returns <tt>lanes.cancel_error</tt> if interrupted by a soft cancel request.<br/> | ||
1232 | </p> | 1246 | </p> |
1233 | 1247 | ||
1248 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | ||
1249 | key, val = h:receive([timeout_secs,] key [, key...]) | ||
1250 | |||
1251 | key, val [, val...] = h:receive(timeout, h.batched, key, n_uint_min[, n_uint_max]) | ||
1252 | </pre></td></tr></table> | ||
1253 | |||
1254 | |||
1234 | <p> | 1255 | <p> |
1235 | The <tt>send()</tt> and <tt>receive()</tt> methods use Linda keys as FIFO stacks (first in, first out).<br/> | 1256 | The <tt>send()</tt> and <tt>receive()</tt> methods use Linda keys as FIFO stacks (first in, first out).<br/> |
1236 | By default, stack sizes are unlimited but limits can be enforced using the <tt>limit()</tt> method. This can be useful to balance execution speeds in a producer/consumer scenario. <tt>nil</tt> removes the limit.<br/> | ||
1237 | A limit of 0 is allowed to block everything.<br/> | ||
1238 | If the key was full but the limit change added some room, <tt>limit()</tt> returns <tt>true</tt> and the Linda is signalled so that <tt>send()</tt>-blocked threads are awakened.<br/> | ||
1239 | In batched mode, <tt>linda:receive()</tt> will raise an error if <tt>min_count < 1</tt> or <tt>max_count < min_count</tt>. | 1257 | In batched mode, <tt>linda:receive()</tt> will raise an error if <tt>min_count < 1</tt> or <tt>max_count < min_count</tt>. |
1240 | </p> | 1258 | </p> |
1241 | 1259 | ||
1242 | <p> | 1260 | <p> |
1243 | Note that any number of lanes can be reading or writing a Linda. There can be many producers, and many consumers. It's up to you. | 1261 | Note that any number of lanes can be reading or writing a Linda. There can be many producers, and many consumers. It is up to you. |
1244 | </p> | 1262 | </p> |
1245 | 1263 | ||
1246 | <p> | 1264 | <p> |
@@ -1248,16 +1266,6 @@ | |||
1248 | </p> | 1266 | </p> |
1249 | 1267 | ||
1250 | <p> | 1268 | <p> |
1251 | <tt>send()</tt> returns <tt>true</tt> if the sending succeeded, and <tt>false</tt> if the queue limit was met, and the queue did not empty enough during the given timeout. | ||
1252 | <br/> | ||
1253 | <tt>send()</tt> returns <tt>lanes.cancel_error</tt> if interrupted by a soft cancel request. | ||
1254 | <br/> | ||
1255 | If no data is provided after the key, <tt>send()</tt> raises an error. | ||
1256 | <br/> | ||
1257 | Also, if <tt>linda.null</tt> or <tt>lanes.null</tt> is sent as data in a Linda, it will be read as a <tt>nil</tt>. | ||
1258 | </p> | ||
1259 | |||
1260 | <p> | ||
1261 | Equally, <tt>receive()</tt> returns a key and the value extracted from it. Note that <tt>nil</tt>s can be sent and received; the <tt>key</tt> value will tell it apart from a timeout.<br/> | 1269 | Equally, <tt>receive()</tt> returns a key and the value extracted from it. Note that <tt>nil</tt>s can be sent and received; the <tt>key</tt> value will tell it apart from a timeout.<br/> |
1262 | <tt>receive()</tt> returns <tt>nil, lanes.cancel_error</tt> if interrupted by a hard cancel request.<br/> | 1270 | <tt>receive()</tt> returns <tt>nil, lanes.cancel_error</tt> if interrupted by a hard cancel request.<br/> |
1263 | <tt>receive()</tt> returns <tt>nil, "timeout"</tt> if nothing was available. | 1271 | <tt>receive()</tt> returns <tt>nil, "timeout"</tt> if nothing was available. |
@@ -1558,14 +1566,22 @@ On the other side, you need to use a common Linda for waiting for multiple keys. | |||
1558 | to the global table in the destination state. Note that this also applies when Lanes is built for Lua 5.1, as it doesn't hurt. | 1566 | to the global table in the destination state. Note that this also applies when Lanes is built for Lua 5.1, as it doesn't hurt. |
1559 | </li> | 1567 | </li> |
1560 | <li> | 1568 | <li> |
1561 | Full userdata can be passed only if it's prepared using the <a href="#deep_userdata">deep userdata</a> system, which handles its lifespan management | 1569 | Full userdata can be passed only if it is prepared using the <a href="#deep_userdata">deep userdata</a> system, which handles its lifespan management |
1562 | <ul> | 1570 | <ul> |
1563 | <li>In particular, lane handles cannot be passed between lanes.</li> | 1571 | <li>In particular, lane handles cannot be passed between lanes.</li> |
1564 | <li>Lanes can either throw an error or attempt a <a href="#demote_full_userdata">light userdata demotion</a>.</li> | 1572 | <li>Lanes can either throw an error or attempt a <a href="#demote_full_userdata">light userdata demotion</a>.</li> |
1565 | </ul> | 1573 | </ul> |
1566 | </li> | 1574 | </li> |
1567 | <li>Coroutines cannot be passed. A coroutine's Lua state is tied to the Lua state that created it, and there is no way the mixed C/Lua stack of a coroutine can be transfered from one Lua state to another.</li> | 1575 | <li>Coroutines cannot be passed. A coroutine's Lua state is tied to the Lua state that created it, and there is no way the mixed C/Lua stack of a coroutine can be transfered from one Lua state to another.</li> |
1568 | <li>If the metatable contains <tt>__lanesignore</tt>, the object is skipped and <tt>nil</tt> is transfered instead.</li> | 1576 | <li> |
1577 | If the metatable contains <tt>__lanesconvert</tt>, the object is converted as follows depending on the value: | ||
1578 | <ul> | ||
1579 | <li><tt>lanes.null</tt>: The value is converted to <tt>nil</tt>.</li> | ||
1580 | <li><tt>"decay"</tt>: The value is converted to a light userdata obtained from <tt>lua_topointer()</tt>.</li> | ||
1581 | <li>A function: The function is called as <tt>o:__lanesconvert(string)</tt>, where the argument is either <tt>"keeper"</tt> or <tt>"regular"</tt>, depending on the type of destination. Its (single) return value is the result of the conversion.</li> | ||
1582 | <li>Any other value raises an error.</li> | ||
1583 | </ul> | ||
1584 | </li> | ||
1569 | </ul> | 1585 | </ul> |
1570 | </p> | 1586 | </p> |
1571 | 1587 | ||
@@ -1573,26 +1589,7 @@ On the other side, you need to use a common Linda for waiting for multiple keys. | |||
1573 | <h3 id="function_notes">Notes about passing C functions</h3> | 1589 | <h3 id="function_notes">Notes about passing C functions</h3> |
1574 | 1590 | ||
1575 | <p> | 1591 | <p> |
1576 | Originally, a C function was copied from one Lua state to another as follows: | 1592 | Functions are transfered as follows (more or less): |
1577 | </p> | ||
1578 | |||
1579 | <table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"><tr><td><pre> | ||
1580 | // expects a C function on top of the source Lua stack | ||
1581 | copy_func(lua_State *dest, lua_State* source) | ||
1582 | { | ||
1583 | // extract C function pointer from source | ||
1584 | lua_CFunction func = lua_tocfunction(source, -1); | ||
1585 | // transfer upvalues | ||
1586 | int nup = transfer_upvalues(dest, source); | ||
1587 | // dest Lua stack contains a copy of all upvalues | ||
1588 | lua_pushcfunction(dest, func, nup); | ||
1589 | } | ||
1590 | </pre></td></tr></table> | ||
1591 | |||
1592 | <p> | ||
1593 | This has the main drawback of not being LuaJIT-compatible, because some functions registered by LuaJIT are not regular C functions, but specially optimized implementations. As a result, <tt>lua_tocfunction()</tt> returns <tt>nullptr</tt> for them. | ||
1594 | <br/> | ||
1595 | Therefore, Lanes no longer transfers functions that way. Instead, functions are transfered as follows (more or less): | ||
1596 | </p> | 1593 | </p> |
1597 | 1594 | ||
1598 | <table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"><tr><td><pre> | 1595 | <table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"><tr><td><pre> |
diff --git a/src/intercopycontext.cpp b/src/intercopycontext.cpp index c7fcf14..a5fd400 100644 --- a/src/intercopycontext.cpp +++ b/src/intercopycontext.cpp | |||
@@ -532,6 +532,76 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
532 | 532 | ||
533 | // ################################################################################################# | 533 | // ################################################################################################# |
534 | 534 | ||
535 | LuaType InterCopyContext::processConversion() const | ||
536 | { | ||
537 | static constexpr int kPODmask = (1 << LUA_TNIL) | (1 << LUA_TBOOLEAN) | (1 << LUA_TLIGHTUSERDATA) | (1 << LUA_TNUMBER) | (1 << LUA_TSTRING); | ||
538 | |||
539 | LuaType _val_type{ luaG_type(L1, L1_i) }; | ||
540 | |||
541 | STACK_CHECK_START_REL(L1, 0); | ||
542 | |||
543 | // it's a POD: nothing to do | ||
544 | if (((1 << static_cast<int>(_val_type)) & kPODmask) != 0) { | ||
545 | return _val_type; | ||
546 | } | ||
547 | |||
548 | // no metatable: nothing to do | ||
549 | if (!lua_getmetatable(L1, L1_i)) { // L1: ... | ||
550 | STACK_CHECK(L1, 0); | ||
551 | return _val_type; | ||
552 | } | ||
553 | // we have a metatable // L1: ... mt | ||
554 | static constexpr std::string_view kConvertField{ "__lanesconvert" }; | ||
555 | LuaType const _converterType{ luaG_getfield(L1, -1, kConvertField) }; // L1: ... mt kConvertField | ||
556 | switch (_converterType) { | ||
557 | case LuaType::NIL: | ||
558 | // no __lanesconvert, nothing to do | ||
559 | lua_pop(L1, 2); // L1: ... | ||
560 | break; | ||
561 | |||
562 | case LuaType::LIGHTUSERDATA: | ||
563 | if (kNilSentinel.equals(L1, -1)) { | ||
564 | DEBUGSPEW_CODE(DebugSpew(U) << "converted " << luaG_typename(L1, _val_type) << " to nil" << std::endl); | ||
565 | lua_replace(L1, L1_i); // L1: ... mt | ||
566 | lua_pop(L1, 1); // L1: ... | ||
567 | _val_type = _converterType; | ||
568 | } else { | ||
569 | raise_luaL_error(getErrL(), "Invalid %s type %s", kConvertField.data(), luaG_typename(L1, _converterType).data()); | ||
570 | } | ||
571 | break; | ||
572 | |||
573 | case LuaType::STRING: | ||
574 | // kConvertField == "decay" -> replace source value with it's pointer | ||
575 | if (std::string_view const _mode{ luaG_tostring(L1, -1) }; _mode == "decay") { | ||
576 | lua_pop(L1, 1); // L1: ... mt | ||
577 | lua_pushlightuserdata(L1, const_cast<void*>(lua_topointer(L1, L1_i))); // L1: ... mt decayed | ||
578 | lua_replace(L1, L1_i); // L1: ... mt | ||
579 | lua_pop(L1, 1); // L1: ... | ||
580 | _val_type = LuaType::LIGHTUSERDATA; | ||
581 | } else { | ||
582 | raise_luaL_error(getErrL(), "Invalid %s mode '%s'", kConvertField.data(), _mode.data()); | ||
583 | } | ||
584 | break; | ||
585 | |||
586 | case LuaType::FUNCTION: | ||
587 | lua_pushvalue(L1, L1_i); // L1: ... mt kConvertField val | ||
588 | std::ignore = luaG_pushstring(L1, mode == LookupMode::ToKeeper ? "keeper" : "regular"); // L1: ... mt kConvertField val string | ||
589 | lua_call(L1, 2, 1); // val:kConvertField(str) -> result // L1: ... mt kConvertField converted | ||
590 | lua_replace(L1, L1_i); // L1: ... mt | ||
591 | lua_pop(L1, 1); // L1: ... mt | ||
592 | _val_type = luaG_type(L1, L1_i); | ||
593 | break; | ||
594 | |||
595 | default: | ||
596 | raise_luaL_error(getErrL(), "Invalid %s type %s", kConvertField.data(), luaG_typename(L1, _converterType).data()); | ||
597 | } | ||
598 | STACK_CHECK(L1, 0); | ||
599 | LUA_ASSERT(getErrL(), luaG_type(L1, L1_i) == _val_type); | ||
600 | return _val_type; | ||
601 | } | ||
602 | |||
603 | // ################################################################################################# | ||
604 | |||
535 | [[nodiscard]] bool InterCopyContext::push_cached_metatable() const | 605 | [[nodiscard]] bool InterCopyContext::push_cached_metatable() const |
536 | { | 606 | { |
537 | STACK_CHECK_START_REL(L1, 0); | 607 | STACK_CHECK_START_REL(L1, 0); |
@@ -1085,21 +1155,10 @@ namespace { | |||
1085 | DEBUGSPEW_CODE(DebugSpew(U) << "inter_copy_one()" << std::endl); | 1155 | DEBUGSPEW_CODE(DebugSpew(U) << "inter_copy_one()" << std::endl); |
1086 | DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ U }); | 1156 | DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ U }); |
1087 | 1157 | ||
1088 | LuaType _val_type{ luaG_type(L1, L1_i) }; | 1158 | // replace the value at L1_i with the result of a conversion if required |
1089 | DEBUGSPEW_CODE(DebugSpew(U) << local::sLuaTypeNames[static_cast<int>(_val_type)] << " " << local::sValueTypeNames[static_cast<int>(vt)] << ": "); | 1159 | LuaType const _val_type{ processConversion() }; |
1090 | |||
1091 | // Non-POD can be skipped if its metatable contains { __lanesignore = true } | ||
1092 | if (((1 << static_cast<int>(_val_type)) & kPODmask) == 0) { | ||
1093 | if (lua_getmetatable(L1, L1_i)) { // L1: ... mt | ||
1094 | LuaType const _type{ luaG_getfield(L1, -1, "__lanesignore") }; // L1: ... mt ignore? | ||
1095 | if (_type == LuaType::BOOLEAN && lua_toboolean(L1, -1)) { | ||
1096 | DEBUGSPEW_CODE(DebugSpew(U) << "__lanesignore -> LUA_TNIL" << std::endl); | ||
1097 | _val_type = LuaType::NIL; | ||
1098 | } | ||
1099 | lua_pop(L1, 2); // L1: ... | ||
1100 | } | ||
1101 | } | ||
1102 | STACK_CHECK(L1, 0); | 1160 | STACK_CHECK(L1, 0); |
1161 | DEBUGSPEW_CODE(DebugSpew(U) << local::sLuaTypeNames[static_cast<int>(_val_type)] << " " << local::sValueTypeNames[static_cast<int>(vt)] << ": "); | ||
1103 | 1162 | ||
1104 | // Lets push nil to L2 if the object should be ignored | 1163 | // Lets push nil to L2 if the object should be ignored |
1105 | bool _ret{ true }; | 1164 | bool _ret{ true }; |
diff --git a/src/intercopycontext.h b/src/intercopycontext.h index ffa825f..459551e 100644 --- a/src/intercopycontext.h +++ b/src/intercopycontext.h | |||
@@ -38,10 +38,11 @@ class InterCopyContext | |||
38 | char const* name; // that one can change when we reuse the context | 38 | char const* name; // that one can change when we reuse the context |
39 | 39 | ||
40 | private: | 40 | private: |
41 | [[nodiscard]] std::string_view findLookupName() const; | ||
41 | // when mode == LookupMode::FromKeeper, L1 is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error | 42 | // when mode == LookupMode::FromKeeper, L1 is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error |
42 | // whon mode != LookupMode::FromKeeper, L1 is not a keeper state, therefore L1 is the state where we want to raise the error | 43 | // whon mode != LookupMode::FromKeeper, L1 is not a keeper state, therefore L1 is the state where we want to raise the error |
43 | lua_State* getErrL() const { return (mode == LookupMode::FromKeeper) ? L2 : L1; } | 44 | lua_State* getErrL() const { return (mode == LookupMode::FromKeeper) ? L2 : L1; } |
44 | [[nodiscard]] std::string_view findLookupName() const; | 45 | [[nodiscard]] LuaType processConversion() const; |
45 | 46 | ||
46 | // for use in copy_cached_func | 47 | // for use in copy_cached_func |
47 | void copy_func() const; | 48 | void copy_func() const; |
diff --git a/tests/basic.lua b/tests/basic.lua index 20865cf..28f0334 100644 --- a/tests/basic.lua +++ b/tests/basic.lua | |||
@@ -226,7 +226,7 @@ local chunk= function(linda) | |||
226 | k,v=receive(); WR("chunk ", v.." received (expecting 1)\n"); assert(v==1) | 226 | k,v=receive(); WR("chunk ", v.." received (expecting 1)\n"); assert(v==1) |
227 | k,v=receive(); WR("chunk ", v.." received (expecting 2)\n"); assert(v==2) | 227 | k,v=receive(); WR("chunk ", v.." received (expecting 2)\n"); assert(v==2) |
228 | k,v=receive(); WR("chunk ", v.." received (expecting 3)\n"); assert(v==3) | 228 | k,v=receive(); WR("chunk ", v.." received (expecting 3)\n"); assert(v==3) |
229 | k,v=receive(); WR("chunk ", tostring(v).." received (expecting nil from __lanesignore)\n"); assert(v==nil) -- a table with __lanesignore was sent | 229 | k,v=receive(); WR("chunk ", tostring(v).." received (expecting nil from __lanesconvert)\n"); assert(v==nil, "table with __lanesconvert==lanes.null should be received as nil, got " .. tostring(v)) -- a table with __lanesconvert was sent |
230 | k,v=receive(); WR("chunk ", tostring(v).." received (expecting nil)\n"); assert(v==nil) | 230 | k,v=receive(); WR("chunk ", tostring(v).." received (expecting nil)\n"); assert(v==nil) |
231 | 231 | ||
232 | send(4,5,6); WR("chunk ", "4,5,6 sent\n") | 232 | send(4,5,6); WR("chunk ", "4,5,6 sent\n") |
@@ -277,7 +277,7 @@ local comms_lane = lanes_gen("io", {gc_cb = gc_cb, name = "auto"}, chunk)(linda) | |||
277 | SEND(1); WR("main ", "1 sent\n") | 277 | SEND(1); WR("main ", "1 sent\n") |
278 | SEND(2); WR("main ", "2 sent\n") | 278 | SEND(2); WR("main ", "2 sent\n") |
279 | SEND(3); WR("main ", "3 sent\n") | 279 | SEND(3); WR("main ", "3 sent\n") |
280 | SEND(setmetatable({"should be ignored"},{__lanesignore=true})); WR("main ", "__lanesignore table sent\n") | 280 | SEND(setmetatable({"should be ignored"},{__lanesconvert=lanes.null})); WR("main ", "__lanesconvert table sent\n") |
281 | for i=1,40 do | 281 | for i=1,40 do |
282 | WR "." | 282 | WR "." |
283 | SLEEP(0.0001) | 283 | SLEEP(0.0001) |