mirror of
https://github.com/lua/lua
synced 2024-11-24 21:59:41 +03:00
0dc5deca1c
OLD1 objects can be potentially anywhere in the 'allgc' list (up to 'reallyold'), but frequently they are all after 'old1' (natural evolution of survivals) or do not exist at all (when all objects die young). So, instead of 'markold' starts looking for them always from the start of 'allgc', the collector keeps an extra pointer, 'firstold1', that points to the first OLD1 object in the 'allgc' list, or is NULL if there are no OLD1 objects in that list.
388 lines
14 KiB
C
388 lines
14 KiB
C
/*
|
|
** $Id: lstate.h $
|
|
** Global State
|
|
** See Copyright Notice in lua.h
|
|
*/
|
|
|
|
#ifndef lstate_h
|
|
#define lstate_h
|
|
|
|
#include "lua.h"
|
|
|
|
#include "lobject.h"
|
|
#include "ltm.h"
|
|
#include "lzio.h"
|
|
|
|
|
|
/*
|
|
** Some notes about garbage-collected objects: All objects in Lua must
|
|
** be kept somehow accessible until being freed, so all objects always
|
|
** belong to one (and only one) of these lists, using field 'next' of
|
|
** the 'CommonHeader' for the link:
|
|
**
|
|
** 'allgc': all objects not marked for finalization;
|
|
** 'finobj': all objects marked for finalization;
|
|
** 'tobefnz': all objects ready to be finalized;
|
|
** 'fixedgc': all objects that are not to be collected (currently
|
|
** only small strings, such as reserved words).
|
|
**
|
|
** For the generational collector, some of these lists have marks for
|
|
** generations. Each mark points to the first element in the list for
|
|
** that particular generation; that generation goes until the next mark.
|
|
**
|
|
** 'allgc' -> 'survival': new objects;
|
|
** 'survival' -> 'old': objects that survived one collection;
|
|
** 'old1' -> 'reallyold': objects that became old in last collection;
|
|
** 'reallyold' -> NULL: objects old for more than one cycle.
|
|
**
|
|
** 'finobj' -> 'finobjsur': new objects marked for finalization;
|
|
** 'finobjsur' -> 'finobjold1': survived """";
|
|
** 'finobjold1' -> 'finobjrold': just old """";
|
|
** 'finobjrold' -> NULL: really old """".
|
|
**
|
|
** All lists can contain elements older than their main ages, due
|
|
** to 'luaC_checkfinalizer' and 'udata2finalize', which move
|
|
** objects between the normal lists and the "marked for finalization"
|
|
** lists. Moreover, barriers can age young objects in young lists as
|
|
** OLD0, which then become OLD1. However, a list never contains
|
|
** elements younger than their main ages.
|
|
**
|
|
** The generational collector also uses a pointer 'firstold1', which
|
|
** points to the first OLD1 object in the list. It is used to optimize
|
|
** 'markold'. (Potentially OLD1 objects can be anywhere between 'allgc'
|
|
** and 'reallyold', but often the list has no OLD1 objects or they are
|
|
** after 'old1'.) Note the difference between it and 'old1':
|
|
** 'firstold1': no OLD1 objects before this point; there can be all
|
|
** ages after it.
|
|
** 'old1': no objects younger than OLD1 after this point.
|
|
*/
|
|
|
|
/*
|
|
** Moreover, there is another set of lists that control gray objects.
|
|
** These lists are linked by fields 'gclist'. (All objects that
|
|
** can become gray have such a field. The field is not the same
|
|
** in all objects, but it always has this name.) Any gray object
|
|
** must belong to one of these lists, and all objects in these lists
|
|
** must be gray (with one exception explained below):
|
|
**
|
|
** 'gray': regular gray objects, still waiting to be visited.
|
|
** 'grayagain': objects that must be revisited at the atomic phase.
|
|
** That includes
|
|
** - black objects got in a write barrier;
|
|
** - all kinds of weak tables during propagation phase;
|
|
** - all threads.
|
|
** 'weak': tables with weak values to be cleared;
|
|
** 'ephemeron': ephemeron tables with white->white entries;
|
|
** 'allweak': tables with weak keys and/or weak values to be cleared.
|
|
**
|
|
** The exception to that "gray rule" is the TOUCHED2 objects in
|
|
** generational mode. Those objects stay in a gray list (because they
|
|
** must be visited again at the end of the cycle), but they are marked
|
|
** black (because assignments to them must activate barriers, to move
|
|
** them back to TOUCHED1).
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
** About 'nCcalls': each thread in Lua (a lua_State) keeps a count of
|
|
** how many "C calls" it still can do in the C stack, to avoid C-stack
|
|
** overflow. This count is very rough approximation; it considers only
|
|
** recursive functions inside the interpreter, as non-recursive calls
|
|
** can be considered using a fixed (although unknown) amount of stack
|
|
** space.
|
|
**
|
|
** The count has two parts: the lower part is the count itself; the
|
|
** higher part counts the number of non-yieldable calls in the stack.
|
|
** (They are together so that we can change both with one instruction.)
|
|
**
|
|
** Because calls to external C functions can use an unknown amount
|
|
** of space (e.g., functions using an auxiliary buffer), calls
|
|
** to these functions add more than one to the count (see CSTACKCF).
|
|
**
|
|
** The proper count excludes the number of CallInfo structures allocated
|
|
** by Lua, as a kind of "potential" calls. So, when Lua calls a function
|
|
** (and "consumes" one CallInfo), it needs neither to decrement nor to
|
|
** check 'nCcalls', as its use of C stack is already accounted for.
|
|
*/
|
|
|
|
/* number of "C stack slots" used by an external C function */
|
|
#define CSTACKCF 10
|
|
|
|
|
|
/*
|
|
** The C-stack size is sliced in the following zones:
|
|
** - larger than CSTACKERR: normal stack;
|
|
** - [CSTACKMARK, CSTACKERR]: buffer zone to signal a stack overflow;
|
|
** - [CSTACKCF, CSTACKERRMARK]: error-handling zone;
|
|
** - below CSTACKERRMARK: buffer zone to signal overflow during overflow;
|
|
** (Because the counter can be decremented CSTACKCF at once, we need
|
|
** the so called "buffer zones", with at least that size, to properly
|
|
** detect a change from one zone to the next.)
|
|
*/
|
|
#define CSTACKERR (8 * CSTACKCF)
|
|
#define CSTACKMARK (CSTACKERR - (CSTACKCF + 2))
|
|
#define CSTACKERRMARK (CSTACKCF + 2)
|
|
|
|
|
|
/* initial limit for the C-stack of threads */
|
|
#define CSTACKTHREAD (2 * CSTACKERR)
|
|
|
|
|
|
/* true if this thread does not have non-yieldable calls in the stack */
|
|
#define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0)
|
|
|
|
/* real number of C calls */
|
|
#define getCcalls(L) ((L)->nCcalls & 0xffff)
|
|
|
|
|
|
/* Increment the number of non-yieldable calls */
|
|
#define incnny(L) ((L)->nCcalls += 0x10000)
|
|
|
|
/* Decrement the number of non-yieldable calls */
|
|
#define decnny(L) ((L)->nCcalls -= 0x10000)
|
|
|
|
/* Increment the number of non-yieldable calls and decrement nCcalls */
|
|
#define incXCcalls(L) ((L)->nCcalls += 0x10000 - CSTACKCF)
|
|
|
|
/* Decrement the number of non-yieldable calls and increment nCcalls */
|
|
#define decXCcalls(L) ((L)->nCcalls -= 0x10000 - CSTACKCF)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct lua_longjmp; /* defined in ldo.c */
|
|
|
|
|
|
/*
|
|
** Atomic type (relative to signals) to better ensure that 'lua_sethook'
|
|
** is thread safe
|
|
*/
|
|
#if !defined(l_signalT)
|
|
#include <signal.h>
|
|
#define l_signalT sig_atomic_t
|
|
#endif
|
|
|
|
|
|
/* extra stack space to handle TM calls and some other extras */
|
|
#define EXTRA_STACK 5
|
|
|
|
|
|
#define BASIC_STACK_SIZE (2*LUA_MINSTACK)
|
|
|
|
|
|
/* kinds of Garbage Collection */
|
|
#define KGC_INC 0 /* incremental gc */
|
|
#define KGC_GEN 1 /* generational gc */
|
|
|
|
|
|
typedef struct stringtable {
|
|
TString **hash;
|
|
int nuse; /* number of elements */
|
|
int size;
|
|
} stringtable;
|
|
|
|
|
|
/*
|
|
** Information about a call.
|
|
*/
|
|
typedef struct CallInfo {
|
|
StkId func; /* function index in the stack */
|
|
StkId top; /* top for this function */
|
|
struct CallInfo *previous, *next; /* dynamic call link */
|
|
union {
|
|
struct { /* only for Lua functions */
|
|
const Instruction *savedpc;
|
|
volatile l_signalT trap;
|
|
int nextraargs; /* # of extra arguments in vararg functions */
|
|
} l;
|
|
struct { /* only for C functions */
|
|
lua_KFunction k; /* continuation in case of yields */
|
|
ptrdiff_t old_errfunc;
|
|
lua_KContext ctx; /* context info. in case of yields */
|
|
} c;
|
|
} u;
|
|
union {
|
|
int funcidx; /* called-function index */
|
|
int nyield; /* number of values yielded */
|
|
struct { /* info about transferred values (for call/return hooks) */
|
|
unsigned short ftransfer; /* offset of first value transferred */
|
|
unsigned short ntransfer; /* number of values transferred */
|
|
} transferinfo;
|
|
} u2;
|
|
short nresults; /* expected number of results from this function */
|
|
unsigned short callstatus;
|
|
} CallInfo;
|
|
|
|
|
|
/*
|
|
** Bits in CallInfo status
|
|
*/
|
|
#define CIST_OAH (1<<0) /* original value of 'allowhook' */
|
|
#define CIST_C (1<<1) /* call is running a C function */
|
|
#define CIST_HOOKED (1<<2) /* call is running a debug hook */
|
|
#define CIST_YPCALL (1<<3) /* call is a yieldable protected call */
|
|
#define CIST_TAIL (1<<4) /* call was tail called */
|
|
#define CIST_HOOKYIELD (1<<5) /* last hook called yielded */
|
|
#define CIST_FIN (1<<6) /* call is running a finalizer */
|
|
#define CIST_TRAN (1<<7) /* 'ci' has transfer information */
|
|
#if defined(LUA_COMPAT_LT_LE)
|
|
#define CIST_LEQ (1<<8) /* using __lt for __le */
|
|
#endif
|
|
|
|
/* active function is a Lua function */
|
|
#define isLua(ci) (!((ci)->callstatus & CIST_C))
|
|
|
|
/* call is running Lua code (not a hook) */
|
|
#define isLuacode(ci) (!((ci)->callstatus & (CIST_C | CIST_HOOKED)))
|
|
|
|
/* assume that CIST_OAH has offset 0 and that 'v' is strictly 0/1 */
|
|
#define setoah(st,v) ((st) = ((st) & ~CIST_OAH) | (v))
|
|
#define getoah(st) ((st) & CIST_OAH)
|
|
|
|
|
|
/*
|
|
** 'global state', shared by all threads of this state
|
|
*/
|
|
typedef struct global_State {
|
|
lua_Alloc frealloc; /* function to reallocate memory */
|
|
void *ud; /* auxiliary data to 'frealloc' */
|
|
l_mem totalbytes; /* number of bytes currently allocated - GCdebt */
|
|
l_mem GCdebt; /* bytes allocated not yet compensated by the collector */
|
|
lu_mem GCestimate; /* an estimate of the non-garbage memory in use */
|
|
lu_mem lastatomic; /* see function 'genstep' in file 'lgc.c' */
|
|
stringtable strt; /* hash table for strings */
|
|
TValue l_registry;
|
|
TValue nilvalue; /* a nil value */
|
|
unsigned int seed; /* randomized seed for hashes */
|
|
lu_byte currentwhite;
|
|
lu_byte gcstate; /* state of garbage collector */
|
|
lu_byte gckind; /* kind of GC running */
|
|
lu_byte genminormul; /* control for minor generational collections */
|
|
lu_byte genmajormul; /* control for major generational collections */
|
|
lu_byte gcrunning; /* true if GC is running */
|
|
lu_byte gcemergency; /* true if this is an emergency collection */
|
|
lu_byte gcpause; /* size of pause between successive GCs */
|
|
lu_byte gcstepmul; /* GC "speed" */
|
|
lu_byte gcstepsize; /* (log2 of) GC granularity */
|
|
GCObject *allgc; /* list of all collectable objects */
|
|
GCObject **sweepgc; /* current position of sweep in list */
|
|
GCObject *finobj; /* list of collectable objects with finalizers */
|
|
GCObject *gray; /* list of gray objects */
|
|
GCObject *grayagain; /* list of objects to be traversed atomically */
|
|
GCObject *weak; /* list of tables with weak values */
|
|
GCObject *ephemeron; /* list of ephemeron tables (weak keys) */
|
|
GCObject *allweak; /* list of all-weak tables */
|
|
GCObject *tobefnz; /* list of userdata to be GC */
|
|
GCObject *fixedgc; /* list of objects not to be collected */
|
|
/* fields for generational collector */
|
|
GCObject *survival; /* start of objects that survived one GC cycle */
|
|
GCObject *old1; /* start of old1 objects */
|
|
GCObject *reallyold; /* objects more than one cycle old ("really old") */
|
|
GCObject *firstold1; /* first OLD1 object in the list (if any) */
|
|
GCObject *finobjsur; /* list of survival objects with finalizers */
|
|
GCObject *finobjold1; /* list of old1 objects with finalizers */
|
|
GCObject *finobjrold; /* list of really old objects with finalizers */
|
|
struct lua_State *twups; /* list of threads with open upvalues */
|
|
lua_CFunction panic; /* to be called in unprotected errors */
|
|
struct lua_State *mainthread;
|
|
TString *memerrmsg; /* message for memory-allocation errors */
|
|
TString *tmname[TM_N]; /* array with tag-method names */
|
|
struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */
|
|
TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */
|
|
lua_WarnFunction warnf; /* warning function */
|
|
void *ud_warn; /* auxiliary data to 'warnf' */
|
|
unsigned int Cstacklimit; /* current limit for the C stack */
|
|
} global_State;
|
|
|
|
|
|
/*
|
|
** 'per thread' state
|
|
*/
|
|
struct lua_State {
|
|
CommonHeader;
|
|
lu_byte status;
|
|
lu_byte allowhook;
|
|
unsigned short nci; /* number of items in 'ci' list */
|
|
StkId top; /* first free slot in the stack */
|
|
global_State *l_G;
|
|
CallInfo *ci; /* call info for current function */
|
|
StkId stack_last; /* last free slot in the stack */
|
|
StkId stack; /* stack base */
|
|
UpVal *openupval; /* list of open upvalues in this stack */
|
|
GCObject *gclist;
|
|
struct lua_State *twups; /* list of threads with open upvalues */
|
|
struct lua_longjmp *errorJmp; /* current error recover point */
|
|
CallInfo base_ci; /* CallInfo for first level (C calling Lua) */
|
|
volatile lua_Hook hook;
|
|
ptrdiff_t errfunc; /* current error handling function (stack index) */
|
|
l_uint32 nCcalls; /* number of allowed nested C calls - 'nci' */
|
|
int oldpc; /* last pc traced */
|
|
int stacksize;
|
|
int basehookcount;
|
|
int hookcount;
|
|
volatile l_signalT hookmask;
|
|
};
|
|
|
|
|
|
#define G(L) (L->l_G)
|
|
|
|
|
|
/*
|
|
** Union of all collectable objects (only for conversions)
|
|
*/
|
|
union GCUnion {
|
|
GCObject gc; /* common header */
|
|
struct TString ts;
|
|
struct Udata u;
|
|
union Closure cl;
|
|
struct Table h;
|
|
struct Proto p;
|
|
struct lua_State th; /* thread */
|
|
struct UpVal upv;
|
|
};
|
|
|
|
|
|
#define cast_u(o) cast(union GCUnion *, (o))
|
|
|
|
/* macros to convert a GCObject into a specific value */
|
|
#define gco2ts(o) \
|
|
check_exp(novariant((o)->tt) == LUA_TSTRING, &((cast_u(o))->ts))
|
|
#define gco2u(o) check_exp((o)->tt == LUA_VUSERDATA, &((cast_u(o))->u))
|
|
#define gco2lcl(o) check_exp((o)->tt == LUA_VLCL, &((cast_u(o))->cl.l))
|
|
#define gco2ccl(o) check_exp((o)->tt == LUA_VCCL, &((cast_u(o))->cl.c))
|
|
#define gco2cl(o) \
|
|
check_exp(novariant((o)->tt) == LUA_TFUNCTION, &((cast_u(o))->cl))
|
|
#define gco2t(o) check_exp((o)->tt == LUA_VTABLE, &((cast_u(o))->h))
|
|
#define gco2p(o) check_exp((o)->tt == LUA_VPROTO, &((cast_u(o))->p))
|
|
#define gco2th(o) check_exp((o)->tt == LUA_VTHREAD, &((cast_u(o))->th))
|
|
#define gco2upv(o) check_exp((o)->tt == LUA_VUPVAL, &((cast_u(o))->upv))
|
|
|
|
|
|
/*
|
|
** macro to convert a Lua object into a GCObject
|
|
** (The access to 'tt' tries to ensure that 'v' is actually a Lua object.)
|
|
*/
|
|
#define obj2gco(v) check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc))
|
|
|
|
|
|
/* actual number of total bytes allocated */
|
|
#define gettotalbytes(g) cast(lu_mem, (g)->totalbytes + (g)->GCdebt)
|
|
|
|
LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt);
|
|
LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
|
|
LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
|
|
LUAI_FUNC void luaE_freeCI (lua_State *L);
|
|
LUAI_FUNC void luaE_shrinkCI (lua_State *L);
|
|
LUAI_FUNC void luaE_enterCcall (lua_State *L);
|
|
LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont);
|
|
LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where);
|
|
|
|
|
|
#define luaE_exitCcall(L) ((L)->nCcalls++)
|
|
|
|
#endif
|
|
|