Skip to content

Commit 60560e2

Browse files
committed
Fully remove memcatbyteslimit logic
1 parent 7db85d1 commit 60560e2

10 files changed

Lines changed: 60 additions & 104 deletions

File tree

VM/include/llsl.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,6 @@ LUA_API int luaSL_pushnativeinteger(lua_State *L, int val);
6868
LUA_API uint8_t luaSL_lsl_type(lua_State *L, int idx);
6969
/// Should only be called in an interrupt handler!
7070
LUA_API YieldableStatus luaSL_may_interrupt(lua_State *L);
71-
// Keep going with GC until we hit the sweep phase, we need some memory.
72-
LUA_API void luaSL_emergencyfinishgc(lua_State *L);
7371

7472
typedef struct lua_TValue TValue;
7573
uint8_t lua_lsl_type(const TValue *tv);

VM/include/lua.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -342,10 +342,6 @@ LUA_API void lua_setmemcat(lua_State* L, int category);
342342
LUA_API int lua_getmemcat(lua_State* L);
343343
LUA_API size_t lua_totalbytes(lua_State* L, int category);
344344

345-
// ServerLua: used for setting a memcat byte size limit for memcat IDs above 1.
346-
// setting a limit of "0" disables the memory limit.
347-
LUA_API void lua_setmemcatbyteslimit(lua_State* L, lua_Unsigned bytes_limit);
348-
349345
/*
350346
** miscellaneous functions
351347
*/

VM/src/ares.cpp

