aboutsummaryrefslogtreecommitdiff
path: root/src/linda.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/linda.cpp')
-rw-r--r--src/linda.cpp152
1 files changed, 81 insertions, 71 deletions
diff --git a/src/linda.cpp b/src/linda.cpp
index 0cdacfa..a9ae61c 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -237,11 +237,23 @@ namespace {
237 _lane->waiting_on = &_linda->writeHappened; 237 _lane->waiting_on = &_linda->writeHappened;
238 _lane->status.store(Lane::Waiting, std::memory_order_release); 238 _lane->status.store(Lane::Waiting, std::memory_order_release);
239 } 239 }
240
241 // wait until the final target date by small increments, interrupting regularly so that we can check for cancel requests,
242 // in case some timing issue caused a cancel request to be issued, and the condvar signalled, before we actually wait for it
243 auto const [_forceTryAgain, _until_check_cancel] = std::invoke([_until, wakePeriod = _linda->getWakePeriod()] {
244 auto _until_check_cancel{ std::chrono::time_point<std::chrono::steady_clock>::max() };
245 if (wakePeriod.count() > 0.0f) {
246 _until_check_cancel = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(wakePeriod);
247 }
248 bool const _forceTryAgain{ _until_check_cancel < _until };
249 return std::make_tuple(_forceTryAgain, _forceTryAgain ? _until_check_cancel : _until);
250 });
251
240 // not enough data to read: wakeup when data was sent, or when timeout is reached 252 // not enough data to read: wakeup when data was sent, or when timeout is reached
241 std::unique_lock<std::mutex> _guard{ _keeper->mutex, std::adopt_lock }; 253 std::unique_lock<std::mutex> _guard{ _keeper->mutex, std::adopt_lock };
242 std::cv_status const _status{ _linda->writeHappened.wait_until(_guard, _until) }; 254 std::cv_status const _status{ _linda->writeHappened.wait_until(_guard, _until_check_cancel) };
243 _guard.release(); // we don't want to unlock the mutex on exit! 255 _guard.release(); // we don't want to unlock the mutex on exit!
244 _try_again = (_status == std::cv_status::no_timeout); // detect spurious wakeups 256 _try_again = _forceTryAgain || (_status == std::cv_status::no_timeout); // detect spurious wakeups
245 if (_lane != nullptr) { 257 if (_lane != nullptr) {
246 _lane->waiting_on = nullptr; 258 _lane->waiting_on = nullptr;
247 _lane->status.store(_prev_status, std::memory_order_release); 259 _lane->status.store(_prev_status, std::memory_order_release);
@@ -296,9 +308,10 @@ LUAG_FUNC(linda);
296// ################################################################################################# 308// #################################################################################################
297// ################################################################################################# 309// #################################################################################################
298 310
299Linda::Linda(Universe* const U_, LindaGroup const group_, std::string_view const& name_) 311Linda::Linda(Universe* const U_, std::string_view const& name_, lua_Duration const wake_period_, LindaGroup const group_)
300: DeepPrelude{ LindaFactory::Instance } 312: DeepPrelude{ LindaFactory::Instance }
301, U{ U_ } 313, U{ U_ }
314, wakePeriod{ wake_period_ }
302, keeperIndex{ group_ % U_->keepers.getNbKeepers() } 315, keeperIndex{ group_ % U_->keepers.getNbKeepers() }
303{ 316{
304 setName(name_); 317 setName(name_);
@@ -330,9 +343,13 @@ Linda* Linda::CreateTimerLinda(lua_State* const L_)
330 STACK_CHECK_START_REL(L_, 0); // L_: 343 STACK_CHECK_START_REL(L_, 0); // L_:
331 // Initialize 'timerLinda'; a common Linda object shared by all states 344 // Initialize 'timerLinda'; a common Linda object shared by all states
332 lua_pushcfunction(L_, LG_linda); // L_: lanes.linda 345 lua_pushcfunction(L_, LG_linda); // L_: lanes.linda
333 luaG_pushstring(L_, "lanes-timer"); // L_: lanes.linda "lanes-timer" 346 lua_createtable(L_, 0, 3); // L_: lanes.linda {}
334 lua_pushinteger(L_, 0); // L_: lanes.linda "lanes-timer" 0 347 luaG_pushstring(L_, "lanes-timer"); // L_: lanes.linda {} "lanes-timer"
335 lua_call(L_, 2, 1); // L_: linda 348 luaG_setfield(L_, StackIndex{ -2 }, std::string_view{ "name" }); // L_: lanes.linda { .name="lanes-timer" }
349 lua_pushinteger(L_, 0); // L_: lanes.linda { .name="lanes-timer" } 0
350 luaG_setfield(L_, StackIndex{ -2 }, std::string_view{ "group" }); // L_: lanes.linda { .name="lanes-timer" .group = 0 }
351 // note that wake_period is not set (will default to the value in the universe)
352 lua_call(L_, 1, 1); // L_: linda
336 STACK_CHECK(L_, 1); 353 STACK_CHECK(L_, 1);
337 354
338 // Proxy userdata contents is only a 'DeepPrelude*' pointer 355 // Proxy userdata contents is only a 'DeepPrelude*' pointer
@@ -941,11 +958,23 @@ LUAG_FUNC(linda_send)
941 _lane->waiting_on = &_linda->readHappened; 958 _lane->waiting_on = &_linda->readHappened;
942 _lane->status.store(Lane::Waiting, std::memory_order_release); 959 _lane->status.store(Lane::Waiting, std::memory_order_release);
943 } 960 }
961
962 // wait until the final target date by small increments, interrupting regularly so that we can check for cancel requests,
963 // in case some timing issue caused a cancel request to be issued, and the condvar signalled, before we actually wait for it
964 auto const [_forceTryAgain, _until_check_cancel] = std::invoke([_until, wakePeriod = _linda->getWakePeriod()] {
965 auto _until_check_cancel{ std::chrono::time_point<std::chrono::steady_clock>::max() };
966 if (wakePeriod.count() > 0.0f) {
967 _until_check_cancel = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(wakePeriod);
968 }
969 bool const _forceTryAgain{ _until_check_cancel < _until };
970 return std::make_tuple(_forceTryAgain, _forceTryAgain ? _until_check_cancel : _until);
971 });
972
944 // could not send because no room: wait until some data was read before trying again, or until timeout is reached 973 // could not send because no room: wait until some data was read before trying again, or until timeout is reached
945 std::unique_lock<std::mutex> _guard{ _keeper->mutex, std::adopt_lock }; 974 std::unique_lock<std::mutex> _guard{ _keeper->mutex, std::adopt_lock };
946 std::cv_status const status{ _linda->readHappened.wait_until(_guard, _until) }; 975 std::cv_status const status{ _linda->readHappened.wait_until(_guard, _until_check_cancel) };
947 _guard.release(); // we don't want to unlock the mutex on exit! 976 _guard.release(); // we don't want to unlock the mutex on exit!
948 _try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups 977 _try_again = _forceTryAgain || (status == std::cv_status::no_timeout); // detect spurious wakeups
949 if (_lane != nullptr) { 978 if (_lane != nullptr) {
950 _lane->waiting_on = nullptr; 979 _lane->waiting_on = nullptr;
951 _lane->status.store(_prev_status, std::memory_order_release); 980 _lane->status.store(_prev_status, std::memory_order_release);
@@ -1129,88 +1158,69 @@ namespace {
1129// ################################################################################################# 1158// #################################################################################################
1130 1159
1131/* 1160/*
1132 * ud = lanes.linda( [name[,group[,close_handler]]]) 1161 * ud = lanes.linda{.name = <string>, .group = <number>, .close_handler = <callable>, .wake_period = <number>}
1133 * 1162 *
1134 * returns a linda object, or raises an error if creation failed 1163 * returns a linda object, or raises an error if creation failed
1135 */ 1164 */
1136LUAG_FUNC(linda) 1165LUAG_FUNC(linda)
1137{ 1166{
1138 static constexpr StackIndex kLastArg{ LUA_VERSION_NUM >= 504 ? 3 : 2 }; 1167 // unpack the received table on the stack, putting name wake_period group close_handler in that order
1139 StackIndex const _top{ lua_gettop(L_) }; 1168 StackIndex const _top{ lua_gettop(L_) };
1140 luaL_argcheck(L_, _top <= kLastArg, _top, "too many arguments"); 1169 luaL_argcheck(L_, _top <= 1, _top, "too many arguments");
1141 StackIndex _closeHandlerIdx{}; 1170 if (_top == 0) {
1142 StackIndex _nameIdx{}; 1171 lua_settop(L_, 3); // L_: nil nil nil
1143 StackIndex _groupIdx{}; 1172 }
1144 for (StackIndex const _i : std::ranges::iota_view{ StackIndex{ 1 }, StackIndex{ _top + 1 }}) { 1173 else if (!lua_istable(L_, kIdxTop)) {
1145 switch (luaG_type(L_, _i)) { 1174 luaL_argerror(L_, 1, "expecting a table");
1175 } else {
1176 auto* const _U{ Universe::Get(L_) };
1177 lua_getfield(L_, 1, "wake_period"); // L_: {} wake_period
1178 if (lua_isnil(L_, kIdxTop)) {
1179 lua_pop(L_, 1);
1180 lua_pushnumber(L_, _U->lindaWakePeriod.count());
1181 } else {
1182 luaL_argcheck(L_, luaL_optnumber(L_, 2, 0) > 0, 1, "wake_period must be > 0");
1183 }
1184
1185 lua_getfield(L_, 1, "group"); // L_: {} wake_period group
1186 int const _nbKeepers{ _U->keepers.getNbKeepers() };
1187 if (lua_isnil(L_, kIdxTop)) {
1188 luaL_argcheck(L_, _nbKeepers < 2, 0, "Group is mandatory in multiple Keeper scenarios");
1189 } else {
1190 int const _group{ static_cast<int>(lua_tointeger(L_, kIdxTop)) };
1191 luaL_argcheck(L_, _group >= 0 && _group < _nbKeepers, 1, "group out of range");
1192 }
1193
1146#if LUA_VERSION_NUM >= 504 // to-be-closed support starts with Lua 5.4 1194#if LUA_VERSION_NUM >= 504 // to-be-closed support starts with Lua 5.4
1147 case LuaType::FUNCTION: 1195 lua_getfield(L_, 1, "close_handler"); // L_: {} wake_period group close_handler
1148 luaL_argcheck(L_, _closeHandlerIdx == 0, _i, "More than one __close handler"); 1196 LuaType const _handlerType{ luaG_type(L_, kIdxTop) };
1149 _closeHandlerIdx = _i; 1197 if (_handlerType == LuaType::NIL) {
1150 break; 1198 lua_pop(L_, 1); // L_: {} wake_period group
1151 1199 } else if (_handlerType == LuaType::USERDATA || _handlerType == LuaType::TABLE) {
1152 case LuaType::USERDATA: 1200 luaL_argcheck(L_, luaL_getmetafield(L_, kIdxTop, "__call") != 0, 1, "__close handler is not callable");
1153 case LuaType::TABLE:
1154 luaL_argcheck(L_, _closeHandlerIdx == 0, _i, "More than one __close handler");
1155 luaL_argcheck(L_, luaL_getmetafield(L_, _i, "__call") != 0, _i, "__close handler is not callable");
1156 lua_pop(L_, 1); // luaL_getmetafield() pushed the field, we need to pop it 1201 lua_pop(L_, 1); // luaL_getmetafield() pushed the field, we need to pop it
1157 _closeHandlerIdx = _i; 1202 } else {
1158 break; 1203 luaL_argcheck(L_, _handlerType == LuaType::FUNCTION, 1, "__close handler is not a function");
1204 }
1159#endif // LUA_VERSION_NUM >= 504 1205#endif // LUA_VERSION_NUM >= 504
1160 1206
1161 case LuaType::STRING: 1207 auto const _nameType{ luaG_getfield(L_, StackIndex{ 1 }, "name") }; // L_: {} wake_period group [close_handler] name
1162 luaL_argcheck(L_, _nameIdx == 0, _i, "More than one name"); 1208 luaL_argcheck(L_, _nameType == LuaType::NIL || _nameType == LuaType::STRING, 1, "name is not a string");
1163 _nameIdx = _i; 1209 lua_replace(L_, 1); // L_: name wake_period group [close_handler]
1164 break;
1165
1166 case LuaType::NUMBER:
1167 luaL_argcheck(L_, _groupIdx == 0, _i, "More than one group");
1168 _groupIdx = _i;
1169 break;
1170
1171 default:
1172 luaL_argcheck(L_, false, _i, "Bad argument type (should be a string, a number, or a callable type)");
1173 }
1174 }
1175
1176 int const _nbKeepers{ Universe::Get(L_)->keepers.getNbKeepers() };
1177 if (!_groupIdx) {
1178 luaL_argcheck(L_, _nbKeepers < 2, 0, "Group is mandatory in multiple Keeper scenarios");
1179 } else {
1180 int const _group{ static_cast<int>(lua_tointeger(L_, _groupIdx)) };
1181 luaL_argcheck(L_, _group >= 0 && _group < _nbKeepers, _groupIdx, "Group out of range");
1182 } 1210 }
1183 1211
1184 // done with argument checking, let's proceed 1212 // done with argument checking, let's proceed
1185 if constexpr (LUA_VERSION_NUM >= 504) { 1213 if (lua_gettop(L_) == 4) {
1186 // make sure we have kMaxArgs arguments on the stack for processing, with name, group, and handler, in that order
1187 lua_settop(L_, kLastArg); // L_: a b c
1188 // If either index is 0, lua_settop() adjusted the stack with a nil in slot kLastArg
1189 lua_pushvalue(L_, _closeHandlerIdx ? _closeHandlerIdx : kLastArg); // L_: a b c close_handler
1190 lua_pushvalue(L_, _groupIdx ? _groupIdx : kLastArg); // L_: a b c close_handler group
1191 lua_pushvalue(L_, _nameIdx ? _nameIdx : kLastArg); // L_: a b c close_handler group name
1192 lua_replace(L_, 1); // L_: name b c close_handler group
1193 lua_replace(L_, 2); // L_: name group c close_handler
1194 lua_replace(L_, 3); // L_: name group close_handler
1195
1196 // if we have a __close handler, we need a uservalue slot to store it 1214 // if we have a __close handler, we need a uservalue slot to store it
1197 UserValueCount const _nuv{ _closeHandlerIdx ? 1 : 0 }; 1215 LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, UserValueCount{ 1 }); // L_: name wake_period group [close_handler] linda
1198 LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, _nuv); // L_: name group close_handler linda 1216 lua_replace(L_, 3); // L_: name wake_period linda close_handler
1199 if (_closeHandlerIdx != 0) { 1217 lua_setiuservalue(L_, StackIndex{ 3 }, UserValueIndex{ 1 }); // L_: name wake_period linda
1200 lua_replace(L_, 2); // L_: name linda close_handler
1201 lua_setiuservalue(L_, StackIndex{ 2 }, UserValueIndex{ 1 }); // L_: name linda
1202 }
1203 // depending on whether we have a handler or not, the stack is not in the same state at this point 1218 // depending on whether we have a handler or not, the stack is not in the same state at this point
1204 // just make sure we have our Linda at the top 1219 // just make sure we have our Linda at the top
1205 LUA_ASSERT(L_, ToLinda<true>(L_, kIdxTop)); 1220 LUA_ASSERT(L_, ToLinda<true>(L_, kIdxTop));
1206 return 1; 1221 return 1;
1207 } else { // no to-be-closed support 1222 } else { // no to-be-closed support
1208 // ensure we have name, group in that order on the stack 1223 LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, UserValueCount{ 0 }); // L_: name wake_period group linda
1209 if (_nameIdx > _groupIdx) {
1210 lua_insert(L_, 1); // L_: name group
1211 }
1212 LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, UserValueCount{ 0 }); // L_: name group linda
1213 return 1; 1224 return 1;
1214 } 1225 }
1215
1216} 1226}