aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/keeper.cpp12
-rw-r--r--src/lanes.cpp20
-rw-r--r--src/linda.cpp18
-rw-r--r--src/state.cpp4
-rw-r--r--src/tools.cpp87
-rw-r--r--src/tools.h1
-rw-r--r--src/universe.h86
7 files changed, 132 insertions, 96 deletions
diff --git a/src/keeper.cpp b/src/keeper.cpp
index 19f6ae1..88bd4ff 100644
--- a/src/keeper.cpp
+++ b/src/keeper.cpp
@@ -614,11 +614,8 @@ void close_keepers(Universe* U)
614 MUTEX_FREE(&U->keepers->keeper_array[i].keeper_cs); 614 MUTEX_FREE(&U->keepers->keeper_array[i].keeper_cs);
615 } 615 }
616 // free the keeper bookkeeping structure 616 // free the keeper bookkeeping structure
617 { 617 U->internal_allocator.free(U->keepers, sizeof(Keepers) + (nbKeepers - 1) * sizeof(Keeper));
618 AllocatorDefinition* const allocD = &U->internal_allocator; 618 U->keepers = nullptr;
619 std::ignore = allocD->allocF(allocD->allocUD, U->keepers, sizeof(Keepers) + (nbKeepers - 1) * sizeof(Keeper), 0);
620 U->keepers = nullptr;
621 }
622 } 619 }
623} 620}
624 621
@@ -649,10 +646,7 @@ void init_keepers(Universe* U, lua_State* L)
649 // Keepers contains an array of 1 Keeper, adjust for the actual number of keeper states 646 // Keepers contains an array of 1 Keeper, adjust for the actual number of keeper states
650 { 647 {
651 size_t const bytes = sizeof(Keepers) + (nb_keepers - 1) * sizeof(Keeper); 648 size_t const bytes = sizeof(Keepers) + (nb_keepers - 1) * sizeof(Keeper);
652 { 649 U->keepers = static_cast<Keepers*>(U->internal_allocator.alloc(bytes));
653 AllocatorDefinition* const allocD = &U->internal_allocator;
654 U->keepers = (Keepers*) allocD->allocF(allocD->allocUD, nullptr, 0, bytes);
655 }
656 if (U->keepers == nullptr) 650 if (U->keepers == nullptr)
657 { 651 {
658 (void) luaL_error(L, "init_keepers() failed while creating keeper array; out of memory"); 652 (void) luaL_error(L, "init_keepers() failed while creating keeper array; out of memory");
diff --git a/src/lanes.cpp b/src/lanes.cpp
index 99c5812..5945a1a 100644
--- a/src/lanes.cpp
+++ b/src/lanes.cpp
@@ -254,10 +254,7 @@ static void lane_cleanup( Lane* s)
254 } 254 }
255#endif // HAVE_LANE_TRACKING() 255#endif // HAVE_LANE_TRACKING()
256 256
257 { 257 s->U->internal_allocator.free(s, sizeof(Lane));
258 AllocatorDefinition* const allocD = &s->U->internal_allocator;
259 (void) allocD->allocF(allocD->allocUD, s, sizeof(Lane), 0);
260 }
261} 258}
262 259
263/* 260/*
@@ -584,7 +581,7 @@ static int selfdestruct_gc( lua_State* L)
584 close_keepers( U); 581 close_keepers( U);
585 582
586 // remove the protected allocator, if any 583 // remove the protected allocator, if any
587 cleanup_allocator_function( U, L); 584 U->protected_allocator.removeFrom(L);
588 585
589 U->Universe::~Universe(); 586 U->Universe::~Universe();
590 587
@@ -1036,10 +1033,6 @@ static constexpr UniqueKey GCCB_KEY{ 0xcfb1f046ef074e88ull };
1036// 1033//
1037LUAG_FUNC( lane_new) 1034LUAG_FUNC( lane_new)
1038{ 1035{
1039 lua_State* L2;
1040 Lane* s;
1041 Lane** ud;
1042
1043 char const* libs_str = lua_tostring( L, 2); 1036 char const* libs_str = lua_tostring( L, 2);
1044 bool const have_priority{ !lua_isnoneornil(L, 3) }; 1037 bool const have_priority{ !lua_isnoneornil(L, 3) };
1045 int const priority = have_priority ? (int) lua_tointeger( L, 3) : THREAD_PRIO_DEFAULT; 1038 int const priority = have_priority ? (int) lua_tointeger( L, 3) : THREAD_PRIO_DEFAULT;
@@ -1066,7 +1059,7 @@ LUAG_FUNC( lane_new)
1066 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1059 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1067 1060
1068 // populate with selected libraries at the same time 1061 // populate with selected libraries at the same time
1069 L2 = luaG_newstate( U, L, libs_str); // L // L2 1062 lua_State* const L2{ luaG_newstate(U, L, libs_str) }; // L // L2
1070 1063
1071 STACK_GROW( L2, nargs + 3); // 1064 STACK_GROW( L2, nargs + 3); //
1072 STACK_CHECK_START_REL(L2, 0); 1065 STACK_CHECK_START_REL(L2, 0);
@@ -1219,11 +1212,8 @@ LUAG_FUNC( lane_new)
1219 // 's' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread) 1212 // 's' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread)
1220 // 1213 //
1221 // a Lane full userdata needs a single uservalue 1214 // a Lane full userdata needs a single uservalue
1222 ud = (Lane**) lua_newuserdatauv( L, sizeof( Lane*), 1); // func libs priority globals package required gc_cb lane 1215 Lane** const ud{ static_cast<Lane**>(lua_newuserdatauv(L, sizeof(Lane*), 1)) }; // func libs priority globals package required gc_cb lane
1223 { 1216 Lane* const s{ *ud = static_cast<Lane*>(U->internal_allocator.alloc(sizeof(Lane))) }; // don't forget to store the pointer in the userdata!
1224 AllocatorDefinition* const allocD = &U->internal_allocator;
1225 s = *ud = (Lane*) allocD->allocF(allocD->allocUD, nullptr, 0, sizeof(Lane));
1226 }
1227 if( s == nullptr) 1217 if( s == nullptr)
1228 { 1218 {
1229 return luaL_error( L, "could not create lane: out of memory"); 1219 return luaL_error( L, "could not create lane: out of memory");
diff --git a/src/linda.cpp b/src/linda.cpp
index f2e39a8..eb2349e 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -759,7 +759,6 @@ static void* linda_id( lua_State* L, DeepOp op_)
759 { 759 {
760 case eDO_new: 760 case eDO_new:
761 { 761 {
762 struct s_Linda* s;
763 size_t name_len = 0; 762 size_t name_len = 0;
764 char const* linda_name = nullptr; 763 char const* linda_name = nullptr;
765 unsigned long linda_group = 0; 764 unsigned long linda_group = 0;
@@ -791,12 +790,9 @@ static void* linda_id( lua_State* L, DeepOp op_)
791 * One can use any memory allocation scheme. 790 * One can use any memory allocation scheme.
792 * just don't use L's allocF because we don't know which state will get the honor of GCing the linda 791 * just don't use L's allocF because we don't know which state will get the honor of GCing the linda
793 */ 792 */
794 Universe* const U = universe_get(L); 793 Universe* const U{ universe_get(L) };
795 { 794 struct s_Linda* s{ static_cast<struct s_Linda*>(U->internal_allocator.alloc(sizeof(struct s_Linda) + name_len)) }; // terminating 0 is already included
796 AllocatorDefinition* const allocD = &U->internal_allocator; 795 if (s)
797 s = (struct s_Linda*) allocD->allocF(allocD->allocUD, nullptr, 0, sizeof(struct s_Linda) + name_len); // terminating 0 is already included
798 }
799 if( s)
800 { 796 {
801 s->prelude.DeepPrelude::DeepPrelude(); 797 s->prelude.DeepPrelude::DeepPrelude();
802 SIGNAL_INIT( &s->read_happened); 798 SIGNAL_INIT( &s->read_happened);
@@ -812,12 +808,11 @@ static void* linda_id( lua_State* L, DeepOp op_)
812 808
813 case eDO_delete: 809 case eDO_delete:
814 { 810 {
815 Keeper* K;
816 struct s_Linda* linda = (struct s_Linda*) lua_touserdata( L, 1); 811 struct s_Linda* linda = (struct s_Linda*) lua_touserdata( L, 1);
817 ASSERT_L( linda); 812 ASSERT_L( linda);
818 813
819 // Clean associated structures in the keeper state. 814 // Clean associated structures in the keeper state.
820 K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); 815 Keeper* const K{ keeper_acquire(linda->U->keepers, LINDA_KEEPER_HASHSEED(linda)) };
821 if( K && K->L) // can be nullptr if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) 816 if( K && K->L) // can be nullptr if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup)
822 { 817 {
823 // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... 818 // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex...
@@ -829,10 +824,7 @@ static void* linda_id( lua_State* L, DeepOp op_)
829 SIGNAL_FREE( &linda->read_happened); 824 SIGNAL_FREE( &linda->read_happened);
830 SIGNAL_FREE( &linda->write_happened); 825 SIGNAL_FREE( &linda->write_happened);
831 linda->prelude.DeepPrelude::~DeepPrelude(); 826 linda->prelude.DeepPrelude::~DeepPrelude();
832 { 827 linda->U->internal_allocator.free(linda, sizeof(struct s_Linda) + strlen(linda->name));
833 AllocatorDefinition* const allocD = &linda->U->internal_allocator;
834 (void) allocD->allocF(allocD->allocUD, linda, sizeof(struct s_Linda) + strlen(linda->name), 0);
835 }
836 return nullptr; 828 return nullptr;
837 } 829 }
838 830
diff --git a/src/state.cpp b/src/state.cpp
index c66242c..7bdaec9 100644
--- a/src/state.cpp
+++ b/src/state.cpp
@@ -260,14 +260,14 @@ lua_State* create_state( Universe* U, lua_State* from_)
260 lua_call( from_, 0, 1); 260 lua_call( from_, 0, 1);
261 { 261 {
262 AllocatorDefinition* const def = (AllocatorDefinition*) lua_touserdata( from_, -1); 262 AllocatorDefinition* const def = (AllocatorDefinition*) lua_touserdata( from_, -1);
263 L = lua_newstate( def->allocF, def->allocUD); 263 L = lua_newstate( def->m_allocF, def->m_allocUD);
264 } 264 }
265 lua_pop( from_, 1); 265 lua_pop( from_, 1);
266 } 266 }
267 else 267 else
268 { 268 {
269 // reuse the allocator provided when the master state was created 269 // reuse the allocator provided when the master state was created
270 L = lua_newstate( U->protected_allocator.definition.allocF, U->protected_allocator.definition.allocUD); 270 L = lua_newstate(U->protected_allocator.m_allocF, U->protected_allocator.m_allocUD);
271 } 271 }
272#endif // LUAJIT_FLAVOR() == 64 272#endif // LUAJIT_FLAVOR() == 64
273 273
diff --git a/src/tools.cpp b/src/tools.cpp
index 68846ba..89e5499 100644
--- a/src/tools.cpp
+++ b/src/tools.cpp
@@ -156,105 +156,92 @@ void luaG_dump( lua_State* L)
156// ################################################################################################ 156// ################################################################################################
157 157
158// same as PUC-Lua l_alloc 158// same as PUC-Lua l_alloc
159static void* libc_lua_Alloc(void* ud, void* ptr, size_t osize, size_t nsize) 159extern "C" static void* libc_lua_Alloc([[maybe_unused]] void* ud, [[maybe_unused]] void* ptr_, size_t osize_, size_t nsize_)
160{ 160{
161 (void)ud; (void)osize; /* not used */ 161 if (nsize_ == 0)
162 if (nsize == 0)
163 { 162 {
164 free(ptr); 163 free(ptr_);
165 return nullptr; 164 return nullptr;
166 } 165 }
167 else 166 else
168 { 167 {
169 return realloc(ptr, nsize); 168 return realloc(ptr_, nsize_);
170 } 169 }
171} 170}
172 171
173static void* protected_lua_Alloc( void *ud, void *ptr, size_t osize, size_t nsize) 172// #################################################################################################
174{
175 void* p;
176 ProtectedAllocator* s = (ProtectedAllocator*) ud;
177 s->lock.lock();
178 p = s->definition.allocF( s->definition.allocUD, ptr, osize, nsize);
179 s->lock.unlock();
180 return p;
181}
182 173
183static int luaG_provide_protected_allocator( lua_State* L) 174static int luaG_provide_protected_allocator(lua_State* L)
184{ 175{
185 Universe* U = universe_get( L); 176 Universe* const U{ universe_get(L) };
186 AllocatorDefinition* const def = (AllocatorDefinition*) lua_newuserdatauv( L, sizeof(AllocatorDefinition), 0); 177 // push a new full userdata on the stack, giving access to the universe's protected allocator
187 def->allocF = protected_lua_Alloc; 178 AllocatorDefinition* const def{ new (L) AllocatorDefinition{ U->protected_allocator.makeDefinition() } };
188 def->allocUD = &U->protected_allocator;
189 return 1; 179 return 1;
190} 180}
191 181
182// #################################################################################################
183
192// called once at the creation of the universe (therefore L is the master Lua state everything originates from) 184// called once at the creation of the universe (therefore L is the master Lua state everything originates from)
193// Do I need to disable this when compiling for LuaJIT to prevent issues? 185// Do I need to disable this when compiling for LuaJIT to prevent issues?
194void initialize_allocator_function( Universe* U, lua_State* L) 186void initialize_allocator_function(Universe* U, lua_State* L)
195{ 187{
196 STACK_CHECK_START_REL(L, 1); // settings 188 STACK_CHECK_START_REL(L, 1); // settings
197 lua_getfield( L, -1, "allocator"); // settings allocator|nil|"protected" 189 lua_getfield(L, -1, "allocator"); // settings allocator|nil|"protected"
198 if( !lua_isnil( L, -1)) 190 if (!lua_isnil(L, -1))
199 { 191 {
200 // store C function pointer in an internal variable 192 // store C function pointer in an internal variable
201 U->provide_allocator = lua_tocfunction( L, -1); // settings allocator 193 U->provide_allocator = lua_tocfunction(L, -1); // settings allocator
202 if (U->provide_allocator != nullptr) 194 if (U->provide_allocator != nullptr)
203 { 195 {
204 // make sure the function doesn't have upvalues 196 // make sure the function doesn't have upvalues
205 char const* upname = lua_getupvalue( L, -1, 1); // settings allocator upval? 197 char const* upname = lua_getupvalue(L, -1, 1); // settings allocator upval?
206 if (upname != nullptr) // should be "" for C functions with upvalues if any 198 if (upname != nullptr) // should be "" for C functions with upvalues if any
207 { 199 {
208 (void) luaL_error( L, "config.allocator() shouldn't have upvalues"); 200 (void) luaL_error(L, "config.allocator() shouldn't have upvalues");
209 } 201 }
210 // remove this C function from the config table so that it doesn't cause problems 202 // remove this C function from the config table so that it doesn't cause problems
211 // when we transfer the config table in newly created Lua states 203 // when we transfer the config table in newly created Lua states
212 lua_pushnil( L); // settings allocator nil 204 lua_pushnil(L); // settings allocator nil
213 lua_setfield( L, -3, "allocator"); // settings allocator 205 lua_setfield(L, -3, "allocator"); // settings allocator
214 } 206 }
215 else if( lua_type( L, -1) == LUA_TSTRING) // should be "protected" 207 else if (lua_type(L, -1) == LUA_TSTRING) // should be "protected"
216 { 208 {
209 ASSERT_L(strcmp(lua_tostring(L, -1), "protected") == 0);
217 // set the original allocator to call from inside protection by the mutex 210 // set the original allocator to call from inside protection by the mutex
218 U->protected_allocator.definition.allocF = lua_getallocf( L, &U->protected_allocator.definition.allocUD); 211 U->protected_allocator.initFrom(L);
212 U->protected_allocator.installIn(L);
219 // before a state is created, this function will be called to obtain the allocator 213 // before a state is created, this function will be called to obtain the allocator
220 U->provide_allocator = luaG_provide_protected_allocator; 214 U->provide_allocator = luaG_provide_protected_allocator;
221
222 lua_setallocf( L, protected_lua_Alloc, &U->protected_allocator);
223 } 215 }
224 } 216 }
225 else 217 else
226 { 218 {
227 // just grab whatever allocator was provided to lua_newstate 219 // just grab whatever allocator was provided to lua_newstate
228 U->protected_allocator.definition.allocF = lua_getallocf( L, &U->protected_allocator.definition.allocUD); 220 U->protected_allocator.initFrom(L);
229 } 221 }
230 lua_pop( L, 1); // settings 222 lua_pop(L, 1); // settings
231 STACK_CHECK(L, 1); 223 STACK_CHECK(L, 1);
232 224
233 lua_getfield( L, -1, "internal_allocator"); // settings "libc"|"allocator" 225 lua_getfield(L, -1, "internal_allocator"); // settings "libc"|"allocator"
234 { 226 {
235 char const* allocator = lua_tostring( L, -1); 227 char const* allocator = lua_tostring(L, -1);
236 if (strcmp(allocator, "libc") == 0) 228 if (strcmp(allocator, "libc") == 0)
237 { 229 {
238 U->internal_allocator.allocF = libc_lua_Alloc; 230 U->internal_allocator = AllocatorDefinition{ libc_lua_Alloc, nullptr };
239 U->internal_allocator.allocUD = nullptr; 231 }
232 else if (U->provide_allocator = luaG_provide_protected_allocator)
233 {
234 // user wants mutex protection on the state's allocator. Use protection for our own allocations too, just in case.
235 U->internal_allocator = U->protected_allocator.makeDefinition();
240 } 236 }
241 else 237 else
242 { 238 {
243 U->internal_allocator = U->protected_allocator.definition; 239 // no protection required, just use whatever we have as-is.
240 U->internal_allocator = U->protected_allocator;
244 } 241 }
245 } 242 }
246 lua_pop( L, 1); // settings 243 lua_pop(L, 1); // settings
247 STACK_CHECK( L, 1); 244 STACK_CHECK(L, 1);
248}
249
250void cleanup_allocator_function( Universe* U, lua_State* L)
251{
252 // remove the protected allocator, if any
253 if (U->protected_allocator.definition.allocF != nullptr)
254 {
255 // install the non-protected allocator
256 lua_setallocf( L, U->protected_allocator.definition.allocF, U->protected_allocator.definition.allocUD);
257 }
258} 245}
259 246
260// ################################################################################################ 247// ################################################################################################
diff --git a/src/tools.h b/src/tools.h
index 95324ee..5e6ce78 100644
--- a/src/tools.h
+++ b/src/tools.h
@@ -38,7 +38,6 @@ int luaG_nameof( lua_State* L);
38 38
39void populate_func_lookup_table( lua_State* L, int _i, char const* _name); 39void populate_func_lookup_table( lua_State* L, int _i, char const* _name);
40void initialize_allocator_function( Universe* U, lua_State* L); 40void initialize_allocator_function( Universe* U, lua_State* L);
41void cleanup_allocator_function( Universe* U, lua_State* L);
42 41
43// ################################################################################################ 42// ################################################################################################
44 43
diff --git a/src/universe.h b/src/universe.h
index a6beb68..3aac20d 100644
--- a/src/universe.h
+++ b/src/universe.h
@@ -27,17 +27,91 @@ struct Lane;
27// ################################################################################################ 27// ################################################################################################
28 28
29// everything we need to provide to lua_newstate() 29// everything we need to provide to lua_newstate()
30struct AllocatorDefinition 30class AllocatorDefinition
31{ 31{
32 lua_Alloc allocF{ nullptr }; 32 public:
33 void* allocUD{ nullptr }; 33
34 lua_Alloc m_allocF{ nullptr };
35 void* m_allocUD{ nullptr };
36
37 static void* operator new(size_t size_, lua_State* L) noexcept { return lua_newuserdatauv(L, size_, 0); }
38 // always embedded somewhere else or "in-place constructed" as a full userdata
39 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception
40 static void operator delete(void* p_, lua_State* L) { ASSERT_L(!"should never be called") };
41
42 AllocatorDefinition(lua_Alloc allocF_, void* allocUD_) noexcept
43 : m_allocF{ allocF_ }
44 , m_allocUD{ allocUD_ }
45 {
46 }
47 AllocatorDefinition() = default;
48 AllocatorDefinition(AllocatorDefinition const& rhs_) = default;
49 AllocatorDefinition(AllocatorDefinition&& rhs_) = default;
50 AllocatorDefinition& operator=(AllocatorDefinition const& rhs_) = default;
51 AllocatorDefinition& operator=(AllocatorDefinition&& rhs_) = default;
52
53 void initFrom(lua_State* L)
54 {
55 m_allocF = lua_getallocf(L, &m_allocUD);
56 }
57
58 void* lua_alloc(void* ptr_, size_t osize_, size_t nsize_)
59 {
60 m_allocF(m_allocUD, ptr_, osize_, nsize_);
61 }
62
63 void* alloc(size_t nsize_)
64 {
65 return m_allocF(m_allocUD, nullptr, 0, nsize_);
66 }
67
68 void free(void* ptr_, size_t osize_)
69 {
70 std::ignore = m_allocF(m_allocUD, ptr_, osize_, 0);
71 }
34}; 72};
35 73
74// ################################################################################################
75
36// mutex-protected allocator for use with Lua states that share a non-threadsafe allocator 76// mutex-protected allocator for use with Lua states that share a non-threadsafe allocator
37struct ProtectedAllocator 77class ProtectedAllocator : public AllocatorDefinition
38{ 78{
39 AllocatorDefinition definition; 79 private:
40 std::mutex lock; 80
81 std::mutex m_lock;
82
83 static void* protected_lua_Alloc(void* ud_, void* ptr_, size_t osize_, size_t nsize_)
84 {
85 ProtectedAllocator* const s{ static_cast<ProtectedAllocator*>(ud_) };
86 std::lock_guard<std::mutex> guard{ s->m_lock };
87 return s->m_allocF(s->m_allocUD, ptr_, osize_, nsize_);
88 }
89
90 public:
91
92 // we are not like our base class: we can't be created inside a full userdata (or we would have to install a metatable and __gc handler to destroy ourselves properly)
93 static void* operator new(size_t size_, lua_State* L) noexcept = delete;
94 static void operator delete(void* p_, lua_State* L) = delete;
95
96 AllocatorDefinition makeDefinition()
97 {
98 return AllocatorDefinition{ protected_lua_Alloc, this};
99 }
100
101 void installIn(lua_State* L)
102 {
103 lua_setallocf(L, protected_lua_Alloc, this);
104 }
105
106 void removeFrom(lua_State* L)
107 {
108 // remove the protected allocator, if any
109 if (m_allocF != nullptr)
110 {
111 // install the non-protected allocator
112 lua_setallocf(L, m_allocF, m_allocUD);
113 }
114 }
41}; 115};
42 116
43// ################################################################################################ 117// ################################################################################################