Lua_API lua_atpanic

开篇

今天继续往下解析第 4 个 API :lua_atpanic 。

解析

释义

panic 有崩溃的意思。望文生义,我们可以猜测 lua_atpanic 是处置 Lua 程序崩溃时的方法。且看官方参考文档对 lua_atpanic 的解释:

Sets a new panic function and returns the old one (see §4.6).
设置一个新的 panic 函数,并返回之前设置的那个。

§4.6 – Error Handling in C

在内部实现中,Lua 使用了 C 的 longjmp 机制来处理错误。(如果你使用 C++ 编译,Lua 将换成异常;细节请在源代码中搜索 LUAI_THROW。) 当 Lua 碰到任何错误 (比如内存分配错误、类型错误、语法错误、还有运行时错误)它都会抛出一个错误出去; 也就是调用一次长跳转。在保护环境下,Lua 使用 setjmp 来设置一个恢复点;任何发生的错误都会跳转到最近的一个恢复点。

如果错误发生在保护环境之外, Lua 会先调用 panic 函数 (参见 lua_atpanic) 然后调用 abort 来退出宿主程序。 你的 panic 函数只要不返回 (例如:长跳转到你在 Lua 外你自己设置的恢复点) 就可以不退出程序。panic 函数以错误消息处理器(参见 §2.3)的方式运行;错误消息在栈顶。 不同的是,它不保证栈空间。 做任何压栈操作前,panic 函数都必须先检查是否有足够的空间 (参见 §4.2)。

大多数 API 函数都有可能抛出错误,例如在内存分配错误时就会抛出。每个函数的文档都会注明它是否可能抛出错误。

在 C 函数内部,你可以通过调用 lua_error 来抛出错误。

实现

LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) {
  lua_CFunction old;
  lua_lock(L);
  old = G(L)->panic;
  G(L)->panic = panicf;
  lua_unlock(L);
  return old;
}

其中,lua_CFunction 是个函数指针,定义在 lua.h 105:

/*
** Type for C functions registered with Lua
*/
typedef int (*lua_CFunction) (lua_State *L);

G(L) 是个宏定义,意在取得 Ll_G 域中的数据:

// lstate.h 186
#define G(L)    (L->l_G)

我们之前一直没有分析过 lua_State 的结构,原因是因为它太复杂了,一时半会讲不清楚,只好一层一层去剥开,去理解。现在我们找到它的定义:

/*
** 'per thread' state
*/
struct lua_State {
  CommonHeader;
  unsigned short nci;  /* number of items in 'ci' list */
  lu_byte status;
  StkId top;  /* first free slot in the stack */
  global_State *l_G;
  CallInfo *ci;  /* call info for current function */
  const Instruction *oldpc;  /* last pc traced */
  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) */
  int stacksize;
  int basehookcount;
  int hookcount;
  unsigned short nny;  /* number of non-yieldable calls in stack */
  unsigned short nCcalls;  /* number of nested C calls */
  l_signalT hookmask;
  lu_byte allowhook;
};

看到 l_Gglobal_State * 类型,global_State 是个结构体:

/*
** '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 GCmemtrav;  /* memory traversed by the GC */
  lu_mem GCestimate;  /* an estimate of the non-garbage memory in use */
  stringtable strt;  /* hash table for strings */
  TValue l_registry;
  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 gcrunning;  /* true if GC is running */
  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 */
  struct lua_State *twups;  /* list of threads with open upvalues */
  unsigned int gcfinnum;  /* number of finalizers to call in each GC step */
  int gcpause;  /* size of pause between successive GCs */
  int gcstepmul;  /* GC 'granularity' */
  lua_CFunction panic;  /* to be called in unprotected errors */
  struct lua_State *mainthread;
  const lua_Number *version;  /* pointer to version number */
  TString *memerrmsg;  /* memory-error message */
  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 */
} global_State;

可以看出,panicglobal_State 的一个成员,它在发生未保护的错误时会被调用。
因此,lua_atpanic 的作用就是将 global_State 的成员 panic 替换成 panicf,最后返回 panic 给调用者。

我们搜索下源码,看看 lua_atpanic 是如何调用的:

// lauxlib.h 1011
static int panic (lua_State *L) {
  lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n",
                        lua_tostring(L, -1));
  return 0;  /* return to Lua to abort */
}


LUALIB_API lua_State *luaL_newstate (void) {
  lua_State *L = lua_newstate(l_alloc, NULL);
  if (L) lua_atpanic(L, &panic);
  return L;
}

原来 Lua 默认定义了一个 panic 函数,并且在创建新的 lua_State 线程状态的时候都会将默认的 panic 注册到 global_State 下。

其实不太懂在注册新的 panic 函数时为什么要返回之前的 panic 函数,可能是为了暂时替换而后还需要恢复吧。现在说不清楚,因为没有可应用的场景或者想象不出那样的应用场景,且留待之后回答。

终了

  • 过于复杂的结构可以先放过,不求甚解,但脑袋里一定得有点概念。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容

  • 第一篇 语言 第0章 序言 Lua仅让你用少量的代码解决关键问题。 Lua所提供的机制是C不擅长的:高级语言,动态...
    testfor阅读 2,672评论 1 7
  • C API 云风Blog:Lua C API 的正确用法 C读取和调用Lua文件的库:lua.h, lauxlib...
    SysuYe阅读 5,659评论 2 10
  • 当在Lua和C之间交换数据时主要的问题是自动回收与手动回收内存管理的不一致。因此,Lua 用一个抽象的栈在Lua与...
    luffier阅读 2,659评论 0 3
  • Lua 是单线程的,但是Lua却有thread类型,显然直觉上Lua的thread并非通常意义上的线程,实际上它是...
    ffl阅读 4,690评论 2 5
  • 文/玛嗒嗒 如果皮囊朽坏,我们还剩下什么?好吧,你告诉我,还有灵魂。有吗?有的吧。—你都有点像祥林嫂了,好吧好吧,...
    玛嗒嗒阅读 1,970评论 19 54