diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-18 09:03:43 +0200 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-18 09:03:43 +0200 |
| commit | e3818609b54c81a83aa73beb322ea9c282b7add8 (patch) | |
| tree | b165d7925206ceb10a851fc8c1eed8b8566e7727 | |
| parent | 54b47307ce4b2e21bc12c1602c77fecf55380452 (diff) | |
| download | lanes-e3818609b54c81a83aa73beb322ea9c282b7add8.tar.gz lanes-e3818609b54c81a83aa73beb322ea9c282b7add8.tar.bz2 lanes-e3818609b54c81a83aa73beb322ea9c282b7add8.zip | |
When it is a function, config.allocator is called with a string hint
| -rw-r--r-- | docs/index.html | 16 | ||||
| -rw-r--r-- | src/allocator.h | 47 | ||||
| -rw-r--r-- | src/keeper.cpp | 2 | ||||
| -rw-r--r-- | src/state.cpp | 15 | ||||
| -rw-r--r-- | src/state.h | 2 | ||||
| -rw-r--r-- | src/universe.h | 47 |
6 files changed, 67 insertions, 62 deletions
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 @@ | |||
| 289 | <td> | 289 | <td> |
| 290 | If <tt>nil</tt>, Lua states are created with <tt>lua_newstate()</tt> and reuse the allocator from the master state.<br /> | 290 | If <tt>nil</tt>, Lua states are created with <tt>lua_newstate()</tt> and reuse the allocator from the master state.<br /> |
| 291 | If <tt>"protected"</tt>, The default allocator obtained from <tt>lua_getallocf()</tt> in the master state is wrapped inside a critical section and used in all newly created states.<br /> | 291 | If <tt>"protected"</tt>, The default allocator obtained from <tt>lua_getallocf()</tt> in the master state is wrapped inside a critical section and used in all newly created states.<br /> |
| 292 | If a <tt>function</tt>, this function is called prior to creating the state. It should return a full userdata containing the following structure: | 292 | If a <tt>function</tt>, this function is called prior to creating the state, with a single string argument, either <tt>"keeper"</tt> or <tt>"lane"</tt>. It should return a full userdata containing the following structure: |
| 293 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"> | 293 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"> |
| 294 | <tr> | 294 | <tr> |
| 295 | <td> | 295 | <td> |
| @@ -347,7 +347,7 @@ | |||
| 347 | <code>.on_state_create</code> | 347 | <code>.on_state_create</code> |
| 348 | </td> | 348 | </td> |
| 349 | <td> | 349 | <td> |
| 350 | function/<tt>nil</tt> | 350 | <tt>nil</tt>/function |
| 351 | </td> | 351 | </td> |
| 352 | <td> | 352 | <td> |
| 353 | If provided, will be called in every created Lua state right after initializing the base libraries, with a single string argument, either <tt>"lane"</tt> or <tt>"keeper"</tt>.<br /> | 353 | If provided, will be called in every created Lua state right after initializing the base libraries, with a single string argument, either <tt>"lane"</tt> or <tt>"keeper"</tt>.<br /> |
| @@ -392,7 +392,7 @@ | |||
| 392 | <code>.strip_functions</code> | 392 | <code>.strip_functions</code> |
| 393 | </td> | 393 | </td> |
| 394 | <td> | 394 | <td> |
| 395 | <tt>nil</tt>/<tt>false</tt>/<tt>true</tt> | 395 | <tt>nil</tt>/<tt>boolean</tt> |
| 396 | </td> | 396 | </td> |
| 397 | <td> | 397 | <td> |
| 398 | Controls function bytecode stripping when dumping them for lane transfer. Choose between faster copies or more debug info. Default is <tt>true</tt>. | 398 | Controls function bytecode stripping when dumping them for lane transfer. Choose between faster copies or more debug info. Default is <tt>true</tt>. |
| @@ -403,7 +403,7 @@ | |||
| 403 | <code>.track_lanes</code> | 403 | <code>.track_lanes</code> |
| 404 | </td> | 404 | </td> |
| 405 | <td> | 405 | <td> |
| 406 | <tt>nil</tt>/<tt>false</tt>/anything | 406 | <tt>nil</tt>/<tt>boolean</tt> |
| 407 | </td> | 407 | </td> |
| 408 | <td> | 408 | <td> |
| 409 | Any non-<tt>nil|false</tt> value instructs Lanes keeps track of all lanes, so that <a href="#tracking"><tt>lanes.threads()</tt></a> can list them. If <tt>false</tt>, <tt>lanes.threads()</tt> will raise an error when called. | 409 | Any non-<tt>nil|false</tt> value instructs Lanes keeps track of all lanes, so that <a href="#tracking"><tt>lanes.threads()</tt></a> can list them. If <tt>false</tt>, <tt>lanes.threads()</tt> will raise an error when called. |
| @@ -416,7 +416,7 @@ | |||
| 416 | <code>.verbose_errors</code> | 416 | <code>.verbose_errors</code> |
| 417 | </td> | 417 | </td> |
| 418 | <td> | 418 | <td> |
| 419 | <tt>nil</tt>/<tt>false</tt>/<tt>true</tt> | 419 | <tt>nil</tt>/<tt>boolean</tt> |
| 420 | </td> | 420 | </td> |
| 421 | <td> | 421 | <td> |
| 422 | If equal to <tt>true</tt>, Lanes will collect more information when transfering stuff across Lua states to help identify errors (with a cost). | 422 | If equal to <tt>true</tt>, Lanes will collect more information when transfering stuff across Lua states to help identify errors (with a cost). |
| @@ -429,7 +429,7 @@ | |||
| 429 | <code>.with_timers</code> | 429 | <code>.with_timers</code> |
| 430 | </td> | 430 | </td> |
| 431 | <td> | 431 | <td> |
| 432 | <tt>nil</tt>/<tt>false</tt>/<tt>true</tt> | 432 | <tt>nil</tt>/<tt>boolean</tt> |
| 433 | </td> | 433 | </td> |
| 434 | <td> | 434 | <td> |
| 435 | If equal to <tt>false</tt> or <tt>nil</tt>, Lanes doesn't start the timer service, and the associated API will be absent from the interface (see below). | 435 | If equal to <tt>false</tt> or <tt>nil</tt>, Lanes doesn't start the timer service, and the associated API will be absent from the interface (see below). |
| @@ -440,11 +440,11 @@ | |||
| 440 | </p> | 440 | </p> |
| 441 | 441 | ||
| 442 | <p> | 442 | <p> |
| 443 | Once Lanes is configured, one should register with Lanes the modules exporting functions that will be transferred either during lane generation or through <a href="#lindas">lindas</a>. | 443 | Once Lanes is configured, one should register with Lanes the modules exporting functions that will be transferred either during lane generation or through <a href="#lindas">Lindas</a>. |
| 444 | <br /> | 444 | <br /> |
| 445 | Use <tt>lanes.require()</tt> for this purpose. This will call the original <tt>require()</tt>, then add the result to the lookup databases. | 445 | Use <tt>lanes.require()</tt> for this purpose. This will call the original <tt>require()</tt>, then add the result to the lookup databases. |
| 446 | <br /> | 446 | <br /> |
| 447 | It is also possible to register a given module with <tt>lanes.register()</tt>. This function will raise an error if the registered module is not a function or table. | 447 | It is also possible to register a given module <i>a posteriori</i> with <tt>lanes.register()</tt>. This function will raise an error if the registered module is not a function or table. |
| 448 | </p> | 448 | </p> |
| 449 | 449 | ||
| 450 | <table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"> | 450 | <table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"> |
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 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | // ################################################################################################# | ||
| 4 | |||
| 5 | // everything we need to provide to lua_newstate() | ||
| 6 | class AllocatorDefinition | ||
| 7 | { | ||
| 8 | public: | ||
| 9 | // xxh64 of string "kAllocatorVersion_1" generated at https://www.pelock.com/products/hash-calculator | ||
| 10 | static constexpr uintptr_t kAllocatorVersion{ static_cast<uintptr_t>(0xCF9D321B0DFB5715ull) }; | ||
| 11 | uintptr_t version{ kAllocatorVersion }; | ||
| 12 | lua_Alloc allocF{ nullptr }; | ||
| 13 | void* allocUD{ nullptr }; | ||
| 14 | |||
| 15 | [[nodiscard]] static void* operator new(size_t size_) noexcept = delete; // can't create one outside of a Lua state | ||
| 16 | [[nodiscard]] static void* operator new(size_t size_, lua_State* L_) noexcept { return lua_newuserdatauv(L_, size_, 0); } | ||
| 17 | // always embedded somewhere else or "in-place constructed" as a full userdata | ||
| 18 | // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception | ||
| 19 | static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] lua_State* L_) {} | ||
| 20 | |||
| 21 | AllocatorDefinition(uintptr_t const version_, lua_Alloc const allocF_, void* const allocUD_) noexcept | ||
| 22 | : version{ version_ } | ||
| 23 | , allocF{ allocF_ } | ||
| 24 | , allocUD{ allocUD_ } | ||
| 25 | { | ||
| 26 | } | ||
| 27 | AllocatorDefinition() = default; | ||
| 28 | AllocatorDefinition(AllocatorDefinition const& rhs_) = default; | ||
| 29 | AllocatorDefinition(AllocatorDefinition&& rhs_) = default; | ||
| 30 | AllocatorDefinition& operator=(AllocatorDefinition const& rhs_) = default; | ||
| 31 | AllocatorDefinition& operator=(AllocatorDefinition&& rhs_) = default; | ||
| 32 | |||
| 33 | void initFrom(lua_State* L_) | ||
| 34 | { | ||
| 35 | allocF = lua_getallocf(L_, &allocUD); | ||
| 36 | } | ||
| 37 | |||
| 38 | void* alloc(size_t nsize_) | ||
| 39 | { | ||
| 40 | return allocF(allocUD, nullptr, 0, nsize_); | ||
| 41 | } | ||
| 42 | |||
| 43 | void free(void* ptr_, size_t osize_) | ||
| 44 | { | ||
| 45 | std::ignore = allocF(allocUD, ptr_, osize_, 0); | ||
| 46 | } | ||
| 47 | }; | ||
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 | |||
| 830 | auto _initOneKeeper = [U = &U_, L = L_, gc_threshold = gc_threshold](Keeper& keeper_, int const i_) { | 830 | auto _initOneKeeper = [U = &U_, L = L_, gc_threshold = gc_threshold](Keeper& keeper_, int const i_) { |
| 831 | STACK_CHECK_START_REL(L, 0); | 831 | STACK_CHECK_START_REL(L, 0); |
| 832 | // note that we will leak K if we raise an error later | 832 | // note that we will leak K if we raise an error later |
| 833 | KeeperState const _K{ state::CreateState(U, L) }; // L_: settings _K: | 833 | KeeperState const _K{ state::CreateState(U, L, "keeper") }; // L_: settings _K: |
| 834 | if (_K == nullptr) { | 834 | if (_K == nullptr) { |
| 835 | raise_luaL_error(L, "out of memory while creating keeper states"); | 835 | raise_luaL_error(L, "out of memory while creating keeper states"); |
| 836 | } | 836 | } |
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 { | |||
| 195 | 195 | ||
| 196 | // ############################################################################################# | 196 | // ############################################################################################# |
| 197 | 197 | ||
| 198 | lua_State* CreateState([[maybe_unused]] Universe* const U_, lua_State* const from_) | 198 | lua_State* CreateState([[maybe_unused]] Universe* const U_, lua_State* const from_, std::string_view const& hint_) |
| 199 | { | 199 | { |
| 200 | lua_State* const _L { | 200 | lua_State* const _L { |
| 201 | std::invoke( | 201 | std::invoke( |
| 202 | [U = U_, from = from_]() { | 202 | [U = U_, from = from_, &hint = hint_]() { |
| 203 | if constexpr (LUAJIT_FLAVOR() == 64) { | 203 | if constexpr (LUAJIT_FLAVOR() == 64) { |
| 204 | // for some reason, LuaJIT 64 bits does not support creating a state with lua_newstate... | 204 | // for some reason, LuaJIT 64 bits does not support creating a state with lua_newstate... |
| 205 | return luaL_newstate(); | 205 | return luaL_newstate(); |
| 206 | } else { | 206 | } else { |
| 207 | if (U->provideAllocator != nullptr) { // we have a function we can call to obtain an allocator | 207 | if (U->provideAllocator != nullptr) { // we have a function we can call to obtain an allocator |
| 208 | lua_pushcclosure(from, U->provideAllocator, 0); | 208 | STACK_CHECK_START_REL(from, 0); |
| 209 | lua_call(from, 0, 1); | 209 | lua_pushcclosure(from, U->provideAllocator, 0); // L: provideAllocator() |
| 210 | luaG_pushstring(from, hint); // L: provideAllocator() "<hint>" | ||
| 211 | lua_call(from, 1, 1); // L: result | ||
| 210 | AllocatorDefinition* const _def{ luaG_tofulluserdata<AllocatorDefinition>(from, -1) }; | 212 | AllocatorDefinition* const _def{ luaG_tofulluserdata<AllocatorDefinition>(from, -1) }; |
| 211 | if (!_def || _def->version != AllocatorDefinition::kAllocatorVersion) { | 213 | if (!_def || _def->version != AllocatorDefinition::kAllocatorVersion) { |
| 212 | raise_luaL_error(from, "Bad config.allocator function, must provide a valid AllocatorDefinition"); | 214 | raise_luaL_error(from, "Bad config.allocator function, must provide a valid AllocatorDefinition"); |
| 213 | } | 215 | } |
| 214 | lua_State* const _L{ lua_newstate(_def->allocF, _def->allocUD) }; | 216 | lua_State* const _L{ lua_newstate(_def->allocF, _def->allocUD) }; |
| 215 | lua_pop(from, 1); | 217 | lua_pop(from, 1); // L: |
| 218 | STACK_CHECK(from, 0); | ||
| 216 | return _L; | 219 | return _L; |
| 217 | } else { | 220 | } else { |
| 218 | // reuse the allocator provided when the master state was created | 221 | // reuse the allocator provided when the master state was created |
| @@ -271,7 +274,7 @@ namespace state { | |||
| 271 | */ | 274 | */ |
| 272 | lua_State* NewLaneState(Universe* const U_, SourceState const from_, std::optional<std::string_view> const& libs_) | 275 | lua_State* NewLaneState(Universe* const U_, SourceState const from_, std::optional<std::string_view> const& libs_) |
| 273 | { | 276 | { |
| 274 | DestState const _L{ CreateState(U_, from_) }; | 277 | DestState const _L{ CreateState(U_, from_, "lane") }; |
| 275 | 278 | ||
| 276 | STACK_GROW(_L, 2); | 279 | STACK_GROW(_L, 2); |
| 277 | STACK_CHECK_START_ABS(_L, 0); | 280 | 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; | |||
| 10 | namespace state { | 10 | namespace state { |
| 11 | 11 | ||
| 12 | void CallOnStateCreate(Universe* U_, lua_State* L_, lua_State* from_, LookupMode mode_); | 12 | void CallOnStateCreate(Universe* U_, lua_State* L_, lua_State* from_, LookupMode mode_); |
| 13 | [[nodiscard]] lua_State* CreateState(Universe* U_, lua_State* from_); | 13 | [[nodiscard]] lua_State* CreateState(Universe* U_, lua_State* from_, std::string_view const& hint_); |
| 14 | void InitializeOnStateCreate(Universe* U_, lua_State* L_); | 14 | void InitializeOnStateCreate(Universe* U_, lua_State* L_); |
| 15 | [[nodiscard]] lua_State* NewLaneState(Universe* U_, SourceState from_, std::optional<std::string_view> const& libs_); | 15 | [[nodiscard]] lua_State* NewLaneState(Universe* U_, SourceState from_, std::optional<std::string_view> const& libs_); |
| 16 | 16 | ||
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 @@ | |||
| 1 | #pragma once | 1 | #pragma once |
| 2 | 2 | ||
| 3 | #include "allocator.h" | ||
| 3 | #include "keeper.h" | 4 | #include "keeper.h" |
| 4 | #include "lanesconf.h" | 5 | #include "lanesconf.h" |
| 5 | #include "tracker.h" | 6 | #include "tracker.h" |
| @@ -14,52 +15,6 @@ class Lane; | |||
| 14 | 15 | ||
| 15 | // ################################################################################################# | 16 | // ################################################################################################# |
| 16 | 17 | ||
| 17 | // everything we need to provide to lua_newstate() | ||
| 18 | class AllocatorDefinition | ||
| 19 | { | ||
| 20 | public: | ||
| 21 | // xxh64 of string "kAllocatorVersion_1" generated at https://www.pelock.com/products/hash-calculator | ||
| 22 | static constexpr uintptr_t kAllocatorVersion{ static_cast<uintptr_t>(0xCF9D321B0DFB5715ull) }; | ||
| 23 | uintptr_t version{ kAllocatorVersion }; | ||
| 24 | lua_Alloc allocF{ nullptr }; | ||
| 25 | void* allocUD{ nullptr }; | ||
| 26 | |||
| 27 | [[nodiscard]] static void* operator new(size_t size_) noexcept = delete; // can't create one outside of a Lua state | ||
| 28 | [[nodiscard]] static void* operator new(size_t size_, lua_State* L_) noexcept { return lua_newuserdatauv(L_, size_, 0); } | ||
| 29 | // always embedded somewhere else or "in-place constructed" as a full userdata | ||
| 30 | // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception | ||
| 31 | static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] lua_State* L_) { LUA_ASSERT(L_, !"should never be called"); } | ||
| 32 | |||
| 33 | AllocatorDefinition(uintptr_t const version_, lua_Alloc const allocF_, void* const allocUD_) noexcept | ||
| 34 | : version{ version_ } | ||
| 35 | , allocF{ allocF_ } | ||
| 36 | , allocUD{ allocUD_ } | ||
| 37 | { | ||
| 38 | } | ||
| 39 | AllocatorDefinition() = default; | ||
| 40 | AllocatorDefinition(AllocatorDefinition const& rhs_) = default; | ||
| 41 | AllocatorDefinition(AllocatorDefinition&& rhs_) = default; | ||
| 42 | AllocatorDefinition& operator=(AllocatorDefinition const& rhs_) = default; | ||
| 43 | AllocatorDefinition& operator=(AllocatorDefinition&& rhs_) = default; | ||
| 44 | |||
| 45 | void initFrom(lua_State* L_) | ||
| 46 | { | ||
| 47 | allocF = lua_getallocf(L_, &allocUD); | ||
| 48 | } | ||
| 49 | |||
| 50 | void* alloc(size_t nsize_) | ||
| 51 | { | ||
| 52 | return allocF(allocUD, nullptr, 0, nsize_); | ||
| 53 | } | ||
| 54 | |||
| 55 | void free(void* ptr_, size_t osize_) | ||
| 56 | { | ||
| 57 | std::ignore = allocF(allocUD, ptr_, osize_, 0); | ||
| 58 | } | ||
| 59 | }; | ||
| 60 | |||
| 61 | // ################################################################################################# | ||
| 62 | |||
| 63 | // mutex-protected allocator for use with Lua states that share a non-threadsafe allocator | 18 | // mutex-protected allocator for use with Lua states that share a non-threadsafe allocator |
| 64 | class ProtectedAllocator | 19 | class ProtectedAllocator |
| 65 | : public AllocatorDefinition | 20 | : public AllocatorDefinition |
