Javascript多线程引擎(五)之异常处理
C语言没有提供一个像Java一样的异常处理机制, 这就带来了一个问题, 对于一个子函数中发生异常后, 需要在父函数调用子函数的位置进行Check, 如果发生异常则直接reuren. 这种机制, 会带来一个问题 ------- 过多的return 和 判断语句, 使得程序的维护成本提高.
而这个项目准备使用setjmp,longjmp的异常处理机制来实现throw异常后,能直接转到catch位置.
/*一个函数中mark都要不同*/ #define JS_TRY(mark) int done##mark; jmp_buf* jmp_buf##mark = (jmp_buf*)JsMalloc(sizeof(jmp_buf)); for(done##mark = 0; done##mark == 0 && (setjmp(*jmp_buf##mark) == 0 ? (JsBuildRecord(jmp_buf##mark),1) : (JsOmitRecord(),0)); ++done##mark, JsOmitRecord()) /*Catch之后, 异常已经被清除了, 并且e会被赋值 [NULL,Value] */ #define JS_CATCH(e) if((e = JsGetError()))
这里使用两组宏, done##mark .. 确保了可以在一个函数中多次使用JS_TRY,
如下是对其中的函数用途解析.
/* 抛出一个String类型的错误 */ void JsThrowString(char* msg); /* 抛出一个error */ void JsThrow(struct JsValue* e); //保存一个还原点到环境中,p 为jmp_buf*指针 void JsBuildRecord(void* p); //每次加锁的时候, 把对应的锁添加到最近还原点的上下文中 void JsPushLockToRecord(JsLock lock); //解锁的时候, 把给定的锁从最后面扫描, 剔除 void JsPopLockInRecord(JsLock lock); //检查当前环境是否存在异常, 当并不清除错误 int JsCheckError(); //在环境中删除一个最近的还原点 void JsOmitRecord(); //获得当前错误, 并且清除当前错误, 如果没有则返回NULL struct JsValue* JsGetError(); //设置一个错误, NULL表示清除错误 void JsSetError(struct JsValue* v);
基本原理是在, TLS中建立一个异常链, 每次throw的时候, 直接获取最近的还原点(Record). 然后通过GetError()获取到这个异常.
为什么要使用TLS? , 因为setjmp/longjmp 是基于函数栈的, 多个线程之间是不能跳转的.
值得一提的是, setjmp/longjmp 不会释放非托管资源(除内存外的所有资源, 比如说, lock, socket, file等), 所以在使用这些句柄的时候, 需要注意TRY --- CATCH
基本使用用法:
JS_TRY(0){ 可以放置表达式(函数调用, 赋值,...) 如果使用return , break 语句则需要在之前调用JsOmitPoint() } doFinally工作 struct JsValue* e = NULL; JS_CATCH(e){ 1. 处理错误 2. 继续抛出异常 JsThrow(e); }
截至到现在, 该引擎的基本模块已经完成, 进入了代码验证和测试阶段.!
项目地址为:
github.com/darkgem/js-engine