joe 发表于 2022-6-20 15:47:25

Lua源码分析 - 主流程篇 - 异常处理机制实现(09)

目录

一、异常处理机制 - setjmp和longjmp

二、异常处理机制 - 异常保护方法实现

三、异常处理机制 - 异常情况的处理

一、异常处理机制 - setjmp和longjmp
在讲解Lua的异常处理机制的时候,我们首先要看一下C语言的setjmp和longjmp的实现机制。

setjmp和longjmp分别承担非局部标号和goto作用。整体的逻辑如下:

使用setjmp,保存一个当前环境到jmp_buf跳转点,默认返回的值为0
程序继续执行,到某个地方调用longjmp,传入上面保存的jmp_buf,设置另外一个值,例如1
执行到longjmp函数的时候,会跳转到setjmp,并获取longjmp设置的新值
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>

jmp_buf env;

int my_test(int a, int b) {
    if (b == 0) {
      printf("test 0\n");
      longjmp(env, 1);
    }
    return a / b;
}

int main(int argc, char const *argv[]) {
    int res = setjmp(env);
    if (res == 0) { //执行my_func方法
      printf("return from setjmp\n");
      my_func(10, 0);
    } else {//遇到longjmp,则进入该分支
      printf("return from longjmp: %d\n", res);
    }
    return 0;
}
二、异常处理机制 - 异常保护方法实现
Lua的异常保护是通过一个luaD_rawrunprotected方法实现的。

函数最开始定义一个lua_longjmp结构体,用于保存当前执行环境,状态值设置为LUA_OK
然后调用LUAI_TRY函数,该函数实际是一个宏定义,将当前执行环境setjmp,并执行回调函数
如果回调函数执行内部,发生异常情况,则通过luaD_throw将异常抛出
异常抛出函数,会执行 LUAI_THROW函数,该函数是longjmp的宏定义,并且将返回值设置为1
由于执行了longjmp,则C语言内部方法会回到跳转点setjmp
LUAI_TRY函数判断setjmp的返回值,之前是0,现在由于longjmp设置了值为1,所以不会继续执行回调函数,回调函数被中断
#define LUAI_THROW(L,c)                longjmp((c)->b, 1)
#define LUAI_TRY(L,c,a)                if (setjmp((c)->b) == 0) { a }

/**
* 异常保护方法
* 通过回调Pfunc f,并用setjmp和longjpm方式,实现代码的中断并回到setjmp处
*
*#define LUAI_THROW(L,c)                longjmp((c)->b, 1)
*        #define LUAI_TRY(L,c,a)                if (setjmp((c)->b) == 0) { a }
*/
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;
/* if (setjmp((c)->b) == 0) { a } */
/* 当函数内部调用处理,遇到异常情况下,*/
LUAI_TRY(L, &lj,
    (*f)(L, ud);
);
L->errorJmp = lj.previous;/* restore old error handler */
L->nCcalls = oldnCcalls;
return lj.status;
}

/**
* 抛出异常
* 其中L->errorJmp->status 状态值会设置为异常状态值
* LUAI_THROW longjmp((c)->b, 1)
* 通过longjmp跳转到 setjmp点
*/
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 {/* 没有error句柄,则直接中断处理 thread has no error handler */
    global_State *g = G(L);
    L->status = cast_byte(errcode);/* L->status=LUA_YIELDmark 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();
    }
}
}
三、异常处理机制 - 异常情况的处理
异常保护方法luaD_rawrunprotected主要在luaD_pcall中被调用(协程的yield也是通过该原理实现,下一章节讲解)。
该方法会返回执行的状态码,如果状态码非LUA_OK,则当前函数处理失败,CallInfo需要回滚到上一个ci
L->errorJmp是一个链表结构,通过L->errorJmp->errorJmp串联整个异常处理栈信息。
/**
* 函数调用主方法(异常保护方式)
* func:f_call方法
* u:CallS 调用的方法等信息
* old_top:函数调用前的栈顶 L->top
* ef:错误状态
*/
int luaD_pcall (lua_State *L, Pfunc func, void *u,
                ptrdiff_t old_top, ptrdiff_t ef) {
int status;
CallInfo *old_ci = L->ci; //老的函数回调CallInfo
lu_byte old_allowhooks = L->allowhook; //是否允许钩子
unsigned short old_nny = L->nny;
ptrdiff_t old_errfunc = L->errfunc;
L->errfunc = ef;
status = luaD_rawrunprotected(L, func, u); //异常保护调用主函数

/* 处理失败,栈状态回滚? */
if (status != LUA_OK) {/* an error occurred? */
    StkId oldtop = restorestack(L, old_top);
    luaF_close(L, oldtop);/* close possible pending closures */
    seterrorobj(L, status, oldtop);
    L->ci = old_ci;
    L->allowhook = old_allowhooks;
    L->nny = old_nny;
    luaD_shrinkstack(L);
}
L->errfunc = old_errfunc;
return status;
}





————————————————
版权声明:本文为CSDN博主「老码农zhuli」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/initphp/article/details/104282733

页: [1]
查看完整版本: Lua源码分析 - 主流程篇 - 异常处理机制实现(09)