aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/index.html7
-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
-rw-r--r--tests/protect_allocator.lua2
9 files changed, 137 insertions, 100 deletions
diff --git a/docs/index.html b/docs/index.html
index e9c3239..b48c527 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -345,13 +345,12 @@
345 <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"> 345 <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%">
346 <tr> 346 <tr>
347 <td> 347 <td>
348 <pre> struct { lua_Alloc allocF; void* allocUD;}</pre> 348 <pre> struct { lua_Alloc allocF; void* allocUD; }</pre>
349 </td> 349 </td>
350 </tr> 350 </tr>
351 </table> 351 </table>
352 The contents will be used to create the state with <tt>lua_newstate( allocF, allocUD)</tt>. 352 The contents will be used to create the state with <tt>lua_newstate(allocF, allocUD)</tt>.
353 This option is mostly useful for embedders that want to provide different allocators to each lane, for example to have each one work in a different memory pool thus preventing the need for the allocator itself to be threadsafe. 353 This option is mostly useful for embedders that want to provide different allocators to each lane, for example to have each one work in a different memory pool thus preventing the need for the allocator itself to be threadsafe.
354 Note however that linda deep proxy are allocated with the allocator from the master state, because they are not tied to a particular state.
355 </td> 354 </td>
356 </tr> 355 </tr>
357 356
@@ -364,7 +363,7 @@
364 </td> 363 </td>
365 <td> 364 <td>
366 (Since v3.16.1)<br /> 365 (Since v3.16.1)<br />
367 Controls which allocator is used for Lanest internal allocations (for keeper and deep userdata management). 366 Controls which allocator is used for Lanes internal allocations (for keeper, linda and lane management).
368 If <tt>"libc"</tt>, Lanes uses <tt>realloc</tt> and <tt>free</tt>.<br /> 367 If <tt>"libc"</tt>, Lanes uses <tt>realloc</tt> and <tt>free</tt>.<br />
369 If <tt>"allocator"</tt>, Lanes uses whatever was obtained from the <tt>"allocator"</tt> setting.<br /> 368 If <tt>"allocator"</tt>, Lanes uses whatever was obtained from the <tt>"allocator"</tt> setting.<br />
370 This option is mostly useful for embedders that want control all memory allocations, but have issues when Lanes tries to use the Lua State allocator for internal purposes (especially with LuaJIT). 369 This option is mostly useful for embedders that want control all memory allocations, but have issues when Lanes tries to use the Lua State allocator for internal purposes (especially with LuaJIT).
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// ################################################################################################
diff --git a/tests/protect_allocator.lua b/tests/protect_allocator.lua
index 593261e..6eead5d 100644
--- a/tests/protect_allocator.lua
+++ b/tests/protect_allocator.lua
@@ -47,4 +47,6 @@ end
47 47
48-- wait for completion 48-- wait for completion
49linda:receive( linda.batched, "key", COUNT) 49linda:receive( linda.batched, "key", COUNT)
50print "waiting a bit more ..."
51linda:receive( 1, "foo")
50print "SUCCESS" 52print "SUCCESS"