【lua学习】8.协程

  • 1 数据结构和宏
    • 1.1 协程的状态码
    • 1.2 协程的执行状态码
    • 1.3 lua_State 协程结构体
  • 2 C API
    • 2.1 lua_newthread 新建一个协程,压栈,返回这个新协程
    • 2.2 luaE_freethread 释放一个协程L1
    • 2.3 lua_status 获取协程的错误码
    • 2.4 lua_resume 唤醒一个协程,nargs为参数个数,返回值为协程状态码
    • 2.5 lua_yield 挂起一个协程
  • 3 Lua API
    • 3.1 协程模块的注册
      • 3.1.1 luaL_register(L, LUA_COLIBNAME, co_funcs) 注册协程模块
      • 3.1.2 co_funcs 注册的协程库函数列表
    • 3.2 luaB_cocreate 创建一个协程,协程留在栈顶
    • 3.3 luaB_costatus 获取当前协程的状态,将状态字符串压栈
    • 3.4 luaB_coresume 唤醒一个协程,栈顶为[true值,协程yield或return的值] 或者为[false值,,错误信息]
    • 3.5 luaB_corunning 获取当前正在执行的协程,压栈
    • 3.6 luaB_cowrap 创建一个协程和一个C闭包,并以该协程作为该C闭包的upvalue,将该C闭包压栈
    • 3.7 luaB_yield 挂起协程

1 数据结构和宏

1.1 协程的状态码

(lua.h)

#define LUA_YIELD	1 //挂起
#define LUA_ERRRUN	2 //运行时错误
#define LUA_ERRSYNTAX	3 //语法错误
#define LUA_ERRMEM	4 //内存错误
#define LUA_ERRERR	5 //在错误处理函数中出错

1.2 协程的执行状态码

(lbaselib.c)

#define CO_RUN	0 //执行中状态
#define CO_SUS	1 //挂起状态
#define CO_NOR	2 //常规状态(这个协程唤醒了另一个协程)
#define CO_DEAD	3 //死亡状态//协程执行状态码对应的字符串
static const char *const statnames[] ={"running", "suspended", "normal", "dead"};

1.3 lua_State 协程结构体

(lstate.h) lua_State

struct lua_State
{CommonHeader;//#define CommonHeader	GCObject *next; lu_byte tt; lu_byte markedlu_byte status;//协程的状态码StkId top;//栈顶位置,“寄存器”第一个可用位置StkId base;//当前函数调用的栈基址global_State* l_G;//全局状态机CallInfo* ci;//当前函数调用信息const Instruction* savedpc;//指令指针StkId stack_last;//“寄存器”最后一个可用位置StkId stack;//栈数组的起始位置CallInfo* end_ci;//函数调用信息数组的最后一个位置的下一个位置CallInfo* base_ci;//函数调用信息数组首地址int stacksize;//栈的大小int size_ci;//函数调用信息数组大小unsigned short nCcalls;//内嵌C调用的层数unsigned short baseCcalls;//唤醒协程时的内嵌C调用层数lu_byte hookmask;lu_byte allowhook;int basehookcount;int hookcount;lua_Hook hook;TValue l_gt;//Global表TValue env;//环境表的临时位置GCObject* openupval;//栈上open状态的uvaluesGCObject* gclist;struct lua_longjmp* errorJmp;//当前跳转信息,实现try catch的关键结构ptrdiff_t errfunc;//当前错误处理函数相对于“寄存器数组首地址”的偏移地址
};

2 C API

2.1 lua_newthread 新建一个协程,压栈,返回这个新协程

(lapi.c) lua_newthread

LUA_API lua_State* lua_newthread(lua_State* L)
{luaC_checkGC(L);lua_State* L1 = luaE_newthread(L);setthvalue(L, L->top, L1);api_incr_top(L);//#define api_incr_top(L) { L->top++; }return L1;
}

(lstate.c) luaE_newthread 新建一个协程并返回这个协程

lua_State* luaE_newthread(lua_State* L)
{//#define tostate(l) (cast(lua_State *, cast(lu_byte *, l) + LUAI_EXTRASPACE))//#define state_size(x)	(sizeof(x) + LUAI_EXTRASPACE)lua_State* L1 = tostate(luaM_malloc(L, state_size(lua_State)));luaC_link(L, obj2gco(L1), LUA_TTHREAD);//初始化协程preinit_state(L1, G(L));//栈初始化(初始化 CallInfo 数组,“寄存器” 数组,第一个CallInfo)stack_init(L1, L);//所有的协程的Global表都是用的主协程的Global表哦setobj2n(L, gt(L1), gt(L));L1->hookmask = L->hookmask;L1->basehookcount = L->basehookcount;L1->hook = L->hook;resethookcount(L1);//#define resethookcount(L) (L->hookcount = L->basehookcount)return L1;
}

