Lua5.3源码理解--异常处理

1 非本地跳转

/*
** LUAI_THROW/LUAI_TRY define how Lua does exception handling. By
** default, Lua handles errors with exceptions when compiling as
** C++ code, with _longjmp/_setjmp when asked to use them, and with
** longjmp/setjmp otherwise.
*/
#if !defined(LUAI_THROW)                /* { */

#if defined(__cplusplus) && !defined(LUA_USE_LONGJMP)   /* { */

/* C++ exceptions */
#define LUAI_THROW(L,c)     throw(c)
#define LUAI_TRY(L,c,a) \
    try { a } catch(...) { if ((c)->status == 0) (c)->status = -1; }
#define luai_jmpbuf     int  /* dummy variable */

#elif defined(LUA_USE_POSIX)                /* }{ */

/* in POSIX, try _longjmp/_setjmp (more efficient) */
#define LUAI_THROW(L,c)     _longjmp((c)->b, 1)
#define LUAI_TRY(L,c,a)     if (_setjmp((c)->b) == 0) { a }
#define luai_jmpbuf     jmp_buf

#else                           /* }{ */

/* ISO C handling with long jumps */
#define LUAI_THROW(L,c)     longjmp((c)->b, 1)
#define LUAI_TRY(L,c,a)     if (setjmp((c)->b) == 0) { a }
#define luai_jmpbuf     jmp_buf

#endif                          /* } */

#endif                          /* } */

通过以上源码可以看到非c++和非posix情况下,是使用非本地跳转(setjmp/longjmp)实现异常处理的。

1.1 非本地跳转<setjmp.h>知识点摘抄

setjmp和longjmp函数提供了一种类似goto语句的机制,但它并不局限于一个函数的作用域之内。这些函数常用于深层嵌套的函数调用链。如果在某个低层的函数中检测到一个错误,你可以立即返回到顶层函数,不必向调用链中的每个中间层函数返回一个错误标志。

2 应用

2.1 调用的地方

/* chain list of long jump buffers */
struct lua_longjmp {
  struct lua_longjmp *previous;
  luai_jmpbuf b;
  volatile int status;  /* error code */
};

l_noret luaD_throw (lua_State *L, int errcode) {
  if (L->errorJmp) {  /* thread has an error handler? */
    L->errorJmp->status = errcode;  /* set status */
    LUAI_THROW(L, L->errorJmp);  /* jump to it */
  }
  else {  /* thread has no error handler */
    global_State *g = G(L);
    L->status = cast_byte(errcode);  /* mark it as dead */
    if (g->mainthread->errorJmp) {  /* main thread has a handler? */
      setobjs2s(L, g->mainthread->top++, L->top - 1);  /* copy error obj. */
      luaD_throw(g->mainthread, errcode);  /* re-throw in main thread */
    }
    else {  /* no handler at all; abort */
      if (g->panic) {  /* panic function? */
        seterrorobj(L, errcode, L->top);  /* assume EXTRA_STACK */
        if (L->ci->top < L->top)
          L->ci->top = L->top;  /* pushing msg. can break this invariant */
        lua_unlock(L);
        g->panic(L);  /* call panic function (last chance to jump out) */
      }
      abort();
    }
  }
}

int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
  unsigned short oldnCcalls = L->nCcalls;
  struct lua_longjmp lj;
  lj.status = LUA_OK;
  lj.previous = L->errorJmp;  /* chain new error handler */
  L->errorJmp = &lj;
  LUAI_TRY(L, &lj,
    (*f)(L, ud);
  );
  L->errorJmp = lj.previous;  /* restore old error handler */
  L->nCcalls = oldnCcalls;
  return lj.status;
}

在以上代码可以看出,LUAI_TRY方法应用在luaD_rawrunprotected函数内,在整个lua源码中也仅应用于此处。
而调用luaD_rawrunprotected的地方有:

  • pcall
  • 协程resume
  • table调整size
  • 创建luastate初始化库
  • 检查栈大小是否足够

2.2 异常处理流程

因为调用luaD_rawrunprotected方法的地方有多个,所以存在嵌套的调用的情况,就需要使用L->errorJmp的链表可以存储每次调用的异常处理回调。

//宏定义
#define LUAI_TRY(L,c,a)     if (setjmp((c)->b) == 0) { a }
//实际调用
 LUAI_TRY(L, &lj,  (*f)(L, ud););

当setjmp函数第1次被调用时,它返回0,所以a代表的语句块(*f)(L, ud)就会被执行。

//宏定义
#define LUAI_THROW(L,c)     longjmp((c)->b, 1)
//实际调用
LUAI_THROW(L, L->errorJmp);  /* jump to it */

当语句块(*f)(L, ud)执行过程出现异常情况会调用LUAI_THROW,即执行longjmp。因为longjmp的效果就是使执行流通过再次从setjmp函数返回,它的返回值是longjmp的第2个参数,即上面宏定义语句中的1。当跳转返回值不等于0 ,就不会再次执行a代表的语句块,然后继续往下执行就是还原异常处理回调链。

3 参考文献

C和指针

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 异常处理 异常处理机制允许程序中独立开发的部分能够在运行时才出现的问题进行通信并做出相应的处理。异常使得我们能够将...
    漫游之光阅读 3,003评论 0 0
  • 一、异常的概念 什么是异常?异常一般指的是程序运行期(Run-Time)发生的非正常情况。异常一般是不可预测的,如...
    MagicDong阅读 13,712评论 0 4
  • 学习目标 了解异步异常与同步异常,以及异常控制流与平时的逻辑控制流的差异 理解进程的工作机制,如何通过异常来进行进...
    西部小笼包阅读 2,600评论 0 1
  • 计算机系统漫游 代码从文本到可执行文件的过程(c语言示例):预处理阶段,处理 #inlcude , #defin...
    willdimagine阅读 9,047评论 0 5
  • 学习目标 1.了解异步异常与同步异常,以及异常控制流与平时的逻辑控制流的差异2.理解进程的工作机制,如何通过异常来...
    KEEEPer阅读 2,707评论 0 2