/* ** Garbage collector. ** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h */ #ifndef _LJ_GC_H #define _LJ_GC_H #include "lj_obj.h" /* Garbage collector states. Order matters. */ enum { GCSpause, GCSpropagate, GCSatomic, GCSsweepstring, GCSsweep, GCSfinalize }; /* Bitmasks for marked field of GCobj. */ #define LJ_GC_WHITE0 0x01 #define LJ_GC_WHITE1 0x02 #define LJ_GC_BLACK 0x04 #define LJ_GC_FINALIZED 0x08 #define LJ_GC_WEAKKEY 0x08 #define LJ_GC_WEAKVAL 0x10 #define LJ_GC_CDATA_FIN 0x10 #define LJ_GC_FIXED 0x20 #define LJ_GC_SFIXED 0x40 #define LJ_GC_WHITES (LJ_GC_WHITE0 | LJ_GC_WHITE1) #define LJ_GC_COLORS (LJ_GC_WHITES | LJ_GC_BLACK) #define LJ_GC_WEAK (LJ_GC_WEAKKEY | LJ_GC_WEAKVAL) /* Macros to test and set GCobj colors. */ #define iswhite(x) ((x)->gch.marked & LJ_GC_WHITES) #define isblack(x) ((x)->gch.marked & LJ_GC_BLACK) #define isgray(x) (!((x)->gch.marked & (LJ_GC_BLACK|LJ_GC_WHITES))) #define tviswhite(x) (tvisgcv(x) && iswhite(gcV(x))) #define otherwhite(g) (g->gc.currentwhite ^ LJ_GC_WHITES) #define isdead(g, v) ((v)->gch.marked & otherwhite(g) & LJ_GC_WHITES) #define curwhite(g) ((g)->gc.currentwhite & LJ_GC_WHITES) #define newwhite(g, x) (obj2gco(x)->gch.marked = (uint8_t)curwhite(g)) #define makewhite(g, x) \ ((x)->gch.marked = ((x)->gch.marked & (uint8_t)~LJ_GC_COLORS) | curwhite(g)) #define flipwhite(x) ((x)->gch.marked ^= LJ_GC_WHITES) #define black2gray(x) ((x)->gch.marked &= (uint8_t)~LJ_GC_BLACK) #define fixstring(s) ((s)->marked |= LJ_GC_FIXED) #define markfinalized(x) ((x)->gch.marked |= LJ_GC_FINALIZED) /* Collector. */ LJ_FUNC size_t lj_gc_separateudata(global_State *g, int all); LJ_FUNC void lj_gc_finalize_udata(lua_State *L); #if LJ_HASFFI LJ_FUNC void lj_gc_finalize_cdata(lua_State *L); #else #define lj_gc_finalize_cdata(L) UNUSED(L) #endif LJ_FUNC void lj_gc_freeall(global_State *g); LJ_FUNCA int LJ_FASTCALL lj_gc_step(lua_State *L); LJ_FUNCA void LJ_FASTCALL lj_gc_step_fixtop(lua_State *L); #if LJ_HASJIT LJ_FUNC int LJ_FASTCALL lj_gc_step_jit(global_State *g, MSize steps); #endif LJ_FUNC void lj_gc_fullgc(lua_State *L); /* GC check: drive collector forward if the GC threshold has been reached. */ #define lj_gc_check(L) \ { if (LJ_UNLIKELY(G(L)->gc.total >= G(L)->gc.threshold)) \ lj_gc_step(L); } #define lj_gc_check_fixtop(L) \ { if (LJ_UNLIKELY(G(L)->gc.total >= G(L)->gc.threshold)) \ lj_gc_step_fixtop(L); } /* Write barriers. */ LJ_FUNC void lj_gc_barrierf(global_State *g, GCobj *o, GCobj *v); LJ_FUNCA void LJ_FASTCALL lj_gc_barrieruv(global_State *g, TValue *tv); LJ_FUNC void lj_gc_closeuv(global_State *g, GCupval *uv); #if LJ_HASJIT LJ_FUNC void lj_gc_barriertrace(global_State *g, uint32_t traceno); #endif /* Move the GC propagation frontier back for tables (make it gray again). */ static LJ_AINLINE void lj_gc_barrierback(global_State *g, GCtab *t) { GCobj *o = obj2gco(t); lj_assertG(isblack(o) && !isdead(g, o), "bad object states for backward barrier"); lj_assertG(g->gc.state != GCSfinalize && g->gc.state != GCSpause, "bad GC state"); black2gray(o); setgcrefr(t->gclist, g->gc.grayagain); setgcref(g->gc.grayagain, o); } /* Barrier for stores to table objects. TValue and GCobj variant. */ #define lj_gc_anybarriert(L, t) \ { if (LJ_UNLIKELY(isblack(obj2gco(t)))) lj_gc_barrierback(G(L), (t)); } #define lj_gc_barriert(L, t, tv) \ { if (tviswhite(tv) && isblack(obj2gco(t))) \ lj_gc_barrierback(G(L), (t)); } #define lj_gc_objbarriert(L, t, o) \ { if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) \ lj_gc_barrierback(G(L), (t)); } /* Barrier for stores to any other object. TValue and GCobj variant. */ #define lj_gc_barrier(L, p, tv) \ { if (tviswhite(tv) && isblack(obj2gco(p))) \ lj_gc_barrierf(G(L), obj2gco(p), gcV(tv)); } #define lj_gc_objbarrier(L, p, o) \ { if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \ lj_gc_barrierf(G(L), obj2gco(p), obj2gco(o)); } /* Allocator. */ LJ_FUNC void *lj_mem_realloc(lua_State *L, void *p, GCSize osz, GCSize nsz); LJ_FUNC void * LJ_FASTCALL lj_mem_newgco(lua_State *L, GCSize size); LJ_FUNC void *lj_mem_grow(lua_State *L, void *p, MSize *szp, MSize lim, MSize esz); #define lj_mem_new(L, s) lj_mem_realloc(L, NULL, 0, (s)) static LJ_AINLINE void lj_mem_free(global_State *g, void *p, size_t osize) { g->gc.total -= (GCSize)osize; g->allocf(g->allocd, p, osize, 0); } #define lj_mem_newvec(L, n, t) ((t *)lj_mem_new(L, (GCSize)((n)*sizeof(t)))) #define lj_mem_reallocvec(L, p, on, n, t) \ ((p) = (t *)lj_mem_realloc(L, p, (on)*sizeof(t), (GCSize)((n)*sizeof(t)))) #define lj_mem_growvec(L, p, n, m, t) \ ((p) = (t *)lj_mem_grow(L, (p), &(n), (m), (MSize)sizeof(t))) #define lj_mem_freevec(g, p, n, t) lj_mem_free(g, (p), (n)*sizeof(t)) #define lj_mem_newobj(L, t) ((t *)lj_mem_newgco(L, sizeof(t))) #define lj_mem_newt(L, s, t) ((t *)lj_mem_new(L, (s))) #define lj_mem_freet(g, p) lj_mem_free(g, (p), sizeof(*(p))) #endif