Skip to content

Commit

Permalink
new .internal_allocator configuration IUNTESTED)
Browse files Browse the repository at this point in the history
new configuration option .internal_allocator to help LuaJIT users. THIS IS YET UNTESTED, USE AT YOUR OWN RISKS.
  • Loading branch information
benoit-germain committed Aug 9, 2023
1 parent d73f4ce commit ad7258f
Show file tree
Hide file tree
Showing 13 changed files with 83 additions and 62 deletions.
Binary file not shown.
4 changes: 4 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
CHANGES:

CHANGE 156: BGe 9-Aug-23
* new configuration option .internal_allocator to help LuaJIT users.
* internal version bumped to 3.16.1

CHANGE 155: BGe 28-Jul-23
* tweaks to linux thread priority management: do nothing if not super-user. if super-user, do nothing if nothing is provided (instead of trying to force a prio when LINUX_SCHED_RR is defined).

Expand Down
34 changes: 25 additions & 9 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@
<font size="-1">
<p>
<br/>
<i>Copyright &copy; 2007-22 Asko Kauppi, Benoit Germain. All rights reserved.</i>
<i>Copyright &copy; 2007-23 Asko Kauppi, Benoit Germain. All rights reserved.</i>
<br/>
Lua Lanes is published under the same <a href="http://en.wikipedia.org/wiki/MIT_License">MIT license</a> as Lua 5.1, 5.2, 5.3 and 5.4.
</p>

<p>
This document was revised on 8-Feb-22, and applies to version <tt>3.16.0</tt>.
This document was revised on 9-Aug-23, and applies to version <tt>3.16.1</tt>.
</p>
</font>
</center>
Expand Down Expand Up @@ -338,9 +338,9 @@ <h2 id="initialization">Initialization</h2>
<tt>nil</tt>/<tt>"protected"</tt>/function
</td>
<td>
(Since v3.13.0)<br/>
If <tt>nil</tt>, Lua states are created with <tt>lua_newstate()</tt> and reuse the allocator from the master state.<br/>
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/>
(Since v3.13.0)<br />
If <tt>nil</tt>, Lua states are created with <tt>lua_newstate()</tt> and reuse the allocator from the master state.<br />
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 />
If a <tt>function</tt>, this function is called prior to creating the state. It should return a full userdata containing the following structure:
<table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%">
<tr>
Expand All @@ -355,6 +355,22 @@ <h2 id="initialization">Initialization</h2>
</td>
</tr>

<tr valign=top>
<td id="internal_allocator">
<code>.internal_allocator</code>
</td>
<td>
<tt>"libc"</tt>/<tt>"allocator"</tt>
</td>
<td>
(Since v3.16.1)<br />
Controls which allocator is used for Lanest internal allocations (for keeper and deep userdata management).
If <tt>"libc"</tt>, Lanes uses <tt>realloc</tt> and <tt>free</tt>.<br />
If <tt>"allocator"</tt>, Lanes uses whatever was obtained from the <tt>"allocator"</tt> setting.<br />
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).
</td>
</tr>

