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 | } |