aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-06-18 09:03:43 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-06-18 09:03:43 +0200
commite3818609b54c81a83aa73beb322ea9c282b7add8 (patch)
treeb165d7925206ceb10a851fc8c1eed8b8566e7727
parent54b47307ce4b2e21bc12c1602c77fecf55380452 (diff)
downloadlanes-e3818609b54c81a83aa73beb322ea9c282b7add8.tar.gz
lanes-e3818609b54c81a83aa73beb322ea9c282b7add8.tar.bz2
lanes-e3818609b54c81a83aa73beb322ea9c282b7add8.zip
When it is a function, config.allocator is called with a string hint
Diffstat (limited to '')
-rw-r--r--docs/index.html16
-rw-r--r--src/allocator.h47
-rw-r--r--src/keeper.cpp2
-rw-r--r--src/state.cpp15
-rw-r--r--src/state.h2
-rw-r--r--src/universe.h47
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()
6class 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;
10namespace state { 10namespace 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()
18class 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
64class ProtectedAllocator 19class ProtectedAllocator
65: public AllocatorDefinition 20: public AllocatorDefinition