aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-05-07 17:56:10 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-05-13 18:15:46 +0200
commit13f7f505375f7c1afd3a7e479a64cc147501b01d (patch)
tree3bccba196595305ffd5f2b30f838dd39fbc5d51d /src
parentd093c5555ec439affcfbecdceabfb122aa8c2f73 (diff)
downloadlanes-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.cpp35
-rw-r--r--src/cancel.h2
-rw-r--r--src/lanes.cpp46
-rw-r--r--src/lanes.lua17
-rw-r--r--src/lanes_private.h2
-rw-r--r--src/linda.cpp25
-rw-r--r--src/lindafactory.cpp1
-rw-r--r--src/universe.cpp3
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
143CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hookCount_, lua_Duration duration_, bool wakeLane_) 143CancelResult 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])
204LUAG_FUNC(thread_cancel) 204LUAG_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
49static constexpr UniqueKey kCancelError{ 0x0630345FEF912746ull, "lanes.cancel_error" }; // 'raise_cancel_error' sentinel 49static 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
172bool Lane::waitForCompletion(lua_Duration duration_) 172bool 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_)
1209LUAG_FUNC(thread_join) 1204LUAG_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--
53local assert = assert(assert) 53local assert = assert(assert)
54local error = assert(error)
54local io = assert(io) 55local io = assert(io)
56local pairs = assert(pairs)
55local string_gmatch = assert(string.gmatch) 57local string_gmatch = assert(string.gmatch)
56local string_format = assert(string.format) 58local string_format = assert(string.format)
57local select = assert(select) 59local select = assert(select)
58local setmetatable = assert(setmetatable) 60local setmetatable = assert(setmetatable)
59local table_insert = assert(table.insert) 61local table_insert = assert(table.insert)
60local type = assert(type) 62local tonumber = assert(tonumber)
61local pairs = assert(pairs)
62local tostring = assert(tostring) 63local tostring = assert(tostring)
63local error = assert(error) 64local type = assert(type)
64 65
65-- ################################################################################################# 66-- #################################################################################################
66 67
@@ -625,10 +626,12 @@ end
625-- 626--
626-- PUBLIC LANES API 627-- PUBLIC LANES API
627local sleep = function(seconds_) 628local 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 }