2.2 luaE_freethread 释放一个协程L1

(lstate.c)

void luaE_freethread(lua_State* L, lua_State* L1)
{//关闭协程L1上所有upvalueluaF_close(L1, L1->stack);//释放协程L1上“寄存器”数组和CallInfo数组freestack(L, L1);//释放协程L1本身的内存//#define luaM_freemem(L, b, s)	luaM_realloc_(L, (b), (s), 0)//#define fromstate(l)	(cast(lu_byte *, (l)) - LUAI_EXTRASPACE)//#define state_size(x)	(sizeof(x) + LUAI_EXTRASPACE)luaM_freemem(L, fromstate(L1), state_size(lua_State));
}

2.3 lua_status 获取协程的错误码

(lapi.c) lua_status

LUA_API int lua_status(lua_State* L)
{return L->status;
}

2.4 lua_resume 唤醒一个协程,nargs为参数个数,返回值为协程状态码

(ldo.c) lua_resume

LUA_API int lua_resume(lua_State* L, int nargs)
{if (L->status != LUA_YEILD && (L->status != 0 || L->ci != L->base_ci)){return resume_error(L, "cannot resume non-suspend coroutine");}if (L->nCcalls >= LUAI_MAXCCALLS){return resume_error(L, "C stack overflow");}L->baseCcalls = ++L->nCcalls;int status = luaD_rawrunprotected(L, resume, L->top - nargs);if (status != 0){//记录状态码L->status = cast_byte(status);//根据状态码status将错误入栈luaD_seterrorobj(L, status, L->top);L->ci->top = L->top;}else{status = L->status;}--L->nCcalls;return status;
}

(ldo.c) resume_error 将错误信息msg压栈

static int resume_error(lua_State* L, const char* msg)
{L->top = L->ci->top;setsvalue2s(L, L->top, luaS_new(L, msg));incr_top(L);return LUA_ERRRUN;
}

(ldo.c) resume 唤醒协程

static void resume(lua_State* L, void* ud)
{StkId firstArg = cast(StkId, ud);CallInfo* ci = L->ci;//若状态码为0,则执行函数if (L->status == 0){//若是C函数,直接结束,因为若是C函数那么在luaD_precall里面就执行过了if (luaD_precall(L, fisrtArg - 1, LUA_MULTRET) != PCRLUA){return;}}else{L->status = 0;//若是C函数//#define ci_func(ci) (clvalue((ci)->func))//#define f_isLua(ci) (!ci_func(ci)->c.isC)if (!f_isLua(ci)){//函数收尾工作。若函数实际返回值数量!=函数原型返回值数量,则调整栈顶if (luaD_poscall(L, firstArg)){L->top = L->ci->top;}}//若是lua函数,恢复函数调用栈基址else{L->base = L->ci->base;}}//继续执行指令luaV_execute(L, cast_int(L->ci - L->base_ci));
}

2.5 lua_yield 挂起一个协程

(ldo.c) lua_yield

LUA_API int lua_yield(lua_State* L, int nresults)
{if (L->nCcalls > L->baseCcalls){luaG_runerror(L, "attempt to yield across metamethod/C-call boundary");}//调整函数调用的栈基址L->base = L->top - nresults;//标记协程状态为LUA_YIELDL->status = LUA_YIELD;return -1;
}

3 Lua API

3.1 协程模块的注册

3.1.1 luaL_register(L, LUA_COLIBNAME, co_funcs) 注册协程模块

(lbaselib.c) luaopen_base

LUALIB_API int luaopen_base(lua_State* L)
{base_open(L);//base_open见模块章节//注册协程模块,LUA_COLIBNAME为模块名,co_funcs为库函数列表//#define LUA_COLIBNAME	"coroutine"luaL_register(L, LUA_COLIBNAME, co_funcs);//luaL_register见模块章节return 2;
}

3.1.2 co_funcs 注册的协程库函数列表

(lbaselib.c)

static const luaL_Reg co_funcs[] = {{"create", luaB_cocreate},{"resume", luaB_coresume},{"running", luaB_corunning},{"status", luaB_costatus},{"wrap", luaB_cowrap},{"yield", luaB_yield},{NULL, NULL}
};

3.2 luaB_cocreate 创建一个协程,协程留在栈顶

(lbaselib.c) luaB_cocreate

static int luaB_cocreate(lua_State* L)
{//新建一个协程NLlua_State* NL = lua_newthread(L);//将函数移到栈顶(前提:函数已经就是第一个参数了)lua_pushvalue(L, 1);//将函数从协程L移到协程NLlua_xmove(L, NL, 1);return 1;
}

3.3 luaB_costatus 获取当前协程的状态,将状态字符串压栈

(lbaselib.c) luaB_costatus

static int luaB_costatus(lua_State* L)
{lua_State* co = lua_tothread(L, 1);lua_pushstring(L, statnames[costatus(L, co)]);return 1;
}

(lbaselib.c) costatus 获取协程的状态码

static int costatus(lua_State* L, lua_State* co)
{//若L==co,必定是 执行中状态if (L == co){return CO_RUN;}//根据co的状态码,来决定其运行状态switch (lua_status(co)){//挂起状态case LUA_YIELD:{return CO_SUS;}//若为0,看情况case 0:{lua_Debug ar;//若还有栈帧,则说明是normal状态if (lua_getstack(co, 0, &ar) > 0){return CO_NOR;}//否则,若无参数,则说明协程已死else if (lua_gettop(co) == 0){return CO_DEAD;}//其余情况,则为挂起状态else{return CO_SUS;}}//其余情况则为死亡状态default:{return CO_DEAD;}}
}

(ldebug.c) lua_getstack 获取第level层函数调用的栈的状态

LUA_API int lua_getstack(lua_Stack* L, int level, lua_Debug* ar)
{//寻找 满足 level==0 或者 为base_ci 的 CallInfofor (CallInfo* ci = L->ci; level > 0 && ci > L->base_ci; ci--){level--;//#define ci_func(ci) (clvalue((ci)->func))//#define f_isLua(ci) (!ci_func(ci)->c.isC)if (f_isLua(ci)){level -= ci->tailcalls;}}if (level == 0 && ci > L->base_ci){status = 1;ar->i_ci = cast_int(ci -> L->base_ci);}else if (level < 0){status = 1;ar->i_ci = 0;}//ci == L->base_cielse{status = 0;}return status;
}

3.4 luaB_coresume 唤醒一个协程,栈顶为[true值,协程yield或return的值] 或者为[false值,,错误信息]

(lbaselib.c) luaB_coresume

static int luaB_coresume(lua_State* L)
{//协程为第一个参数,传给协程的参数个数为lua_gettop(L) - 1lua_State* co = lua_tothread(L, 1);//唤醒协程,r若<0则表示出错,r>=0表示 协程yield或return的值的数量int r = auxresume(L, co, lua_gettop(L) - 1);if (r < 0){//错误信息在栈顶,false值插入错误信息下lua_pushboolean(L, 0);lua_insert(L, -2);return 2;}else{//true值插入r个返回值之下lua_pushboolean(L, 1);lua_insert(L, -(r + 1));return r + 1;}
}

(lbaselib.c) auxresume 恢复协程co,参数narg个,返回值为 协程yield或return的值的数量

static int auxresume(lua_State* L, lua_State* co, int narg)
{int status = costatus(L, co);//若栈无 需要参数个数 的空闲空间,且扩容失败,则报错if (!lua_checkstack(co, narg)){luaL_error(L, "too many arguments to resume");}//co不是挂起状态,则报错if (status != CO_SUS){lua_pushfstring(L, "cannot resume %s coroutine", statnames[status]);return -1;}//将协程L栈顶的narg个值出栈,移到协程co的栈顶lua_xmove(L, co, narg);//将协程L的C调用层数 赋值给 协程co的C调用层数lua_setlevel(L, co);//co->nCcalls = L->nCcalls;status = lua_resume(co, narg);if (status == 0 || status == LUA_YIELD){int nres = lua_gettop(co);if (!lua_checkstack(L, nres + 1)){luaL_error(L, "too many results to resume");}//将协程co yeild的返回值压入 协程L的栈顶lua_xmove(co, L, nres);return nres;}else{//将栈顶的错误信息从协程co移到协程Llua_xmove(co, L, 1);return -1;}
}

3.5 luaB_corunning 获取当前正在执行的协程,压栈

(lbaselib.c) luaB_corunning

static int luaB_corunning(lua_State* L)
{//将L放在L的栈顶if (lua_pushthread(L))//若 G(L)->mainthread == L 为true{lua_pushnil(L);//若为主协程,则返回nil。因为lua用的不对称协程,所以主协程不能当作一般的协程看待。}return 1;
}

3.6 luaB_cowrap 创建一个协程和一个C闭包,并以该协程作为该C闭包的upvalue,将该C闭包压栈

(lbaselib.c) luaB_cowrap

static int luaB_cowrap(lua_State* L)
{luaB_cocreate(L);lua_pushcclosure(L, luaB_auxwrap, 1);return 1;
}

(lbaselib.c) luaB_auxwrap 协程的包装函数

static int luaB_auxwrap(lua_State* L)
{lua_State* co = lua_tothread(L, lua_upvalueindex(1));//唤醒协程int r = auxresume(L, co, lua_gettop(L));if (r < 0){//若栈顶是字符串类型,则栈顶的为错误信息if (lua_isstring(L, -1)){luaL_where(L, 1);//获取堆栈信息lua_insert(L, -2);//插入错误信息之前lua_concat(L, 2);//连接错误信息}lua_error(L);//传递错误信息}return r;
}

3.7 luaB_yield 挂起协程

(lbaselib.c) luaB_yield

static int luaB_yield(lua_State* L)
{return lua_yield(L, lua_gettop(L));
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/337351.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

检测到基于堆栈的缓冲区溢出_检测到堆栈粉碎

检测到基于堆栈的缓冲区溢出我敢打赌&#xff0c;每个Java开发人员在他们的职业生涯开始时第一次遇到Java代码的本机方法时都会感到惊讶。 我还可以肯定&#xff0c;多年来随着了解JVM如何通过JNI处理对本机实现的调用而使惊奇消失了。 这篇文章是关于本机方法的最新经验。 更…

探讨 C++ 虚函数 virtual

点击蓝字关注我们来源于网络&#xff0c;侵删有无虚函数的对比C 中的虚函数用于解决动态多态问题&#xff0c;虚函数的作用是允许在派生类中重新定义与积累同名的函数&#xff0c;并且可以通过基类指针或引用来访问基类和派生类中的同名函数。首先写两个简单的类&#xff0c;类…

python上一行的代码打错了怎么办_写Python代码过程中碰到各种错误异常要怎么样去处理?...

错误异常即便Python程序的语法是正确的&#xff0c;在程序运行的过程中&#xff0c;也可能发生错误。运行期检测到的错误被称为异常。如果发生了错误&#xff0c;可以事先约定返回一个错误代码&#xff0c;这样&#xff0c;就可以知道是否有错&#xff0c;以及出错的原因。所以…

双重for_测试双重图案

双重for前段时间&#xff0c;我写了一篇有关使用Test Double的后果的文章&#xff0c;但是与Test Double Patterns无关&#xff0c;仅是一个简单的清单。 今天&#xff0c;我想对其进行更改&#xff0c;并解释这些模式之间的差异。 正如我在提到的文章中写道&#xff1a; Tes…

C++ STL详解(4)

点击蓝字关注我们来源于网络&#xff0c;侵删unordered_set / unordered_multiset这两个容器的方法与上一篇 C STL简介&#xff08;3&#xff09; 提到的 set / multiset 基本一样。但是要注意的是&#xff1a;这两个是无序的&#xff0c;基于哈希表实现的&#xff0c;增删改查…

js压缩图片_Web 性能优化: 图片优化让网站大小减少 62%

图像是web上提供的最基本的内容类型之一。他们说一张图片胜过千言万语。但是如果你不小心的话&#xff0c;图片大小有时高达几十兆。因此&#xff0c;虽然网络图像需要清晰明快&#xff0c;但它们尺寸可以缩小压缩的&#xff0c;使用加载时间保持在可接受的水平。在我的网站上&…

春春幼儿园堆积木大赛_春云边车

春春幼儿园堆积木大赛我有一个部署到基于NetflixOSS的云的应用程序&#xff0c;该应用程序具有以下结构&#xff1a; 本质上是一种将信息持久保存到Cassandra集群的服务。 所有应用程序都已注册到Eureka –因此&#xff0c;在本例中&#xff0c;该服务以及Cassandra节点都已在…

C++ 标准输入的行加速

点击蓝字关注我们来源于网络&#xff0c;侵删一、前言c/c 的标准输入函数&#xff0c;在输入一行数据&#xff0c;当数据量比较大的时候&#xff0c; cin 的效率是非常低的&#xff0c;为了加快读入的效率&#xff0c;可以改用 scanf&#xff0c;当然&#xff0c;还有一种利用 …

如何用python 开发web_如何用python开发web

Django是一个Web框架——一套用于帮助开发交互式网站的工具。Django能够响应网页请求&#xff0c;还能让你更轻松地读写数据库、管理用户等。建立Django项目建立Django项目首先需要确保我们按照好了Django&#xff0c;我们在Pycharm中打开虚拟环境的终端&#xff0c;然后输入&a…

redis安装_Redis安装

redis安装本文是我们学院课程的一部分&#xff0c;标题为Redis NoSQL键值存储 。 这是Redis的速成课程。 您将学习如何安装Redis和启动服务器。 此外&#xff0c;您还会在Redis命令行上乱七八糟。 接下来是更高级的主题&#xff0c;例如复制&#xff0c;分片和集群&#xff0c…

C++ 自动锁

点击蓝字关注我们来源于网络&#xff0c;侵删一、锁1.锁 是 多线程编程 中一个很常用的概念&#xff0c;这里不多加介绍其原理&#xff0c;有兴趣可以参考 临界区 进行更多的了解&#xff1b;2.锁 一般会提供三个接口&#xff1a;加锁&#xff08;Lock&#xff09;、解锁&#…

python快速编程入门黑马程序员pdf_Python快速编程入门 传智播客 黑马程序员 python编程从入门到实践基础视频教程核心编程爬虫数据分析程序设计机器学习简明书籍...

传智播客就业系列从入门到就业JAVA从入门到精通 套装5本&#xffe5;148javaweb从入门到精通套装3本&#xffe5;88Python从入门到实践套装8本&#xffe5;280php教程全套7本&#xffe5;227PHP套装5本&#xffe5;165php全套5本&#xffe5;149android开发从入门到精通 共3本&…

叉叉框架_叉/连接框架

叉叉框架本文是我们名为Java Concurrency Essentials的学院课程的一部分。 在本课程中&#xff0c;您将深入探讨并发的魔力。 将向您介绍并发和并发代码的基础知识&#xff0c;并学习诸如原子性&#xff0c;同步和线程安全性的概念。 在这里查看 &#xff01; 目录 1.简介 2…

C++ 浮点数精度判定

点击蓝字关注我们来源于网络&#xff0c;侵删一、引例看下下面这段代码&#xff0c;会输出什么结果呢&#xff1f;double x 0;for (int i 0; i < 10; i) {x 0.1;}printf("%d\n", x 1);输出如下&#xff1a;0引起这种反差的原因就是浮点误差&#xff0c;浮点数…

字节流和字符流哪个不刷新_不喜欢节流吗?

字节流和字符流哪个不刷新您别无选择–底层系统&#xff08;此处的JVM将为您完成选择&#xff09;。 我仍然记得2013年夏天&#xff0c;当时我正在运行一个项目&#xff0c;整个应用程序中只有1个URL使服务器瘫痪。 问题很简单-机器人决定以很高的速率索引我们的网站&#xff…

C/C++动态内存管理—(new与malloc)

点击蓝字关注我们来源于网络&#xff0c;侵删1.C/C内存分布虚拟地址空间分布&#xff1a;由C/C编译的程序占用的内存分为以下几个部分&#xff1a;栈区&#xff08;stack&#xff09;— 由编译器自动分配释放 &#xff0c;存放为运行函数而分配的局部变量、函数参数、返回数据、…

python实现简单小游戏_python实现简单井字棋小游戏

#Tic-Tac-Toe 井字棋游戏#全局常量X"X"O"O"EMPTY" "#询问是否继续def ask_yes_no(question):responseNone;while response not in("y","n"):responseinput(question).lower()return response#输入位置数字def ask_number(qu…

C++ sort()排序详解

点击蓝字关注我们来源自网络&#xff0c;侵删一.sort()简介1.为什么选择使用sort()我们经常会碰到排序的问题&#xff0c;如果我们不使用一些排序的方法那我们只能手撕排序&#xff0c;这样就会浪费一些时间。而且我们还需要根据需要去选择相关的排序方法&#xff1a;冒泡排序、…

java尾行注释有什么不好_注释不好吗?

java尾行注释有什么不好那天&#xff0c;我在有关Spring XML与注释的文章中运用了自己的原则&#xff0c;轻松进入了这个主题。 对于目前正在编写此新应用程序的团队来说&#xff0c;这种简单的输入方式也是我不使事情复杂化的方式&#xff0c;该应用程序的生产寿命可能为3-5年…

python输出结果为none_python的reverse函数翻转结果为None的问题

今天刷二级题的时候&#xff0c;遇到一个问题>>> L2[1,2,3,4]>>> L3L2.reverse()>>> print( L3)None>>> print(L3)None>>> print(L2.reverse())None其实我想让它输出[4,3,2,1]reverse函数&#xff0c;翻转列表然后我改了一下>…