<tr valign=top>
<td id="demote_full_userdata">
<code>.demote_full_userdata</code>
Expand Down Expand Up @@ -390,18 +406,18 @@ <h2 id="initialization">Initialization</h2>
</td>
<td>
If provided, will be called in every created Lua state right after initializing the base libraries.
<br/>
<br />
Keeper states will call it as well, but only if it is a C function (keeper states are not able to execute any user Lua code).
<br/>
<br />
Typical usage is twofold:
<ul>
<li>Tweak <tt>package.loaders</tt></li>
<li>Load some additional C functions in the global space (of course only a C function will be able to do this).</li>
</ul>
That way, all changes in the state can be properly taken into account when building the function lookup database. Default is <tt>nil</tt>.
<br/>
<br />
(Since version 3.7.6) If <tt>on_state_create()</tt> is a Lua function, it will be transfered normally before the call.
<br/>
<br />
If it is a C function, a C closure will be reconstructed in the created state from the C pointer. Lanes will raise an error if the function has upvalues.
</td>
</tr>
Expand Down
2 changes: 1 addition & 1 deletion src/deep.c
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc, int nuv_)
DeepPrelude* prelude = idfunc( L, eDO_new);
if( prelude == NULL)
{
luaL_error( L, "idfunc(eDO_new) failed to create deep userdata (out of memory)");
return luaL_error( L, "idfunc(eDO_new) failed to create deep userdata (out of memory)");
}
if( prelude->magic.value != DEEP_VERSION.value)
{
Expand Down
20 changes: 4 additions & 16 deletions src/keeper.c
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ int keepercall_count( lua_State* L)
*/

// called as __gc for the keepers array userdata
void close_keepers( Universe* U, lua_State* L)
void close_keepers( Universe* U)
{
if( U->keepers != NULL)
{
Expand Down Expand Up @@ -611,15 +611,8 @@ void close_keepers( Universe* U, lua_State* L)
}
// free the keeper bookkeeping structure
{
// don't hijack the state allocator when running LuaJIT because it looks like LuaJIT does not expect it and might invalidate the memory unexpectedly
#if USE_LUA_STATE_ALLOCATOR()
{
AllocatorDefinition* const allocD = &U->protected_allocator.definition;
allocD->allocF( allocD->allocUD, U->keepers, sizeof( Keepers) + (nbKeepers - 1) * sizeof( Keeper), 0);
}
#else // USE_LUA_STATE_ALLOCATOR()
free(U->keepers);
#endif // USE_LUA_STATE_ALLOCATOR()
AllocatorDefinition* const allocD = &U->internal_allocator;
allocD->allocF( allocD->allocUD, U->keepers, sizeof( Keepers) + (nbKeepers - 1) * sizeof( Keeper), 0);
U->keepers = NULL;
}
}
Expand Down Expand Up @@ -653,15 +646,10 @@ void init_keepers( Universe* U, lua_State* L)
// Keepers contains an array of 1 s_Keeper, adjust for the actual number of keeper states
{
size_t const bytes = sizeof( Keepers) + (nb_keepers - 1) * sizeof( Keeper);
// don't hijack the state allocator when running LuaJIT because it looks like LuaJIT does not expect it and might invalidate the memory unexpectedly
#if USE_LUA_STATE_ALLOCATOR()
{
AllocatorDefinition* const allocD = &U->protected_allocator.definition;
AllocatorDefinition* const allocD = &U->internal_allocator;
U->keepers = (Keepers*) allocD->allocF( allocD->allocUD, NULL, 0, bytes);
}
#else // USE_LUA_STATE_ALLOCATOR()
U->keepers = (Keepers*)malloc(bytes);
#endif // USE_LUA_STATE_ALLOCATOR()
if( U->keepers == NULL)
{
(void) luaL_error( L, "init_keepers() failed while creating keeper array; out of memory");
Expand Down
2 changes: 1 addition & 1 deletion src/keeper.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ struct s_Keepers
typedef struct s_Keepers Keepers;

void init_keepers( Universe* U, lua_State* L);
void close_keepers( Universe* U, lua_State* L);
void close_keepers( Universe* U);

Keeper* which_keeper( Keepers* keepers_, ptrdiff_t magic_);
Keeper* keeper_acquire( Keepers* keepers_, ptrdiff_t magic_);
Expand Down
16 changes: 3 additions & 13 deletions src/lanes.c
Original file line number Diff line number Diff line change
Expand Up @@ -253,15 +253,10 @@ static void lane_cleanup( Lane* s)
}
#endif // HAVE_LANE_TRACKING()

// don't hijack the state allocator when running LuaJIT because it looks like LuaJIT does not expect it and might invalidate the memory unexpectedly
#if USE_LUA_STATE_ALLOCATOR()
{
AllocatorDefinition* const allocD = &s->U->protected_allocator.definition;
AllocatorDefinition* const allocD = &s->U->internal_allocator;
allocD->allocF(allocD->allocUD, s, sizeof(Lane), 0);
}
#else // USE_LUA_STATE_ALLOCATOR()
free(s);
#endif // USE_LUA_STATE_ALLOCATOR()
}

/*
Expand Down Expand Up @@ -584,7 +579,7 @@ static int selfdestruct_gc( lua_State* L)
U->timer_deep = NULL;
}

close_keepers( U, L);
close_keepers( U);

// remove the protected allocator, if any
cleanup_allocator_function( U, L);
Expand Down Expand Up @@ -1231,15 +1226,10 @@ LUAG_FUNC( lane_new)
//
// a Lane full userdata needs a single uservalue
ud = lua_newuserdatauv( L, sizeof( Lane*), 1); // func libs priority globals package required gc_cb lane
// don't hijack the state allocator when running LuaJIT because it looks like LuaJIT does not expect it and might invalidate the memory unexpectedly
#if USE_LUA_STATE_ALLOCATOR()
{
AllocatorDefinition* const allocD = &U->protected_allocator.definition;
AllocatorDefinition* const allocD = &U->internal_allocator;
s = *ud = (Lane*)allocD->allocF(allocD->allocUD, NULL, 0, sizeof(Lane));
}
#else // USE_LUA_STATE_ALLOCATOR()
s = *ud = (Lane*) malloc(sizeof(Lane));
#endif // USE_LUA_STATE_ALLOCATOR()
if( s == NULL)
{
return luaL_error( L, "could not create lane: out of memory");
Expand Down
2 changes: 1 addition & 1 deletion src/lanes.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

#define LANES_VERSION_MAJOR 3
#define LANES_VERSION_MINOR 16
#define LANES_VERSION_PATCH 0
#define LANES_VERSION_PATCH 1

#define LANES_MIN_VERSION_REQUIRED(MAJOR, MINOR, PATCH) ((LANES_VERSION_MAJOR>MAJOR) || (LANES_VERSION_MAJOR==MAJOR && (LANES_VERSION_MINOR>MINOR || (LANES_VERSION_MINOR==MINOR && LANES_VERSION_PATCH>=PATCH))))
#define LANES_VERSION_LESS_THAN(MAJOR, MINOR, PATCH) ((LANES_VERSION_MAJOR<MAJOR) || (LANES_VERSION_MAJOR==MAJOR && (LANES_VERSION_MINOR<MINOR || (LANES_VERSION_MINOR==MINOR && LANES_VERSION_PATCH<PATCH))))
Expand Down
8 changes: 7 additions & 1 deletion src/lanes.lua
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ lanes.configure = function( settings_)
demote_full_userdata = nil,
verbose_errors = false,
-- LuaJIT provides a thread-unsafe allocator by default, so we need to protect it when used in parallel lanes
allocator = (package.loaded.jit and jit.version) and "protected" or nil
allocator = (package.loaded.jit and jit.version) and "protected" or nil,
-- it looks also like LuaJIT allocator may not appreciate direct use of its allocator for other purposes than the VM operation
internal_allocator = (package.loaded.jit and jit.version) and "libc" or "allocator"
}
local boolean_param_checker = function( val_)
-- non-'boolean-false' should be 'boolean-true' or nil
Expand All @@ -94,6 +96,10 @@ lanes.configure = function( settings_)
-- can be nil, "protected", or a function
return val_ and (type( val_) == "function" or val_ == "protected") or true
end,
internal_allocator = function( val_)
-- can be "libc" or "allocator"
return val_ == "libc" or val_ == "allocator"
end,
on_state_create = function( val_)
-- on_state_create may be nil or a function
return val_ and type( val_) == "function" or true
Expand Down
14 changes: 2 additions & 12 deletions src/linda.c
Original file line number Diff line number Diff line change
Expand Up @@ -794,17 +794,12 @@ static void* linda_id( lua_State* L, DeepOp op_)
* One can use any memory allocation scheme.
* just don't use L's allocF because we don't know which state will get the honor of GCing the linda
*/
// don't hijack the state allocator when running LuaJIT because it looks like LuaJIT does not expect it and might invalidate the memory unexpectedly
#if USE_LUA_STATE_ALLOCATOR()
{
Universe* const U = universe_get(L);
AllocatorDefinition* const allocD = &U->protected_allocator.definition;
AllocatorDefinition* const allocD = &U->internal_allocator;

s = (struct s_Linda*)allocD->allocF(allocD->allocUD, NULL, 0, sizeof(struct s_Linda) + name_len); // terminating 0 is already included
}
#else // USE_LUA_STATE_ALLOCATOR()
s = (struct s_Linda*)malloc(sizeof(struct s_Linda) + name_len); // terminating 0 is already included
#endif // USE_LUA_STATE_ALLOCATOR()
if( s)
{
s->prelude.magic.value = DEEP_VERSION.value;
Expand Down Expand Up @@ -837,17 +832,12 @@ static void* linda_id( lua_State* L, DeepOp op_)
// There aren't any lanes waiting on these lindas, since all proxies have been gc'ed. Right?
SIGNAL_FREE( &linda->read_happened);
SIGNAL_FREE( &linda->write_happened);
// don't hijack the state allocator when running LuaJIT because it looks like LuaJIT does not expect it and might invalidate the memory unexpectedly
#if USE_LUA_STATE_ALLOCATOR()
{
Universe* const U = universe_get(L);
AllocatorDefinition* const allocD = &U->protected_allocator.definition;
AllocatorDefinition* const allocD = &U->internal_allocator;

allocD->allocF(allocD->allocUD, linda, sizeof(struct s_Linda) + strlen(linda->name), 0);
}
#else // USE_LUA_STATE_ALLOCATOR()
free(linda);
#endif // USE_LUA_STATE_ALLOCATOR()
return NULL;
}

Expand Down
3 changes: 0 additions & 3 deletions src/macros_and_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,4 @@ extern char const* debugspew_indent;

#define LUAG_FUNC( func_name) int LG_##func_name( lua_State* L)

// after all, it looks like we can use the state allocator for our own usage when running LuaJIT, as long as we mutex-protect it
#define USE_LUA_STATE_ALLOCATOR() 1 // (LUAJIT_FLAVOR()==0)

#endif // MACROS_AND_UTILS_H
38 changes: 33 additions & 5 deletions src/tools.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,20 @@ void luaG_dump( lua_State* L)

// ################################################################################################

static void* libc_lua_Alloc(void* ud, void* ptr, size_t osize, size_t nsize)
{
(void)ud; (void)osize; /* not used */
if (nsize == 0)
{
free(ptr);
return NULL;
}
else
{
return realloc(ptr, nsize);
}
}

static void* protected_lua_Alloc( void *ud, void *ptr, size_t osize, size_t nsize)
{
void* p;
Expand Down Expand Up @@ -217,6 +231,22 @@ void initialize_allocator_function( Universe* U, lua_State* L)
U->protected_allocator.definition.allocF = lua_getallocf( L, &U->protected_allocator.definition.allocUD);
}
lua_pop( L, 1); // settings
STACK_MID(L, 0);

lua_getfield( L, -1, "internal_allocator"); // settings "libc"|"allocator"
{
char const* allocator = lua_tostring( L, -1);
if (stricmp(allocator, "libc") == 0)
{
U->internal_allocator.allocF = libc_lua_Alloc;
U->internal_allocator.allocUD = NULL;
}
else
{
U->internal_allocator = U->protected_allocator.definition;
}
}
lua_pop( L, 1); // settings
STACK_END( L, 0);
}

