diff options
-rw-r--r-- | docs/index.html | 7 | ||||
-rw-r--r-- | src/keeper.cpp | 12 | ||||
-rw-r--r-- | src/lanes.cpp | 20 | ||||
-rw-r--r-- | src/linda.cpp | 18 | ||||
-rw-r--r-- | src/state.cpp | 4 | ||||
-rw-r--r-- | src/tools.cpp | 87 | ||||
-rw-r--r-- | src/tools.h | 1 | ||||
-rw-r--r-- | src/universe.h | 86 | ||||
-rw-r--r-- | tests/protect_allocator.lua | 2 |
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 | // |
1037 | LUAG_FUNC( lane_new) | 1034 | LUAG_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 |
159 | static void* libc_lua_Alloc(void* ud, void* ptr, size_t osize, size_t nsize) | 159 | extern "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 | ||
173 | static 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 | ||
183 | static int luaG_provide_protected_allocator( lua_State* L) | 174 | static 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? |
194 | void initialize_allocator_function( Universe* U, lua_State* L) | 186 | void 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 | |||
250 | void 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 | ||
39 | void populate_func_lookup_table( lua_State* L, int _i, char const* _name); | 39 | void populate_func_lookup_table( lua_State* L, int _i, char const* _name); |
40 | void initialize_allocator_function( Universe* U, lua_State* L); | 40 | void initialize_allocator_function( Universe* U, lua_State* L); |
41 | void 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() |
30 | struct AllocatorDefinition | 30 | class 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 |
37 | struct ProtectedAllocator | 77 | class 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 |
49 | linda:receive( linda.batched, "key", COUNT) | 49 | linda:receive( linda.batched, "key", COUNT) |
50 | print "waiting a bit more ..." | ||
51 | linda:receive( 1, "foo") | ||
50 | print "SUCCESS" | 52 | print "SUCCESS" |