diff options
Diffstat (limited to '')
-rw-r--r-- | src/lanes.lua | 14 | ||||
-rw-r--r-- | src/linda.cpp | 152 | ||||
-rw-r--r-- | src/linda.hpp | 10 | ||||
-rw-r--r-- | src/lindafactory.cpp | 9 | ||||
-rw-r--r-- | src/universe.cpp | 8 | ||||
-rw-r--r-- | src/universe.hpp | 2 |
6 files changed, 118 insertions, 77 deletions
diff --git a/src/lanes.lua b/src/lanes.lua index bd94a14..3ee959c 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
@@ -96,6 +96,7 @@ local default_params = | |||
96 | -- it looks also like LuaJIT allocator may not appreciate direct use of its allocator for other purposes than the VM operation | 96 | -- it looks also like LuaJIT allocator may not appreciate direct use of its allocator for other purposes than the VM operation |
97 | internal_allocator = isLuaJIT and "libc" or "allocator", | 97 | internal_allocator = isLuaJIT and "libc" or "allocator", |
98 | keepers_gc_threshold = -1, | 98 | keepers_gc_threshold = -1, |
99 | linda_wake_period = 'never', | ||
99 | nb_user_keepers = 0, | 100 | nb_user_keepers = 0, |
100 | on_state_create = nil, | 101 | on_state_create = nil, |
101 | shutdown_timeout = 0.25, | 102 | shutdown_timeout = 0.25, |
@@ -141,6 +142,19 @@ local param_checkers = | |||
141 | end | 142 | end |
142 | return true | 143 | return true |
143 | end, | 144 | end, |
145 | linda_wake_period = function(val_) | ||
146 | -- linda_wake_period should be a number > 0, or the string 'never' | ||
147 | if val_ == 'never' then | ||
148 | return true | ||
149 | end | ||
150 | if type(val_) ~= "number" then | ||
151 | return nil, "not a number" | ||
152 | end | ||
153 | if val_ <= 0 then | ||
154 | return nil, "value out of range" | ||
155 | end | ||
156 | return true | ||
157 | end, | ||
144 | nb_user_keepers = function(val_) | 158 | nb_user_keepers = function(val_) |
145 | -- nb_user_keepers should be a number in [0,100] (so that nobody tries to run OOM by specifying a huge amount) | 159 | -- nb_user_keepers should be a number in [0,100] (so that nobody tries to run OOM by specifying a huge amount) |
146 | if type(val_) ~= "number" then | 160 | if type(val_) ~= "number" then |
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 | ||
299 | Linda::Linda(Universe* const U_, LindaGroup const group_, std::string_view const& name_) | 311 | Linda::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 | */ |
1136 | LUAG_FUNC(linda) | 1165 | LUAG_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 | } |
diff --git a/src/linda.hpp b/src/linda.hpp index 2d5c9dc..7874db3 100644 --- a/src/linda.hpp +++ b/src/linda.hpp | |||
@@ -42,19 +42,21 @@ class Linda final | |||
42 | }; | 42 | }; |
43 | using enum Status; | 43 | using enum Status; |
44 | 44 | ||
45 | private: | 45 | public: |
46 | Universe* const U{ nullptr }; // the universe this linda belongs to | ||
46 | 47 | ||
48 | private: | ||
47 | static constexpr size_t kEmbeddedNameLength = 24; | 49 | static constexpr size_t kEmbeddedNameLength = 24; |
48 | using EmbeddedName = std::array<char, kEmbeddedNameLength>; | 50 | using EmbeddedName = std::array<char, kEmbeddedNameLength>; |
49 | // depending on the name length, it is either embedded inside the Linda, or allocated separately | 51 | // depending on the name length, it is either embedded inside the Linda, or allocated separately |
50 | std::variant<std::string_view, EmbeddedName> nameVariant{}; | 52 | std::variant<std::string_view, EmbeddedName> nameVariant{}; |
51 | // counts the keeper operations in progress | 53 | // counts the keeper operations in progress |
52 | std::atomic<int> keeperOperationCount{}; | 54 | std::atomic<int> keeperOperationCount{}; |
55 | lua_Duration wakePeriod{}; | ||
53 | 56 | ||
54 | public: | 57 | public: |
55 | std::condition_variable readHappened{}; | 58 | std::condition_variable readHappened{}; |
56 | std::condition_variable writeHappened{}; | 59 | std::condition_variable writeHappened{}; |
57 | Universe* const U{ nullptr }; // the universe this linda belongs to | ||
58 | KeeperIndex const keeperIndex{ -1 }; // the keeper associated to this linda | 60 | KeeperIndex const keeperIndex{ -1 }; // the keeper associated to this linda |
59 | Status cancelStatus{ Status::Active }; | 61 | Status cancelStatus{ Status::Active }; |
60 | 62 | ||
@@ -68,7 +70,7 @@ class Linda final | |||
68 | static void operator delete(void* p_) { static_cast<Linda*>(p_)->U->internalAllocator.free(p_, sizeof(Linda)); } | 70 | static void operator delete(void* p_) { static_cast<Linda*>(p_)->U->internalAllocator.free(p_, sizeof(Linda)); } |
69 | 71 | ||
70 | ~Linda(); | 72 | ~Linda(); |
71 | Linda(Universe* U_, LindaGroup group_, std::string_view const& name_); | 73 | Linda(Universe* U_, std::string_view const& name_, lua_Duration wake_period_, LindaGroup group_); |
72 | Linda() = delete; | 74 | Linda() = delete; |
73 | // non-copyable, non-movable | 75 | // non-copyable, non-movable |
74 | Linda(Linda const&) = delete; | 76 | Linda(Linda const&) = delete; |
@@ -92,6 +94,8 @@ class Linda final | |||
92 | [[nodiscard]] | 94 | [[nodiscard]] |
93 | std::string_view getName() const; | 95 | std::string_view getName() const; |
94 | [[nodiscard]] | 96 | [[nodiscard]] |
97 | auto getWakePeriod() const { return wakePeriod; } | ||
98 | [[nodiscard]] | ||
95 | bool inKeeperOperation() const { return keeperOperationCount.load(std::memory_order_seq_cst) != 0; } | 99 | bool inKeeperOperation() const { return keeperOperationCount.load(std::memory_order_seq_cst) != 0; } |
96 | template <typename T = uintptr_t> | 100 | template <typename T = uintptr_t> |
97 | [[nodiscard]] | 101 | [[nodiscard]] |
diff --git a/src/lindafactory.cpp b/src/lindafactory.cpp index 42d0984..4eab0c1 100644 --- a/src/lindafactory.cpp +++ b/src/lindafactory.cpp | |||
@@ -108,9 +108,11 @@ std::string_view LindaFactory::moduleName() const | |||
108 | 108 | ||
109 | DeepPrelude* LindaFactory::newDeepObjectInternal(lua_State* const L_) const | 109 | DeepPrelude* LindaFactory::newDeepObjectInternal(lua_State* const L_) const |
110 | { | 110 | { |
111 | // we always expect name and group at the bottom of the stack (either can be nil). any extra stuff we ignore and keep unmodified | 111 | STACK_CHECK_START_REL(L_, 0); |
112 | // we always expect name, wake_period, group at the bottom of the stack (either can be nil). any extra stuff we ignore and keep unmodified | ||
112 | std::string_view _linda_name{ luaG_tostring(L_, StackIndex{ 1 }) }; | 113 | std::string_view _linda_name{ luaG_tostring(L_, StackIndex{ 1 }) }; |
113 | LindaGroup _linda_group{ static_cast<int>(lua_tointeger(L_, 2)) }; | 114 | auto const _wake_period{ static_cast<lua_Duration>(lua_tonumber(L_, 2)) }; |
115 | LindaGroup const _linda_group{ static_cast<int>(lua_tointeger(L_, 3)) }; | ||
114 | 116 | ||
115 | // store in the linda the location of the script that created it | 117 | // store in the linda the location of the script that created it |
116 | if (_linda_name == "auto") { | 118 | if (_linda_name == "auto") { |
@@ -129,6 +131,7 @@ DeepPrelude* LindaFactory::newDeepObjectInternal(lua_State* const L_) const | |||
129 | // The deep data is allocated separately of Lua stack; we might no longer be around when last reference to it is being released. | 131 | // The deep data is allocated separately of Lua stack; we might no longer be around when last reference to it is being released. |
130 | // One can use any memory allocation scheme. Just don't use L's allocF because we don't know which state will get the honor of GCing the linda | 132 | // One can use any memory allocation scheme. Just don't use L's allocF because we don't know which state will get the honor of GCing the linda |
131 | Universe* const _U{ Universe::Get(L_) }; | 133 | Universe* const _U{ Universe::Get(L_) }; |
132 | Linda* const _linda{ new (_U) Linda{ _U, _linda_group, _linda_name } }; | 134 | Linda* const _linda{ new (_U) Linda{ _U, _linda_name, _wake_period, _linda_group } }; |
135 | STACK_CHECK(L_, 0); | ||
133 | return _linda; | 136 | return _linda; |
134 | } | 137 | } |
diff --git a/src/universe.cpp b/src/universe.cpp index 89ad02a..335f056 100644 --- a/src/universe.cpp +++ b/src/universe.cpp | |||
@@ -153,6 +153,14 @@ Universe* Universe::Create(lua_State* const L_) | |||
153 | lua_setmetatable(L_, -2); // L_: settings universe | 153 | lua_setmetatable(L_, -2); // L_: settings universe |
154 | lua_pop(L_, 1); // L_: settings | 154 | lua_pop(L_, 1); // L_: settings |
155 | 155 | ||
156 | std::ignore = luaG_getfield(L_, kIdxSettings, "linda_wake_period"); // L_: settings linda_wake_period | ||
157 | if (luaG_type(L_, kIdxTop) == LuaType::NUMBER) { | ||
158 | _U->lindaWakePeriod = lua_Duration{ lua_tonumber(L_, kIdxTop) }; | ||
159 | } else { | ||
160 | LUA_ASSERT(L_, luaG_tostring(L_, kIdxTop) == "never"); | ||
161 | } | ||
162 | lua_pop(L_, 1); // L_: settings | ||
163 | |||
156 | std::ignore = luaG_getfield(L_, kIdxSettings, "strip_functions"); // L_: settings strip_functions | 164 | std::ignore = luaG_getfield(L_, kIdxSettings, "strip_functions"); // L_: settings strip_functions |
157 | _U->stripFunctions = lua_toboolean(L_, -1) ? true : false; | 165 | _U->stripFunctions = lua_toboolean(L_, -1) ? true : false; |
158 | lua_pop(L_, 1); // L_: settings | 166 | lua_pop(L_, 1); // L_: settings |
diff --git a/src/universe.hpp b/src/universe.hpp index 42a3d83..0c5e659 100644 --- a/src/universe.hpp +++ b/src/universe.hpp | |||
@@ -99,6 +99,8 @@ class Universe final | |||
99 | 99 | ||
100 | Keepers keepers; | 100 | Keepers keepers; |
101 | 101 | ||
102 | lua_Duration lindaWakePeriod{}; | ||
103 | |||
102 | // Initialized by 'init_once_LOCKED()': the deep userdata Linda object | 104 | // Initialized by 'init_once_LOCKED()': the deep userdata Linda object |
103 | // used for timers (each lane will get a proxy to this) | 105 | // used for timers (each lane will get a proxy to this) |
104 | Linda* timerLinda{ nullptr }; | 106 | Linda* timerLinda{ nullptr }; |