Expand Down Expand Up @@ -1337,17 +1367,17 @@ static void copy_cached_func( Universe* U, lua_State* L2, uint_t L2_cache_i, lua

if( lua_isnil( L2, -1)) // function is unknown
{
lua_pop( L2, 1); // ... {cache} ... p
lua_pop( L2, 1); // ... {cache} ... p

// Set to 'true' for the duration of creation; need to find self-references
// via upvalues
//
// pushes a copy of the func, stores a reference in the cache
copy_func( U, L2, L2_cache_i, L, i, mode_, upName_); // ... {cache} ... function
copy_func( U, L2, L2_cache_i, L, i, mode_, upName_); // ... {cache} ... function
}
else // found function in the cache
{
lua_remove( L2, -2); // ... {cache} ... function
lua_remove( L2, -2); // ... {cache} ... function
}
STACK_END( L2, 1);
ASSERT_L( lua_isfunction( L2, -1));
Expand Down Expand Up @@ -1725,9 +1755,7 @@ static bool_t inter_copy_function( Universe* U, lua_State* L2, uint_t L2_cache_i
{
DEBUGSPEW_CODE( fprintf( stderr, "FUNCTION %s\n", upName_));
DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
STACK_CHECK( L2, 0);
copy_cached_func( U, L2, L2_cache_i, L, source_i_, mode_, upName_); // ... f
STACK_END( L2, 1);
DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
}
STACK_END( L2, 1);
Expand Down
2 changes: 2 additions & 0 deletions src/universe.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ struct s_Universe
// contains a mutex and the original allocator definition
ProtectedAllocator protected_allocator;

AllocatorDefinition internal_allocator;

Keepers* keepers;

// Initialized by 'init_once_LOCKED()': the deep userdata Linda object
Expand Down

0 comments on commit ad7258f

Please sign in to comment.