From e3818609b54c81a83aa73beb322ea9c282b7add8 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Tue, 18 Jun 2024 09:03:43 +0200 Subject: When it is a function, config.allocator is called with a string hint --- docs/index.html | 16 ++++++++-------- src/allocator.h | 47 +++++++++++++++++++++++++++++++++++++++++++++++ src/keeper.cpp | 2 +- src/state.cpp | 15 +++++++++------ src/state.h | 2 +- src/universe.h | 47 +---------------------------------------------- 6 files changed, 67 insertions(+), 62 deletions(-) create mode 100644 src/allocator.h diff --git a/docs/index.html b/docs/index.html index c9058fd..fe92565 100644 --- a/docs/index.html +++ b/docs/index.html @@ -289,7 +289,7 @@ If nil, Lua states are created with lua_newstate() and reuse the allocator from the master state.
If "protected", The default allocator obtained from lua_getallocf() in the master state is wrapped inside a critical section and used in all newly created states.
- If a function, this function is called prior to creating the state. It should return a full userdata containing the following structure: + If a function, this function is called prior to creating the state, with a single string argument, either "keeper" or "lane". It should return a full userdata containing the following structure:
@@ -347,7 +347,7 @@ .on_state_create - function/nil + nil/function If provided, will be called in every created Lua state right after initializing the base libraries, with a single string argument, either "lane" or "keeper".
@@ -392,7 +392,7 @@ .strip_functions
- nil/false/true + nil/boolean Controls function bytecode stripping when dumping them for lane transfer. Choose between faster copies or more debug info. Default is true. @@ -403,7 +403,7 @@ .track_lanes - nil/false/anything + nil/boolean Any non-nil|false value instructs Lanes keeps track of all lanes, so that lanes.threads() can list them. If false, lanes.threads() will raise an error when called. @@ -416,7 +416,7 @@ .verbose_errors - nil/false/true + nil/boolean If equal to true, Lanes will collect more information when transfering stuff across Lua states to help identify errors (with a cost). @@ -429,7 +429,7 @@ .with_timers - nil/false/true + nil/boolean If equal to false or nil, Lanes doesn't start the timer service, and the associated API will be absent from the interface (see below). @@ -440,11 +440,11 @@

- Once Lanes is configured, one should register with Lanes the modules exporting functions that will be transferred either during lane generation or through lindas. + Once Lanes is configured, one should register with Lanes the modules exporting functions that will be transferred either during lane generation or through Lindas.
Use lanes.require() for this purpose. This will call the original require(), then add the result to the lookup databases.
- It is also possible to register a given module with lanes.register(). This function will raise an error if the registered module is not a function or table. + It is also possible to register a given module a posteriori with lanes.register(). This function will raise an error if the registered module is not a function or table.

