diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 2 | ||||
-rw-r--r-- | src/allocator.cpp | 51 | ||||
-rw-r--r-- | src/allocator.hpp | 39 | ||||
-rw-r--r-- | src/state.cpp | 2 | ||||
-rw-r--r-- | src/universe.cpp | 9 | ||||
-rw-r--r-- | src/universe.hpp | 21 |
6 files changed, 100 insertions, 24 deletions
diff --git a/src/Makefile b/src/Makefile index 5fc3482..f2e2eb5 100644 --- a/src/Makefile +++ b/src/Makefile | |||
@@ -9,7 +9,7 @@ MODULE=lanes | |||
9 | 9 | ||
10 | CC= g++ -std=c++20 | 10 | CC= g++ -std=c++20 |
11 | 11 | ||
12 | SRC=_pch.cpp cancel.cpp compat.cpp deep.cpp intercopycontext.cpp keeper.cpp lane.cpp lanes.cpp linda.cpp lindafactory.cpp nameof.cpp state.cpp threading.cpp tools.cpp tracker.cpp universe.cpp | 12 | SRC=_pch.cpp allocator.cpp cancel.cpp compat.cpp deep.cpp intercopycontext.cpp keeper.cpp lane.cpp lanes.cpp linda.cpp lindafactory.cpp nameof.cpp state.cpp threading.cpp tools.cpp tracker.cpp universe.cpp |
13 | 13 | ||
14 | OBJ=$(SRC:.cpp=.o) | 14 | OBJ=$(SRC:.cpp=.o) |
15 | 15 | ||
diff --git a/src/allocator.cpp b/src/allocator.cpp new file mode 100644 index 0000000..84acde5 --- /dev/null +++ b/src/allocator.cpp | |||
@@ -0,0 +1,51 @@ | |||
1 | /* | ||
2 | * ALLOCATOR.CPP Copyright (c) 2017-2024, Benoit Germain | ||
3 | */ | ||
4 | |||
5 | /* | ||
6 | =============================================================================== | ||
7 | |||
8 | Copyright (C) 2017-2024 Benoit Germain <bnt.germain@gmail.com> | ||
9 | |||
10 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
11 | of this software and associated documentation files (the "Software"), to deal | ||
12 | in the Software without restriction, including without limitation the rights | ||
13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
14 | copies of the Software, and to permit persons to whom the Software is | ||
15 | furnished to do so, subject to the following conditions: | ||
16 | |||
17 | The above copyright notice and this permission notice shall be included in | ||
18 | all copies or substantial portions of the Software. | ||
19 | |||
20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
26 | THE SOFTWARE. | ||
27 | |||
28 | =============================================================================== | ||
29 | */ | ||
30 | |||
31 | #include "_pch.hpp" | ||
32 | #include "allocator.hpp" | ||
33 | |||
34 | namespace lanes | ||
35 | { | ||
36 | AllocatorDefinition& AllocatorDefinition::Validated(lua_State* const L_, StackIndex const idx_) | ||
37 | { | ||
38 | lanes::AllocatorDefinition* const _def{ luaG_tofulluserdata<lanes::AllocatorDefinition>(L_, idx_) }; | ||
39 | // raise an error and don't return if the full userdata at the specified index is not a valid AllocatorDefinition | ||
40 | if (!_def) { | ||
41 | raise_luaL_error(L_, "Bad config.allocator function, provided value is not a userdata"); | ||
42 | } | ||
43 | if (lua_rawlen(L_, idx_) < sizeof(lanes::AllocatorDefinition)) { | ||
44 | raise_luaL_error(L_, "Bad config.allocator function, provided value is too small to contain a valid AllocatorDefinition"); | ||
45 | } | ||
46 | if (_def->version != kAllocatorVersion) { | ||
47 | raise_luaL_error(L_, "Bad config.allocator function, AllocatorDefinition version mismatch"); | ||
48 | } | ||
49 | return *_def; | ||
50 | } | ||
51 | } | ||
diff --git a/src/allocator.hpp b/src/allocator.hpp index fa48c46..4fec044 100644 --- a/src/allocator.hpp +++ b/src/allocator.hpp | |||
@@ -9,22 +9,30 @@ namespace lanes { | |||
9 | // everything we need to provide to lua_newstate() | 9 | // everything we need to provide to lua_newstate() |
10 | class AllocatorDefinition | 10 | class AllocatorDefinition |
11 | { | 11 | { |
12 | public: | 12 | private: |
13 | // xxh64 of string "kAllocatorVersion_1" generated at https://www.pelock.com/products/hash-calculator | 13 | // xxh64 of string "kAllocatorVersion_1" generated at https://www.pelock.com/products/hash-calculator |
14 | static constexpr uintptr_t kAllocatorVersion{ static_cast<uintptr_t>(0xCF9D321B0DFB5715ull) }; | 14 | static constexpr auto kAllocatorVersion{ static_cast<uintptr_t>(0xCF9D321B0DFB5715ull) }; |
15 | uintptr_t version{ kAllocatorVersion }; | 15 | |
16 | public: | ||
17 | using version_t = std::remove_const_t<decltype(kAllocatorVersion)>; | ||
18 | |||
19 | private: | ||
20 | // can't make these members const because we need to be able to copy an AllocatorDefinition into another | ||
21 | // so the members are not const, but they are private to avoid accidents. | ||
22 | version_t version{ kAllocatorVersion }; | ||
16 | lua_Alloc allocF{ nullptr }; | 23 | lua_Alloc allocF{ nullptr }; |
17 | void* allocUD{ nullptr }; | 24 | void* allocUD{ nullptr }; |
18 | 25 | ||
26 | public: | ||
27 | |||
19 | [[nodiscard]] static void* operator new(size_t const size_) noexcept = delete; // can't create one outside of a Lua state | 28 | [[nodiscard]] static void* operator new(size_t const size_) noexcept = delete; // can't create one outside of a Lua state |
20 | [[nodiscard]] static void* operator new(size_t const size_, lua_State* const L_) noexcept { return lua_newuserdatauv(L_, size_, UserValueCount{ 0 }); } | 29 | [[nodiscard]] static void* operator new(size_t const size_, lua_State* const L_) noexcept { return lua_newuserdatauv(L_, size_, UserValueCount{ 0 }); } |
21 | // always embedded somewhere else or "in-place constructed" as a full userdata | 30 | // always embedded somewhere else or "in-place constructed" as a full userdata |
22 | // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception | 31 | // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception |
23 | static void operator delete([[maybe_unused]] void* const p_, [[maybe_unused]] lua_State* const L_) {} | 32 | static void operator delete([[maybe_unused]] void* const p_, [[maybe_unused]] lua_State* const L_) {} |
24 | 33 | ||
25 | AllocatorDefinition(uintptr_t const version_, lua_Alloc const allocF_, void* const allocUD_) noexcept | 34 | AllocatorDefinition(lua_Alloc const allocF_, void* const allocUD_) noexcept |
26 | : version{ version_ } | 35 | : allocF{ allocF_ } |
27 | , allocF{ allocF_ } | ||
28 | , allocUD{ allocUD_ } | 36 | , allocUD{ allocUD_ } |
29 | { | 37 | { |
30 | } | 38 | } |
@@ -36,16 +44,35 @@ namespace lanes { | |||
36 | AllocatorDefinition& operator=(AllocatorDefinition const& rhs_) = default; | 44 | AllocatorDefinition& operator=(AllocatorDefinition const& rhs_) = default; |
37 | AllocatorDefinition& operator=(AllocatorDefinition&& rhs_) = default; | 45 | AllocatorDefinition& operator=(AllocatorDefinition&& rhs_) = default; |
38 | 46 | ||
47 | static AllocatorDefinition& Validated(lua_State* L_, StackIndex idx_); | ||
48 | |||
39 | void initFrom(lua_State* const L_) | 49 | void initFrom(lua_State* const L_) |
40 | { | 50 | { |
41 | allocF = lua_getallocf(L_, &allocUD); | 51 | allocF = lua_getallocf(L_, &allocUD); |
42 | } | 52 | } |
43 | 53 | ||
54 | void installIn(lua_State* const L_) const | ||
55 | { | ||
56 | if (allocF) { | ||
57 | lua_setallocf(L_, allocF, allocUD); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | lua_State* newState() const | ||
62 | { | ||
63 | return lua_newstate(allocF, allocUD); | ||
64 | } | ||
65 | |||
44 | void* alloc(size_t const nsize_) | 66 | void* alloc(size_t const nsize_) |
45 | { | 67 | { |
46 | return allocF(allocUD, nullptr, 0, nsize_); | 68 | return allocF(allocUD, nullptr, 0, nsize_); |
47 | } | 69 | } |
48 | 70 | ||
71 | void* alloc(void* const ptr_, size_t const osize_, size_t const nsize_) | ||
72 | { | ||
73 | return allocF(allocUD, ptr_, osize_, nsize_); | ||
74 | } | ||
75 | |||
49 | void free(void* const ptr_, size_t const osize_) | 76 | void free(void* const ptr_, size_t const osize_) |
50 | { | 77 | { |
51 | std::ignore = allocF(allocUD, ptr_, osize_, 0); | 78 | std::ignore = allocF(allocUD, ptr_, osize_, 0); |
diff --git a/src/state.cpp b/src/state.cpp index 6c49694..88f406a 100644 --- a/src/state.cpp +++ b/src/state.cpp | |||
@@ -159,7 +159,7 @@ namespace state { | |||
159 | return luaL_newstate(); | 159 | return luaL_newstate(); |
160 | } else { | 160 | } else { |
161 | lanes::AllocatorDefinition const _def{ U->resolveAllocator(from, hint) }; | 161 | lanes::AllocatorDefinition const _def{ U->resolveAllocator(from, hint) }; |
162 | return lua_newstate(_def.allocF, _def.allocUD); | 162 | return _def.newState(); |
163 | } | 163 | } |
164 | } | 164 | } |
165 | ) | 165 | ) |
diff --git a/src/universe.cpp b/src/universe.cpp index 357aa08..283747f 100644 --- a/src/universe.cpp +++ b/src/universe.cpp | |||
@@ -263,7 +263,7 @@ void Universe::initializeAllocatorFunction(lua_State* const L_) | |||
263 | LUA_ASSERT(L_, lua_isstring(L_, kIdxTop)); // should be the case due to lanes.lua parameter validation | 263 | LUA_ASSERT(L_, lua_isstring(L_, kIdxTop)); // should be the case due to lanes.lua parameter validation |
264 | std::string_view const _allocator{ luaG_tostring(L_, kIdxTop) }; | 264 | std::string_view const _allocator{ luaG_tostring(L_, kIdxTop) }; |
265 | if (_allocator == "libc") { | 265 | if (_allocator == "libc") { |
266 | internalAllocator = lanes::AllocatorDefinition{ lanes::AllocatorDefinition::kAllocatorVersion, libc_lua_Alloc, nullptr }; | 266 | internalAllocator = lanes::AllocatorDefinition{ libc_lua_Alloc, nullptr }; |
267 | } else { | 267 | } else { |
268 | // use whatever the provider provides | 268 | // use whatever the provider provides |
269 | internalAllocator = resolveAllocator(L_, "internal"); | 269 | internalAllocator = resolveAllocator(L_, "internal"); |
@@ -338,11 +338,8 @@ lanes::AllocatorDefinition Universe::resolveAllocator(lua_State* const L_, std:: | |||
338 | lua_pushcclosure(L_, provideAllocator, 0); // L_: provideAllocator() | 338 | lua_pushcclosure(L_, provideAllocator, 0); // L_: provideAllocator() |
339 | luaG_pushstring(L_, hint_); // L_: provideAllocator() "<hint>" | 339 | luaG_pushstring(L_, hint_); // L_: provideAllocator() "<hint>" |
340 | lua_call(L_, 1, 1); // L_: result | 340 | lua_call(L_, 1, 1); // L_: result |
341 | lanes::AllocatorDefinition* const _def{ luaG_tofulluserdata<lanes::AllocatorDefinition>(L_, kIdxTop) }; | 341 | // make sure we have a valid AllocatorDefinition on the stack (an error is raised instead if it is not the case) |
342 | if (!_def || _def->version != lanes::AllocatorDefinition::kAllocatorVersion) { | 342 | _ret = lanes::AllocatorDefinition::Validated(L_, kIdxTop); |
343 | raise_luaL_error(L_, "Bad config.allocator function, must provide a valid AllocatorDefinition"); | ||
344 | } | ||
345 | _ret = *_def; | ||
346 | lua_pop(L_, 1); // L_: | 343 | lua_pop(L_, 1); // L_: |
347 | STACK_CHECK(L_, 0); | 344 | STACK_CHECK(L_, 0); |
348 | return _ret; | 345 | return _ret; |
diff --git a/src/universe.hpp b/src/universe.hpp index a45ce86..77fbb52 100644 --- a/src/universe.hpp +++ b/src/universe.hpp | |||
@@ -20,13 +20,16 @@ class ProtectedAllocator | |||
20 | : public lanes::AllocatorDefinition | 20 | : public lanes::AllocatorDefinition |
21 | { | 21 | { |
22 | private: | 22 | private: |
23 | |||
24 | using super = lanes::AllocatorDefinition; | ||
25 | |||
23 | std::mutex mutex; | 26 | std::mutex mutex; |
24 | 27 | ||
25 | [[nodiscard]] static void* protected_lua_Alloc(void* ud_, void* ptr_, size_t osize_, size_t nsize_) | 28 | [[nodiscard]] static void* protected_lua_Alloc(void* ud_, void* ptr_, size_t osize_, size_t nsize_) |
26 | { | 29 | { |
27 | ProtectedAllocator* const allocator{ static_cast<ProtectedAllocator*>(ud_) }; | 30 | ProtectedAllocator* const allocator{ static_cast<ProtectedAllocator*>(ud_) }; |
28 | std::lock_guard<std::mutex> guard{ allocator->mutex }; | 31 | std::lock_guard<std::mutex> guard{ allocator->mutex }; |
29 | return allocator->allocF(allocator->allocUD, ptr_, osize_, nsize_); | 32 | return allocator->alloc(ptr_, osize_, nsize_); |
30 | } | 33 | } |
31 | 34 | ||
32 | public: | 35 | public: |
@@ -36,21 +39,19 @@ class ProtectedAllocator | |||
36 | 39 | ||
37 | AllocatorDefinition makeDefinition() | 40 | AllocatorDefinition makeDefinition() |
38 | { | 41 | { |
39 | return AllocatorDefinition{ version, protected_lua_Alloc, this }; | 42 | return AllocatorDefinition{ protected_lua_Alloc, this }; |
40 | } | 43 | } |
41 | 44 | ||
42 | void installIn(lua_State* L_) | 45 | void installIn(lua_State* const L_) const |
43 | { | 46 | { |
44 | lua_setallocf(L_, protected_lua_Alloc, this); | 47 | // install our replacement allocator function (this is a C function, we need to deconst ourselves) |
48 | lua_setallocf(L_, protected_lua_Alloc, static_cast<void*>(const_cast<ProtectedAllocator*>(this))); | ||
45 | } | 49 | } |
46 | 50 | ||
47 | void removeFrom(lua_State* L_) | 51 | void removeFrom(lua_State* const L_) const |
48 | { | 52 | { |
49 | // remove the protected allocator, if any | 53 | // restore the base allocator function |
50 | if (allocF != nullptr) { | 54 | super::installIn(L_); |
51 | // install the non-protected allocator | ||
52 | lua_setallocf(L_, allocF, allocUD); | ||
53 | } | ||
54 | } | 55 | } |
55 | }; | 56 | }; |
56 | 57 | ||