diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-07 17:56:10 +0200 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-13 18:15:46 +0200 |
| commit | 13f7f505375f7c1afd3a7e479a64cc147501b01d (patch) | |
| tree | 3bccba196595305ffd5f2b30f838dd39fbc5d51d /src | |
| parent | d093c5555ec439affcfbecdceabfb122aa8c2f73 (diff) | |
| download | lanes-13f7f505375f7c1afd3a7e479a64cc147501b01d.tar.gz lanes-13f7f505375f7c1afd3a7e479a64cc147501b01d.tar.bz2 lanes-13f7f505375f7c1afd3a7e479a64cc147501b01d.zip | |
Linda API changes
* timeout clarifications (negative values are no longer accepted, use nil instead)
* linda(send, linda.null, key, ...) removed, if you want to send a nil, just do it as usual
Diffstat (limited to 'src')
| -rw-r--r-- | src/cancel.cpp | 35 | ||||
| -rw-r--r-- | src/cancel.h | 2 | ||||
| -rw-r--r-- | src/lanes.cpp | 46 | ||||
| -rw-r--r-- | src/lanes.lua | 17 | ||||
| -rw-r--r-- | src/lanes_private.h | 2 | ||||
| -rw-r--r-- | src/linda.cpp | 25 | ||||
| -rw-r--r-- | src/lindafactory.cpp | 1 | ||||
| -rw-r--r-- | src/universe.cpp | 3 |
8 files changed, 69 insertions, 62 deletions
diff --git a/src/cancel.cpp b/src/cancel.cpp index dd848a7..fe1623b 100644 --- a/src/cancel.cpp +++ b/src/cancel.cpp | |||
| @@ -107,7 +107,7 @@ LUAG_FUNC(cancel_test) | |||
| 107 | 107 | ||
| 108 | // ################################################################################################# | 108 | // ################################################################################################# |
| 109 | 109 | ||
| 110 | [[nodiscard]] static CancelResult thread_cancel_soft(Lane* lane_, lua_Duration duration_, bool wakeLane_) | 110 | [[nodiscard]] static CancelResult thread_cancel_soft(Lane* lane_, std::chrono::time_point<std::chrono::steady_clock> until_, bool wakeLane_) |
| 111 | { | 111 | { |
| 112 | lane_->cancelRequest = CancelRequest::Soft; // it's now signaled to stop | 112 | lane_->cancelRequest = CancelRequest::Soft; // it's now signaled to stop |
| 113 | // negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own | 113 | // negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own |
| @@ -118,12 +118,12 @@ LUAG_FUNC(cancel_test) | |||
| 118 | } | 118 | } |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | return lane_->waitForCompletion(duration_) ? CancelResult::Cancelled : CancelResult::Timeout; | 121 | return lane_->waitForCompletion(until_) ? CancelResult::Cancelled : CancelResult::Timeout; |
| 122 | } | 122 | } |
| 123 | 123 | ||
| 124 | // ################################################################################################# | 124 | // ################################################################################################# |
| 125 | 125 | ||
| 126 | [[nodiscard]] static CancelResult thread_cancel_hard(Lane* lane_, lua_Duration duration_, bool wakeLane_) | 126 | [[nodiscard]] static CancelResult thread_cancel_hard(Lane* lane_, std::chrono::time_point<std::chrono::steady_clock> until_, bool wakeLane_) |
| 127 | { | 127 | { |
| 128 | lane_->cancelRequest = CancelRequest::Hard; // it's now signaled to stop | 128 | lane_->cancelRequest = CancelRequest::Hard; // it's now signaled to stop |
| 129 | // lane_->thread.get_stop_source().request_stop(); | 129 | // lane_->thread.get_stop_source().request_stop(); |
| @@ -134,13 +134,13 @@ LUAG_FUNC(cancel_test) | |||
| 134 | } | 134 | } |
| 135 | } | 135 | } |
| 136 | 136 | ||
| 137 | CancelResult result{ lane_->waitForCompletion(duration_) ? CancelResult::Cancelled : CancelResult::Timeout }; | 137 | CancelResult result{ lane_->waitForCompletion(until_) ? CancelResult::Cancelled : CancelResult::Timeout }; |
| 138 | return result; | 138 | return result; |
| 139 | } | 139 | } |
| 140 | 140 | ||
| 141 | // ################################################################################################# | 141 | // ################################################################################################# |
| 142 | 142 | ||
| 143 | CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hookCount_, lua_Duration duration_, bool wakeLane_) | 143 | CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hookCount_, std::chrono::time_point<std::chrono::steady_clock> until_, bool wakeLane_) |
| 144 | { | 144 | { |
| 145 | // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here | 145 | // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here |
| 146 | // We can read 'lane_->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN) | 146 | // We can read 'lane_->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN) |
| @@ -152,12 +152,12 @@ CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hookCount_, lua_Durati | |||
| 152 | // signal the linda the wake up the thread so that it can react to the cancel query | 152 | // signal the linda the wake up the thread so that it can react to the cancel query |
| 153 | // let us hope we never land here with a pointer on a linda that has been destroyed... | 153 | // let us hope we never land here with a pointer on a linda that has been destroyed... |
| 154 | if (op_ == CancelOp::Soft) { | 154 | if (op_ == CancelOp::Soft) { |
| 155 | return thread_cancel_soft(lane_, duration_, wakeLane_); | 155 | return thread_cancel_soft(lane_, until_, wakeLane_); |
| 156 | } else if (static_cast<int>(op_) > static_cast<int>(CancelOp::Soft)) { | 156 | } else if (static_cast<int>(op_) > static_cast<int>(CancelOp::Soft)) { |
| 157 | lua_sethook(lane_->L, cancel_hook, static_cast<int>(op_), hookCount_); | 157 | lua_sethook(lane_->L, cancel_hook, static_cast<int>(op_), hookCount_); |
| 158 | } | 158 | } |
| 159 | 159 | ||
| 160 | return thread_cancel_hard(lane_, duration_, wakeLane_); | 160 | return thread_cancel_hard(lane_, until_, wakeLane_); |
| 161 | } | 161 | } |
| 162 | 162 | ||
| 163 | // ################################################################################################# | 163 | // ################################################################################################# |
| @@ -200,7 +200,7 @@ CancelOp which_cancel_op(char const* opString_) | |||
| 200 | 200 | ||
| 201 | // ################################################################################################# | 201 | // ################################################################################################# |
| 202 | 202 | ||
| 203 | // bool[,reason] = lane_h:cancel( [mode, hookcount] [, timeout] [, wake_lindas]) | 203 | // bool[,reason] = lane_h:cancel( [mode, hookcount] [, timeout] [, wake_lane]) |
| 204 | LUAG_FUNC(thread_cancel) | 204 | LUAG_FUNC(thread_cancel) |
| 205 | { | 205 | { |
| 206 | Lane* const lane{ ToLane(L_, 1) }; | 206 | Lane* const lane{ ToLane(L_, 1) }; |
| @@ -215,14 +215,19 @@ LUAG_FUNC(thread_cancel) | |||
| 215 | } | 215 | } |
| 216 | } | 216 | } |
| 217 | 217 | ||
| 218 | lua_Duration wait_timeout{ 0.0 }; | 218 | std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; |
| 219 | if (lua_type(L_, 2) == LUA_TNUMBER) { | 219 | if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion |
| 220 | wait_timeout = lua_Duration{ lua_tonumber(L_, 2) }; | 220 | lua_Duration const duration{ lua_tonumber(L_, 2) }; |
| 221 | lua_remove(L_, 2); // argument is processed, remove it | 221 | if (duration.count() >= 0.0) { |
| 222 | if (wait_timeout.count() < 0.0) { | 222 | until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration); |
| 223 | raise_luaL_error(L_, "cancel timeout cannot be < 0"); | 223 | } else { |
| 224 | raise_luaL_argerror(L_, 2, "duration cannot be < 0"); | ||
| 224 | } | 225 | } |
| 226 | lua_remove(L_, 2); // argument is processed, remove it | ||
| 227 | } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key | ||
| 228 | lua_remove(L_, 2); // argument is processed, remove it | ||
| 225 | } | 229 | } |
| 230 | |||
| 226 | // we wake by default in "hard" mode (remember that hook is hard too), but this can be turned off if desired | 231 | // we wake by default in "hard" mode (remember that hook is hard too), but this can be turned off if desired |
| 227 | bool wake_lane{ op != CancelOp::Soft }; | 232 | bool wake_lane{ op != CancelOp::Soft }; |
| 228 | if (lua_gettop(L_) >= 2) { | 233 | if (lua_gettop(L_) >= 2) { |
| @@ -233,7 +238,7 @@ LUAG_FUNC(thread_cancel) | |||
| 233 | lua_remove(L_, 2); // argument is processed, remove it | 238 | lua_remove(L_, 2); // argument is processed, remove it |
| 234 | } | 239 | } |
| 235 | STACK_CHECK_START_REL(L_, 0); | 240 | STACK_CHECK_START_REL(L_, 0); |
| 236 | switch (thread_cancel(lane, op, hook_count, wait_timeout, wake_lane)) { | 241 | switch (thread_cancel(lane, op, hook_count, until, wake_lane)) { |
| 237 | default: // should never happen unless we added a case and forgot to handle it | 242 | default: // should never happen unless we added a case and forgot to handle it |
| 238 | LUA_ASSERT(L_, false); | 243 | LUA_ASSERT(L_, false); |
| 239 | break; | 244 | break; |
diff --git a/src/cancel.h b/src/cancel.h index 3df5252..1918df3 100644 --- a/src/cancel.h +++ b/src/cancel.h | |||
| @@ -49,7 +49,7 @@ enum class CancelOp | |||
| 49 | static constexpr UniqueKey kCancelError{ 0x0630345FEF912746ull, "lanes.cancel_error" }; // 'raise_cancel_error' sentinel | 49 | static constexpr UniqueKey kCancelError{ 0x0630345FEF912746ull, "lanes.cancel_error" }; // 'raise_cancel_error' sentinel |
| 50 | 50 | ||
| 51 | [[nodiscard]] CancelOp which_cancel_op(char const* opString_); | 51 | [[nodiscard]] CancelOp which_cancel_op(char const* opString_); |
| 52 | [[nodiscard]] CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hookCount_, lua_Duration secs_, bool wakeLane_); | 52 | [[nodiscard]] CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hookCount_, std::chrono::time_point<std::chrono::steady_clock> until_, bool wakeLane_); |
| 53 | 53 | ||
| 54 | [[noreturn]] static inline void raise_cancel_error(lua_State* L_) | 54 | [[noreturn]] static inline void raise_cancel_error(lua_State* L_) |
| 55 | { | 55 | { |
diff --git a/src/lanes.cpp b/src/lanes.cpp index 90f0f9f..d211b6a 100644 --- a/src/lanes.cpp +++ b/src/lanes.cpp | |||
| @@ -169,17 +169,12 @@ Lane::Lane(Universe* U_, lua_State* L_) | |||
| 169 | 169 | ||
| 170 | // ################################################################################################# | 170 | // ################################################################################################# |
| 171 | 171 | ||
| 172 | bool Lane::waitForCompletion(lua_Duration duration_) | 172 | bool Lane::waitForCompletion(std::chrono::time_point<std::chrono::steady_clock> until_) |
| 173 | { | 173 | { |
| 174 | std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; | ||
| 175 | if (duration_.count() >= 0.0) { | ||
| 176 | until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration_); | ||
| 177 | } | ||
| 178 | |||
| 179 | std::unique_lock lock{ doneMutex }; | 174 | std::unique_lock lock{ doneMutex }; |
| 180 | // std::stop_token token{ thread.get_stop_token() }; | 175 | // std::stop_token token{ thread.get_stop_token() }; |
| 181 | // return doneCondVar.wait_until(lock, token, secs_, [this](){ return status >= Lane::Done; }); | 176 | // return doneCondVar.wait_until(lock, token, secs_, [this](){ return status >= Lane::Done; }); |
| 182 | return doneCondVar.wait_until(lock, until, [this]() { return status >= Lane::Done; }); | 177 | return doneCondVar.wait_until(lock, until_, [this]() { return status >= Lane::Done; }); |
| 183 | } | 178 | } |
| 184 | 179 | ||
| 185 | // ################################################################################################# | 180 | // ################################################################################################# |
| @@ -1209,22 +1204,33 @@ void Lane::pushThreadStatus(lua_State* L_) | |||
| 1209 | LUAG_FUNC(thread_join) | 1204 | LUAG_FUNC(thread_join) |
| 1210 | { | 1205 | { |
| 1211 | Lane* const lane{ ToLane(L_, 1) }; | 1206 | Lane* const lane{ ToLane(L_, 1) }; |
| 1212 | lua_Duration const duration{ luaL_optnumber(L_, 2, -1.0) }; | ||
| 1213 | lua_State* const L2{ lane->L }; | 1207 | lua_State* const L2{ lane->L }; |
| 1214 | 1208 | ||
| 1215 | bool const done{ !lane->thread.joinable() || lane->waitForCompletion(duration) }; | 1209 | std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; |
| 1210 | if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion | ||
| 1211 | lua_Duration const duration{ lua_tonumber(L_, 2) }; | ||
| 1212 | if (duration.count() >= 0.0) { | ||
| 1213 | until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration); | ||
| 1214 | } else { | ||
| 1215 | raise_luaL_argerror(L_, 2, "duration cannot be < 0"); | ||
| 1216 | } | ||
| 1217 | |||
| 1218 | } else if (!lua_isnoneornil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key | ||
| 1219 | raise_luaL_argerror(L_, 2, "incorrect duration type"); | ||
| 1220 | } | ||
| 1221 | |||
| 1222 | bool const done{ !lane->thread.joinable() || lane->waitForCompletion(until) }; | ||
| 1223 | lua_settop(L_, 1); // L_: lane | ||
| 1216 | if (!done || !L2) { | 1224 | if (!done || !L2) { |
| 1217 | STACK_GROW(L_, 2); | 1225 | lua_pushnil(L_); // L_: lane nil |
| 1218 | lua_pushnil(L_); // L_: lane timeout? nil | 1226 | lua_pushliteral(L_, "timeout"); // L_: lane nil "timeout" |
| 1219 | lua_pushliteral(L_, "timeout"); // L_: lane timeout? nil "timeout" | ||
| 1220 | return 2; | 1227 | return 2; |
| 1221 | } | 1228 | } |
| 1222 | 1229 | ||
| 1223 | STACK_CHECK_START_REL(L_, 0); | 1230 | STACK_CHECK_START_REL(L_, 0); // L_: lane |
| 1224 | // Thread is Done/Error/Cancelled; all ours now | 1231 | // Thread is Done/Error/Cancelled; all ours now |
| 1225 | 1232 | ||
| 1226 | int ret{ 0 }; | 1233 | int ret{ 0 }; |
| 1227 | Universe* const U{ lane->U }; | ||
| 1228 | // debugName is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed | 1234 | // debugName is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed |
| 1229 | // so store it in the userdata uservalue at a key that can't possibly collide | 1235 | // so store it in the userdata uservalue at a key that can't possibly collide |
| 1230 | lane->securizeDebugName(L_); | 1236 | lane->securizeDebugName(L_); |
| @@ -1234,8 +1240,8 @@ LUAG_FUNC(thread_join) | |||
| 1234 | int const n{ lua_gettop(L2) }; // whole L2 stack | 1240 | int const n{ lua_gettop(L2) }; // whole L2 stack |
| 1235 | if ( | 1241 | if ( |
| 1236 | (n > 0) && | 1242 | (n > 0) && |
| 1237 | (InterCopyContext{ U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} }.inter_move(n) != InterCopyResult::Success) | 1243 | (InterCopyContext{ lane->U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} }.inter_move(n) != InterCopyResult::Success) |
| 1238 | ) { // L_: lane timeout? results L2: | 1244 | ) { // L_: lane results L2: |
| 1239 | raise_luaL_error(L_, "tried to copy unsupported types"); | 1245 | raise_luaL_error(L_, "tried to copy unsupported types"); |
| 1240 | } | 1246 | } |
| 1241 | ret = n; | 1247 | ret = n; |
| @@ -1244,12 +1250,12 @@ LUAG_FUNC(thread_join) | |||
| 1244 | 1250 | ||
| 1245 | case Lane::Error: | 1251 | case Lane::Error: |
| 1246 | { | 1252 | { |
| 1247 | int const n{ lua_gettop(L2) }; // L_: lane timeout? L2: "err" [trace] | 1253 | int const n{ lua_gettop(L2) }; // L_: lane L2: "err" [trace] |
| 1248 | STACK_GROW(L_, 3); | 1254 | STACK_GROW(L_, 3); |
| 1249 | lua_pushnil(L_); // L_: lane timeout? nil | 1255 | lua_pushnil(L_); // L_: lane nil |
| 1250 | // even when ERROR_FULL_STACK, if the error is not LUA_ERRRUN, the handler wasn't called, and we only have 1 error message on the stack ... | 1256 | // even when ERROR_FULL_STACK, if the error is not LUA_ERRRUN, the handler wasn't called, and we only have 1 error message on the stack ... |
| 1251 | InterCopyContext c{ U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} }; | 1257 | InterCopyContext c{ lane->U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} }; |
| 1252 | if (c.inter_move(n) != InterCopyResult::Success) { // L_: lane timeout? nil "err" [trace] L2: | 1258 | if (c.inter_move(n) != InterCopyResult::Success) { // L_: lane nil "err" [trace] L2: |
| 1253 | raise_luaL_error(L_, "tried to copy unsupported types: %s", lua_tostring(L_, -n)); | 1259 | raise_luaL_error(L_, "tried to copy unsupported types: %s", lua_tostring(L_, -n)); |
| 1254 | } | 1260 | } |
| 1255 | ret = 1 + n; | 1261 | ret = 1 + n; |
diff --git a/src/lanes.lua b/src/lanes.lua index caa8818..0ab6661 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
| @@ -51,16 +51,17 @@ local lanes = setmetatable({}, lanesMeta) | |||
| 51 | -- and 'table' visible. | 51 | -- and 'table' visible. |
| 52 | -- | 52 | -- |
| 53 | local assert = assert(assert) | 53 | local assert = assert(assert) |
| 54 | local error = assert(error) | ||
| 54 | local io = assert(io) | 55 | local io = assert(io) |
| 56 | local pairs = assert(pairs) | ||
| 55 | local string_gmatch = assert(string.gmatch) | 57 | local string_gmatch = assert(string.gmatch) |
| 56 | local string_format = assert(string.format) | 58 | local string_format = assert(string.format) |
| 57 | local select = assert(select) | 59 | local select = assert(select) |
| 58 | local setmetatable = assert(setmetatable) | 60 | local setmetatable = assert(setmetatable) |
| 59 | local table_insert = assert(table.insert) | 61 | local table_insert = assert(table.insert) |
| 60 | local type = assert(type) | 62 | local tonumber = assert(tonumber) |
| 61 | local pairs = assert(pairs) | ||
| 62 | local tostring = assert(tostring) | 63 | local tostring = assert(tostring) |
| 63 | local error = assert(error) | 64 | local type = assert(type) |
| 64 | 65 | ||
| 65 | -- ################################################################################################# | 66 | -- ################################################################################################# |
| 66 | 67 | ||
| @@ -625,10 +626,12 @@ end | |||
| 625 | -- | 626 | -- |
| 626 | -- PUBLIC LANES API | 627 | -- PUBLIC LANES API |
| 627 | local sleep = function(seconds_) | 628 | local sleep = function(seconds_) |
| 628 | seconds_ = seconds_ or 0.0 -- this causes false and nil to be a valid input, equivalent to 0.0, but that's ok | 629 | local type = type(seconds_) |
| 629 | if seconds_ == 'indefinitely' then | 630 | if type == "string" then |
| 630 | seconds_ = nil | 631 | seconds_ = (seconds_ ~= 'indefinitely') and tonumber(seconds_) or nil |
| 631 | elseif type(seconds_) ~= "number" then | 632 | elseif type == "nil" then |
| 633 | seconds_ = 0 | ||
| 634 | elseif type ~= "number" then | ||
| 632 | error("invalid duration " .. string_format("%q", tostring(seconds_))) | 635 | error("invalid duration " .. string_format("%q", tostring(seconds_))) |
| 633 | end | 636 | end |
| 634 | -- receive data on a channel no-one ever sends anything, thus blocking for the specified duration | 637 | -- receive data on a channel no-one ever sends anything, thus blocking for the specified duration |
diff --git a/src/lanes_private.h b/src/lanes_private.h index 196a346..a756c42 100644 --- a/src/lanes_private.h +++ b/src/lanes_private.h | |||
| @@ -94,7 +94,7 @@ class Lane | |||
| 94 | Lane(Universe* U_, lua_State* L_); | 94 | Lane(Universe* U_, lua_State* L_); |
| 95 | ~Lane(); | 95 | ~Lane(); |
| 96 | 96 | ||
| 97 | [[nodiscard]] bool waitForCompletion(lua_Duration duration_); | 97 | [[nodiscard]] bool waitForCompletion(std::chrono::time_point<std::chrono::steady_clock> until_); |
| 98 | void startThread(int priority_); | 98 | void startThread(int priority_); |
| 99 | void pushThreadStatus(lua_State* L_); | 99 | void pushThreadStatus(lua_State* L_); |
| 100 | void changeDebugName(int nameIdx_); | 100 | void changeDebugName(int nameIdx_); |
diff --git a/src/linda.cpp b/src/linda.cpp index bbfbd69..40ef6c7 100644 --- a/src/linda.cpp +++ b/src/linda.cpp | |||
| @@ -180,7 +180,7 @@ int Linda::ProtectedCall(lua_State* L_, lua_CFunction f_) | |||
| 180 | // ################################################################################################# | 180 | // ################################################################################################# |
| 181 | 181 | ||
| 182 | /* | 182 | /* |
| 183 | * bool= linda_send( linda_ud, [timeout_secs=-1,] [linda.null,] key_num|str|bool|lightuserdata, ... ) | 183 | * bool= linda:linda_send([timeout_secs=nil,] key_num|str|bool|lightuserdata, ...) |
| 184 | * | 184 | * |
| 185 | * Send one or more values to a Linda. If there is a limit, all values must fit. | 185 | * Send one or more values to a Linda. If there is a limit, all values must fit. |
| 186 | * | 186 | * |
| @@ -192,25 +192,21 @@ LUAG_FUNC(linda_send) | |||
| 192 | { | 192 | { |
| 193 | auto send = [](lua_State* L_) { | 193 | auto send = [](lua_State* L_) { |
| 194 | Linda* const linda{ ToLinda<false>(L_, 1) }; | 194 | Linda* const linda{ ToLinda<false>(L_, 1) }; |
| 195 | std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; | ||
| 196 | int key_i{ 2 }; // index of first key, if timeout not there | 195 | int key_i{ 2 }; // index of first key, if timeout not there |
| 197 | 196 | ||
| 197 | std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; | ||
| 198 | if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion | 198 | if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion |
| 199 | lua_Duration const duration{ lua_tonumber(L_, 2) }; | 199 | lua_Duration const duration{ lua_tonumber(L_, 2) }; |
| 200 | if (duration.count() >= 0.0) { | 200 | if (duration.count() >= 0.0) { |
| 201 | until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration); | 201 | until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration); |
| 202 | } else { | ||
| 203 | raise_luaL_argerror(L_, 2, "duration cannot be < 0"); | ||
| 202 | } | 204 | } |
| 203 | ++key_i; | 205 | ++key_i; |
| 204 | } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key | 206 | } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key |
| 205 | ++key_i; | 207 | ++key_i; |
| 206 | } | 208 | } |
| 207 | 209 | ||
| 208 | bool const as_nil_sentinel{ kNilSentinel.equals(L_, key_i) }; // if not nullptr, send() will silently send a single nil if nothing is provided | ||
| 209 | if (as_nil_sentinel) { | ||
| 210 | // the real key to send data to is after the kNilSentinel marker | ||
| 211 | ++key_i; | ||
| 212 | } | ||
| 213 | |||
| 214 | // make sure the key is of a valid type | 210 | // make sure the key is of a valid type |
| 215 | check_key_types(L_, key_i, key_i); | 211 | check_key_types(L_, key_i, key_i); |
| 216 | 212 | ||
| @@ -218,12 +214,7 @@ LUAG_FUNC(linda_send) | |||
| 218 | 214 | ||
| 219 | // make sure there is something to send | 215 | // make sure there is something to send |
| 220 | if (lua_gettop(L_) == key_i) { | 216 | if (lua_gettop(L_) == key_i) { |
| 221 | if (as_nil_sentinel) { | 217 | raise_luaL_error(L_, "no data to send"); |
| 222 | // send a single nil if nothing is provided | ||
| 223 | kNilSentinel.pushKey(L_); | ||
| 224 | } else { | ||
| 225 | raise_luaL_error(L_, "no data to send"); | ||
| 226 | } | ||
| 227 | } | 218 | } |
| 228 | 219 | ||
| 229 | // convert nils to some special non-nil sentinel in sent values | 220 | // convert nils to some special non-nil sentinel in sent values |
| @@ -322,7 +313,7 @@ LUAG_FUNC(linda_send) | |||
| 322 | 313 | ||
| 323 | /* | 314 | /* |
| 324 | * 2 modes of operation | 315 | * 2 modes of operation |
| 325 | * [val, key]= linda_receive( linda_ud, [timeout_secs_num=-1], key_num|str|bool|lightuserdata [, ...] ) | 316 | * [val, key]= linda_receive( linda_ud, [timeout_secs_num=nil], key_num|str|bool|lightuserdata [, ...] ) |
| 326 | * Consumes a single value from the Linda, in any key. | 317 | * Consumes a single value from the Linda, in any key. |
| 327 | * Returns: received value (which is consumed from the slot), and the key which had it | 318 | * Returns: received value (which is consumed from the slot), and the key which had it |
| 328 | 319 | ||
| @@ -335,13 +326,15 @@ LUAG_FUNC(linda_receive) | |||
| 335 | { | 326 | { |
| 336 | auto receive = [](lua_State* L_) { | 327 | auto receive = [](lua_State* L_) { |
| 337 | Linda* const linda{ ToLinda<false>(L_, 1) }; | 328 | Linda* const linda{ ToLinda<false>(L_, 1) }; |
| 338 | std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; | ||
| 339 | int key_i{ 2 }; // index of first key, if timeout not there | 329 | int key_i{ 2 }; // index of first key, if timeout not there |
| 340 | 330 | ||
| 331 | std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; | ||
| 341 | if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion | 332 | if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion |
| 342 | lua_Duration const duration{ lua_tonumber(L_, 2) }; | 333 | lua_Duration const duration{ lua_tonumber(L_, 2) }; |
| 343 | if (duration.count() >= 0.0) { | 334 | if (duration.count() >= 0.0) { |
| 344 | until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration); | 335 | until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration); |
| 336 | } else { | ||
| 337 | raise_luaL_argerror(L_, 2, "duration cannot be < 0"); | ||
| 345 | } | 338 | } |
| 346 | ++key_i; | 339 | ++key_i; |
| 347 | } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key | 340 | } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key |
diff --git a/src/lindafactory.cpp b/src/lindafactory.cpp index 0ec5a0a..917d949 100644 --- a/src/lindafactory.cpp +++ b/src/lindafactory.cpp | |||
| @@ -32,6 +32,7 @@ THE SOFTWARE. | |||
| 32 | 32 | ||
| 33 | #include "lindafactory.h" | 33 | #include "lindafactory.h" |
| 34 | 34 | ||
| 35 | #include "lanes_private.h" | ||
| 35 | #include "linda.h" | 36 | #include "linda.h" |
| 36 | 37 | ||
| 37 | // must be a #define instead of a constexpr to work with lua_pushliteral (until I templatize it) | 38 | // must be a #define instead of a constexpr to work with lua_pushliteral (until I templatize it) |
diff --git a/src/universe.cpp b/src/universe.cpp index 6adc314..becffdd 100644 --- a/src/universe.cpp +++ b/src/universe.cpp | |||
| @@ -89,13 +89,12 @@ void Universe::terminateFreeRunningLanes(lua_State* L_, lua_Duration shutdownTim | |||
| 89 | { | 89 | { |
| 90 | std::lock_guard<std::mutex> guard{ selfdestructMutex }; | 90 | std::lock_guard<std::mutex> guard{ selfdestructMutex }; |
| 91 | Lane* lane{ selfdestructFirst }; | 91 | Lane* lane{ selfdestructFirst }; |
| 92 | lua_Duration timeout{ 1us }; | ||
| 93 | while (lane != SELFDESTRUCT_END) { | 92 | while (lane != SELFDESTRUCT_END) { |
| 94 | // attempt the requested cancel with a small timeout. | 93 | // attempt the requested cancel with a small timeout. |
| 95 | // if waiting on a linda, they will raise a cancel_error. | 94 | // if waiting on a linda, they will raise a cancel_error. |
| 96 | // if a cancellation hook is desired, it will be installed to try to raise an error | 95 | // if a cancellation hook is desired, it will be installed to try to raise an error |
| 97 | if (lane->thread.joinable()) { | 96 | if (lane->thread.joinable()) { |
| 98 | std::ignore = thread_cancel(lane, op_, 1, timeout, true); | 97 | std::ignore = thread_cancel(lane, op_, 1, std::chrono::steady_clock::now() + 1us, true); |
| 99 | } | 98 | } |
| 100 | lane = lane->selfdestruct_next; | 99 | lane = lane->selfdestruct_next; |
| 101 | } | 100 | } |