diff --git a/src/allocator.h b/src/allocator.h new file mode 100644 index 0000000..9a9c245 --- /dev/null +++ b/src/allocator.h @@ -0,0 +1,47 @@ +#pragma once + +// ################################################################################################# + +// everything we need to provide to lua_newstate() +class AllocatorDefinition +{ + public: + // xxh64 of string "kAllocatorVersion_1" generated at https://www.pelock.com/products/hash-calculator + static constexpr uintptr_t kAllocatorVersion{ static_cast(0xCF9D321B0DFB5715ull) }; + uintptr_t version{ kAllocatorVersion }; + lua_Alloc allocF{ nullptr }; + void* allocUD{ nullptr }; + + [[nodiscard]] static void* operator new(size_t size_) noexcept = delete; // can't create one outside of a Lua state + [[nodiscard]] static void* operator new(size_t size_, lua_State* L_) noexcept { return lua_newuserdatauv(L_, size_, 0); } + // always embedded somewhere else or "in-place constructed" as a full userdata + // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception + static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] lua_State* L_) {} + + AllocatorDefinition(uintptr_t const version_, lua_Alloc const allocF_, void* const allocUD_) noexcept + : version{ version_ } + , allocF{ allocF_ } + , allocUD{ allocUD_ } + { + } + AllocatorDefinition() = default; + AllocatorDefinition(AllocatorDefinition const& rhs_) = default; + AllocatorDefinition(AllocatorDefinition&& rhs_) = default; + AllocatorDefinition& operator=(AllocatorDefinition const& rhs_) = default; + AllocatorDefinition& operator=(AllocatorDefinition&& rhs_) = default; + + void initFrom(lua_State* L_) + { + allocF = lua_getallocf(L_, &allocUD); + } + + void* alloc(size_t nsize_) + { + return allocF(allocUD, nullptr, 0, nsize_); + } + + void free(void* ptr_, size_t osize_) + { + std::ignore = allocF(allocUD, ptr_, osize_, 0); + } +}; diff --git a/src/keeper.cpp b/src/keeper.cpp index e058114..11f1797 100644 --- a/src/keeper.cpp +++ b/src/keeper.cpp @@ -830,7 +830,7 @@ void Keepers::initialize(Universe& U_, lua_State* L_, int const nbKeepers_, int auto _initOneKeeper = [U = &U_, L = L_, gc_threshold = gc_threshold](Keeper& keeper_, int const i_) { STACK_CHECK_START_REL(L, 0); // note that we will leak K if we raise an error later - KeeperState const _K{ state::CreateState(U, L) }; // L_: settings _K: + KeeperState const _K{ state::CreateState(U, L, "keeper") }; // L_: settings _K: if (_K == nullptr) { raise_luaL_error(L, "out of memory while creating keeper states"); } diff --git a/src/state.cpp b/src/state.cpp index ee0b199..bae2ba5 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -195,24 +195,27 @@ namespace state { // ############################################################################################# - lua_State* CreateState([[maybe_unused]] Universe* const U_, lua_State* const from_) + lua_State* CreateState([[maybe_unused]] Universe* const U_, lua_State* const from_, std::string_view const& hint_) { lua_State* const _L { std::invoke( - [U = U_, from = from_]() { + [U = U_, from = from_, &hint = hint_]() { if constexpr (LUAJIT_FLAVOR() == 64) { // for some reason, LuaJIT 64 bits does not support creating a state with lua_newstate... return luaL_newstate(); } else { if (U->provideAllocator != nullptr) { // we have a function we can call to obtain an allocator - lua_pushcclosure(from, U->provideAllocator, 0); - lua_call(from, 0, 1); + STACK_CHECK_START_REL(from, 0); + lua_pushcclosure(from, U->provideAllocator, 0); // L: provideAllocator() + luaG_pushstring(from, hint); // L: provideAllocator() "" + lua_call(from, 1, 1); // L: result AllocatorDefinition* const _def{ luaG_tofulluserdata(from, -1) }; if (!_def || _def->version != AllocatorDefinition::kAllocatorVersion) { raise_luaL_error(from, "Bad config.allocator function, must provide a valid AllocatorDefinition"); } lua_State* const _L{ lua_newstate(_def->allocF, _def->allocUD) }; - lua_pop(from, 1); + lua_pop(from, 1); // L: + STACK_CHECK(from, 0); return _L; } else { // reuse the allocator provided when the master state was created @@ -271,7 +274,7 @@ namespace state { */ lua_State* NewLaneState(Universe* const U_, SourceState const from_, std::optional const& libs_) { - DestState const _L{ CreateState(U_, from_) }; + DestState const _L{ CreateState(U_, from_, "lane") }; STACK_GROW(_L, 2); STACK_CHECK_START_ABS(_L, 0); diff --git a/src/state.h b/src/state.h index 9b42786..98bb47b 100644 --- a/src/state.h +++ b/src/state.h @@ -10,7 +10,7 @@ class Universe; namespace state { void CallOnStateCreate(Universe* U_, lua_State* L_, lua_State* from_, LookupMode mode_); - [[nodiscard]] lua_State* CreateState(Universe* U_, lua_State* from_); + [[nodiscard]] lua_State* CreateState(Universe* U_, lua_State* from_, std::string_view const& hint_); void InitializeOnStateCreate(Universe* U_, lua_State* L_); [[nodiscard]] lua_State* NewLaneState(Universe* U_, SourceState from_, std::optional const& libs_); diff --git a/src/universe.h b/src/universe.h index 3742ddd..2edd825 100644 --- a/src/universe.h +++ b/src/universe.h @@ -1,5 +1,6 @@ #pragma once +#include "allocator.h" #include "keeper.h" #include "lanesconf.h" #include "tracker.h" @@ -14,52 +15,6 @@ class Lane; // ################################################################################################# -// everything we need to provide to lua_newstate() -class AllocatorDefinition -{ - public: - // xxh64 of string "kAllocatorVersion_1" generated at https://www.pelock.com/products/hash-calculator - static constexpr uintptr_t kAllocatorVersion{ static_cast(0xCF9D321B0DFB5715ull) }; - uintptr_t version{ kAllocatorVersion }; - lua_Alloc allocF{ nullptr }; - void* allocUD{ nullptr }; - - [[nodiscard]] static void* operator new(size_t size_) noexcept = delete; // can't create one outside of a Lua state - [[nodiscard]] static void* operator new(size_t size_, lua_State* L_) noexcept { return lua_newuserdatauv(L_, size_, 0); } - // always embedded somewhere else or "in-place constructed" as a full userdata - // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception - static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] lua_State* L_) { LUA_ASSERT(L_, !"should never be called"); } - - AllocatorDefinition(uintptr_t const version_, lua_Alloc const allocF_, void* const allocUD_) noexcept - : version{ version_ } - , allocF{ allocF_ } - , allocUD{ allocUD_ } - { - } - AllocatorDefinition() = default; - AllocatorDefinition(AllocatorDefinition const& rhs_) = default; - AllocatorDefinition(AllocatorDefinition&& rhs_) = default; - AllocatorDefinition& operator=(AllocatorDefinition const& rhs_) = default; - AllocatorDefinition& operator=(AllocatorDefinition&& rhs_) = default; - - void initFrom(lua_State* L_) - { - allocF = lua_getallocf(L_, &allocUD); - } - - void* alloc(size_t nsize_) - { - return allocF(allocUD, nullptr, 0, nsize_); - } - - void free(void* ptr_, size_t osize_) - { - std::ignore = allocF(allocUD, ptr_, osize_, 0); - } -}; - -// ################################################################################################# - // mutex-protected allocator for use with Lua states that share a non-threadsafe allocator class ProtectedAllocator : public AllocatorDefinition -- cgit v1.2.3-55-g6feb