Lines changed: 24 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2823,8 +2823,6 @@ static void scavenge_sl_vm_internals(lua_State *L, bool forUnpersist) {
28232823
eris_ifassert(const int top = lua_gettop(L));
28242824
int perms_idx = lua_gettop(L);
28252825

2826-
auto *runtime_state = (lua_SLRuntimeState*)lua_getthreaddata(L);
2827-
28282826
const std::vector<std::pair<const char *, int>> lsl_vm_refs {
28292827
// none for now
28302828
};
@@ -3004,6 +3002,10 @@ void eris_register_perms(lua_State *L, bool for_unpersist) {
30043002

30053003
static void
30063004
unchecked_persist(lua_State *L, std::ostream *writer) {
3005+
// pause GC for the duration of serialization - some objects we're creating aren't rooted
3006+
// Also prevents beforeallocate callbacks from being invoked during setup
3007+
ScopedDisableGC _disable_gc(L);
3008+
30073009
eris_ifassert(int old_top=lua_gettop(L));
30083010
Info info; /* perms buff rootobj */
30093011
info.L = L;
@@ -3064,11 +3066,7 @@ unchecked_persist(lua_State *L, std::ostream *writer) {
30643066
#endif
30653067

30663068
p_header(&info);
3067-
{
3068-
ScopedDisableGC _disable_gc(L);
3069-
3070-
persist(&info); /* perms reftbl buff path? rootobj */
3071-
}
3069+
persist(&info); /* perms reftbl buff path? rootobj */
30723070

30733071
#if HARDSTACKTESTS
30743072
lua_pop(L, LUA_MINSTACK - pre_pad_top);
@@ -3084,6 +3082,10 @@ unchecked_persist(lua_State *L, std::ostream *writer) {
30843082

30853083
static void
30863084
unchecked_unpersist(lua_State *L, std::istream *reader) {/* perms str? */
3085+
// pause GC for the duration of deserialization - some objects we're creating aren't rooted
3086+
// Also prevents beforeallocate callbacks from being invoked during setup
3087+
ScopedDisableGC _disable_gc(L);
3088+
30873089
eris_ifassert(int old_top = lua_gettop(L));
30883090
Info info;
30893091
info.L = L;
@@ -3128,32 +3130,27 @@ unchecked_unpersist(lua_State *L, std::istream *reader) {/* perms str? */
31283130
eris_populate_perms(L, true);
31293131
lua_pop(L, 1); /* perms reftbl nil? path? str? */
31303132

3131-
// pause GC for the duration of deserialization - some objects we're creating aren't rooted
3132-
{
3133-
ScopedDisableGC _disable_gc(L);
3134-
31353133
#if HARDSTACKTESTS
3136-
// Arrange the stack to make it more likely that we hit any lua_checkstack() misuse
3137-
int pre_pad_top = lua_gettop(L);
3138-
lua_checkstack(L, LUA_MINSTACK);
3139-
while (lua_gettop(L) != LUA_MINSTACK - 1) {
3140-
lua_pushnil(L);
3141-
}
3142-
// A reference to the root obj needs to end back up on top
3143-
lua_pushvalue(L, pre_pad_top);
3144-
eris_assert(lua_gettop(L) == LUA_MINSTACK);
3134+
// Arrange the stack to make it more likely that we hit any lua_checkstack() misuse
3135+
int pre_pad_top = lua_gettop(L);
3136+
lua_checkstack(L, LUA_MINSTACK);
3137+
while (lua_gettop(L) != LUA_MINSTACK - 1) {
3138+
lua_pushnil(L);
3139+
}
3140+
// A reference to the root obj needs to end back up on top
3141+
lua_pushvalue(L, pre_pad_top);
3142+
eris_assert(lua_gettop(L) == LUA_MINSTACK);
31453143
#endif
31463144

3147-
u_header(&info);
3148-
unpersist(&info); /* perms reftbl nil? path? str? rootobj */
3145+
u_header(&info);
3146+
unpersist(&info); /* perms reftbl nil? path? str? rootobj */
31493147

31503148
#if HARDSTACKTESTS
3151-
// Slot the top of the stack back in where it should be
3152-
lua_replace(L, pre_pad_top + 1);
3153-
lua_pop(L, LUA_MINSTACK - pre_pad_top - 1);
3154-
eris_assert(lua_gettop(L) == pre_pad_top + 1);
3149+
// Slot the top of the stack back in where it should be
3150+
lua_replace(L, pre_pad_top + 1);
3151+
lua_pop(L, LUA_MINSTACK - pre_pad_top - 1);
3152+
eris_assert(lua_gettop(L) == pre_pad_top + 1);
31553153
#endif
3156-
}
31573154

31583155
if (info.generatePath) { /* perms reftbl nil path str? rootobj */
31593156
lua_remove(L, PATHIDX); /* perms reftbl nil str? rootobj */
@@ -3514,18 +3511,10 @@ eris_fork_thread(lua_State *Lforker, uint8_t default_state, uint8_t memcat) {
35143511
// Make sure any objects we create during deserialization are created with the desired memcat
35153512
lua_setmemcat(Lforker, memcat);
35163513

3517-
// We need to relax the bytes limit so that we don't fail due to garbage generated during deserialization.
3518-
int old_bytes_limit = Lforker->global->memcatbyteslimit;
3519-
lua_setmemcatbyteslimit(Lforker, 0);
35203514
int status = eris_unpersist(Lforker, -1, -2); /* Lforker: state serialized uperms new_th */
35213515

3522-
// Get rid of any garbage created during deserialization if we're using memcat-based memory limits
3523-
if (old_bytes_limit)
3524-
lua_gc(Lforker, LUA_GCCOLLECT, 0);
3525-
35263516
// Done, set the memcat back to the main one.
35273517
lua_setmemcat(Lforker, 0);
3528-
lua_setmemcatbyteslimit(Lforker, old_bytes_limit);
35293518

35303519
if (status == LUA_OK) {
35313520
eris_assert(lua_isthread(Lforker, -1));

VM/src/lapi.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1661,11 +1661,6 @@ int lua_getmemcat(lua_State* L)
16611661
return L->activememcat;
16621662
}
16631663

1664-
void lua_setmemcatbyteslimit(lua_State* L, lua_Unsigned bytes_limit)
1665-
{
1666-
L->global->memcatbyteslimit = bytes_limit;
1667-
}
1668-
16691664
size_t lua_totalbytes(lua_State* L, int category)
16701665
{
16711666
api_check(L, category < LUA_MEMORY_CATEGORIES);
@@ -2197,7 +2192,6 @@ CLANG_NOOPT void GCC_NOOPT lua_useconstsstate(lua_State *L, lua_State * constsL)
21972192
runtime_state->uuidCompressedWeakTab = consts_runtime_state->uuidCompressedWeakTab;
21982193
}
21992194

2200-
L->global->memcatbyteslimit = constsL->global->memcatbyteslimit;
22012195
*lua_callbacks(L) = *lua_callbacks(constsL);
22022196

22032197
// Make sure we didn't muck up `L` very much

VM/src/llsl.cpp

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,34 +1189,6 @@ YieldableStatus luaSL_may_interrupt(lua_State *L)
11891189
return YieldableStatus::OK;
11901190
}
11911191

1192-
void luaSL_emergencyfinishgc(lua_State *L)
1193-
{
1194-
global_State *g = L->global;
1195-
size_t limit = g->memcatbyteslimit;
1196-
if (!limit)
1197-
{
1198-
// Nothing to do here, we're not using memcat memory limits
1199-
// so we don't need to dynamically tune the GC.
1200-
return;
1201-
}
1202-
1203-
// We want GC to get more aggressive as memory fills up, but we don't
1204-
// want to waste a lot of time on GC if we're not really allocating
1205-
// things very fast anyway. Only start getting aggressive once we start
1206-
// getting near limits.
1207-
int cycle_count = 0;
1208-
while (true) {
1209-
// We need to fix `GCthreshold` so assertions in lgc hold true.
1210-
g->GCthreshold = std::min(g->GCthreshold, g->totalbytes);
1211-
1212-
// This logic is copied from Lua 5.1.4's emergency collector.
1213-
/* only allow the GC to finish at least 1 full cycle. */
1214-
if (g->gcstate == GCSpause && ++cycle_count > 1)
1215-
break;
1216-
luaC_step(L, false);
1217-
}
1218-
}
1219-
12201192
int luaSL_pushnativeinteger(lua_State *L, int val)
12211193
{
12221194
if (LUAU_IS_LSL_VM(L))

VM/src/lmem.cpp

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -507,15 +507,10 @@ void* luaM_new_(lua_State* L, size_t nsize, uint8_t memcat)
507507
global_State* g = L->global;
508508

509509
// ServerLua: enforce memory allocation limits in user-owned memcats
510-
if (memcat > 1 && g->memcatbyteslimit)
511-
{
512-
if (g->memcatbytes[memcat] + nsize >= g->memcatbyteslimit)
513-
luaD_throw(L, LUA_ERRMEM);
514-
}
515-
516510
if (LUAU_LIKELY(!!g->cb.beforeallocate) && memcat > 1)
517511
{
518-
if (g->cb.beforeallocate(L, 0, nsize))
512+
// But don't do invoke the hook if GC is disabled
513+
if (LUAU_LIKELY(g->GCthreshold != SIZE_MAX) && g->cb.beforeallocate(L, 0, nsize))
519514
luaD_throw(L, LUA_ERRMEM);
520515
}
521516

@@ -551,15 +546,9 @@ GCObject* luaM_newgco_(lua_State* L, size_t nsize, uint8_t memcat)
551546
global_State* g = L->global;
552547

553548
// ServerLua: enforce memory allocation limits in user-owned memcats
554-
if (memcat > 1 && g->memcatbyteslimit)
555-
{
556-
if (g->memcatbytes[memcat] + nsize >= g->memcatbyteslimit)
557-
luaD_throw(L, LUA_ERRMEM);
558-
}
559-
560549
if (LUAU_LIKELY(!!g->cb.beforeallocate) && memcat > 1)
561550
{
562-
if (g->cb.beforeallocate(L, 0, nsize))
551+
if (LUAU_LIKELY(g->GCthreshold != SIZE_MAX) && g->cb.beforeallocate(L, 0, nsize))
563552
luaD_throw(L, LUA_ERRMEM);
564553
}
565554

@@ -669,15 +658,9 @@ void* luaM_realloc_(lua_State* L, void* block, size_t osize, size_t nsize, uint8
669658
LUAU_ASSERT((osize == 0) == (block == NULL));
670659

671660
// ServerLua: enforce memory allocation limits in user-owned memcats
672-
if (memcat > 1 && g->memcatbyteslimit)
673-
{
674-
if (g->memcatbytes[memcat] - osize + nsize >= g->memcatbyteslimit)
675-
luaD_throw(L, LUA_ERRMEM);
676-
}
677-
678661
if (LUAU_LIKELY(!!g->cb.beforeallocate) && memcat > 1)
679662
{
680-
if (g->cb.beforeallocate(L, 0, nsize))
663+
if (LUAU_LIKELY(g->GCthreshold != SIZE_MAX) && g->cb.beforeallocate(L, osize, nsize))
681664
luaD_throw(L, LUA_ERRMEM);
682665
}
683666

VM/src/lstate.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,6 @@ lua_State* lua_newstate(lua_Alloc f, void* ud)
233233

234234
g->memcatbytes[0] = sizeof(LG);
235235
// ServerLua: additional book-keeping
236-
g->memcatbyteslimit = 0;
237236
g->calltailinterruptcheck = 0;
238237

239238
g->cb = lua_Callbacks();

VM/src/lstate.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,6 @@ typedef struct global_State
235235
#ifdef LUAI_GCMETRICS
236236
GCMetrics gcmetrics;
237237
#endif
238-
// ServerLua: byte allocation limit for memcats above 1,
239-
// memcat "1" is treated as the "garbage" memcat, and everything in
240-
// it is immediately collectible. 0 is the "system" memcat, and is
241-
// used for all allocations that users don't directly control.
242-
uint32_t memcatbyteslimit;
243238
} global_State;
244239
// clang-format on
245240

tests/Conformance.test.cpp

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,9 @@ TEST_CASE("Ares bad serialize state")
959959
luaC_validate(GL);
960960
}
961961

962+
// Used just for this test
963+
static lua_OpaqueGCObjectSet free_objects;
964+
962965
TEST_CASE("ServerLua memory limits")
963966
{
964967
std::string source = getConformanceTestSource("serverlua_memory.lua");
@@ -1008,8 +1011,34 @@ TEST_CASE("ServerLua memory limits")
10081011
}
10091012

10101013
lua_State* Lforker = eris_make_forkserver(L);
1011-
// 10kb memory limit
1012-
lua_setmemcatbyteslimit(Lforker, 10000);
1014+
1015+
// Collect free objects from the loaded bytecode
1016+
free_objects = lua_collectfreeobjects(Lforker);
1017+
1018+
// 10kb memory limit using beforeallocate callback with reachability-based accounting
1019+
lua_callbacks(Lforker)->beforeallocate = [](lua_State* L, size_t osize, size_t nsize) -> int {
1020+
constexpr size_t MAX_MEM = 10000;
1021+
static size_t actual_size = 0;
1022+
static size_t approximate_size = 0;
1023+
1024+
// Ignore shrinking allocations
1025+
if (osize >= nsize)
1026+
return 0;
1027+
1028+
size_t net_gain = nsize - osize;
1029+
1030+
// Re-calculate actual size if we haven't yet or if approximate would exceed limit
1031+
if (actual_size == 0 || (approximate_size + net_gain > MAX_MEM))
1032+
{
1033+
approximate_size = actual_size = lua_userthreadsize(L, &free_objects);
1034+
1035+
if (actual_size + net_gain > MAX_MEM)
1036+
return 1;
1037+
}
1038+
1039+
approximate_size += net_gain;
1040+
return 0;
1041+
};
10131042
// Don't need the original thread on the main stack anymore
10141043
lua_remove(GL, -2);
10151044

tests/conformance/serverlua_memory.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ local success, ret = xpcall(
1414
end)
1515
assert(not success)
1616
assert(ret == "not enough memory")
17+
assert(#foo > 225)
1718
assert(#foo < 20000)
1819

1920
return 'OK'

0 commit comments

Comments
 (0)