ToLua Timer机制
从一个Bug说起:
在内部试玩时发现有个任务的玩家跟随Npc逻辑挂了.
telnet连接到出问题的设备上, 开始搞事情
这个跟随的逻辑是一个Timer驱动的. 这个Timer在主角创建时就会启动. 一开始不认为是Timer本身的问题. 怀疑是流程上各个地方有return的情况
动态改掉几个函数都没有执行到. 说明Timer的回调没有调用到. 怀疑到Timer本身来
输出了下Timer的状态, 发现是running = true. 感觉很诧异. 又看了下Timer里的其他字段发现有个handle. 输出来看了下, 发现有个removed = true.
这里就奇怪了, 说明被异常removed了. 于是开始看Timer的实现:
Timer.lua 创建一个Timer的流程如下:
1. Timer.New()
创建元表, 基础字段初始化. running = false...
2. Timer.Start()
1). 从UpdateBeat创建一个Handle
通过调用UpdateBeat:CreateListener, 传入Timer.Update函数和Timer本身得到返回值Handle, 其作用是一个链表的Node.
代码: self.handle = UpdateBeat:CreateListener(self.Update, self)
2). 注册到UpdateBeat里:
即把handler作为链表的node放到UpdateBeat的列表里.
代码: UpdateBeat:AddListener(self.handle)
3). 此时Timer类的初始化过程完成了. 后续就等着Timer.Update被调用了. 可以看到主要的内容其实是在UpdateBeat里.
4). 要了解Handle的管理和Timer.Update的调用方式就要看UpdateBeat代码了
5). UpdateBeat是在event.lua里定义的
可以看到是一个_event的元表
6) 关注下_event的内容:
#1 _event.CreateListener(func, obj)
即上面 1)里调用的函数, 这里的func就是Timer.Update, obj就是Timer自身.
这里判断了self.keepSafe, 即5)里 定义UpdateBeat的第二个参数true. 所以这里的self.keepSafe = true.
可以看到keepSafe的情况下, func和obj被包到了一个xfunctor里, 这个xfunctor看了下, 是一个xpcall的封装. 也就是keepSafe的event实际调用func时会用xpcall的方式执行. 出错的话会返回抛错信息.
然后是其他的几个字段. 包括缓存了封装了Timer.Update的xpcall函数的value字段. 实现双向链表需要的两个指针(_prev, _next), 和我们要查bug需要关注的removed = true . 可以看到在Timer.Start申请Handle之初, 这个removed就是true状态的.
#2_event.AddListener
创建了Handle之后, 通过AddListener接口注册到链表里. 可以看到, 这里判断了self.lock 这个字段的作用是锁self.list. 即避免在操作list的时候, 有其他逻辑来改这个list.
这个lock字段是在执行一轮Update时置为true. 执行完成一轮后改成false. 在lock为true的过程中, 如果有其他AddListener的行为, 都会把Add行为缓存到一个临时列表: self.opList里. 而不是self.list
这个列表缓存了Add操作. 即function() self.list:pushnode(handle) end. 然后等执行一轮Update之后, 把缓存的Add的操作都执行一遍. (RemoveListener同理)
#3 _event.__call
这个函数是在对_event执行调用操作时执行的. 如下图 UpdateBeat()
__call函数首先上锁 lock = true. 然后用迭代器生成器 ilist 遍历 链表list. 这个链表就是我们AddListener操作存放Timer的双向链表. (ilist详细见下方解释)
每次迭代的 i 即为一个handle, f是xpcall包装的Timer.Update函数. 然后调用f函数, 返回xpcall的结果 成功标识: flag, 错误信息 msg. 如果出错, 则用error抛出LuaException. 并且调用_list.remove,从链表移除该Node, 并且解锁: lock = false
(这里有个问题, 如果其中一个Timer出错了, 然后解锁了, 后面有Timer真的塞了Timer进来, 岂不是要炸?? 求解释. )
如果一切正常情况下, 会在最后把cache的Add/RemoveListener的操作执行一遍.
ilist迭代器可以在list.lua中看到, 根据Lua的for语句的性质. 迭代器生成器返回三个值: 迭代函数(list.next), 恒定状态(即_list), 控制变量(没用). 然后以恒定状态和控制变量为参数去调用迭代函数, 得到的返回值再赋值给 for循环的key, value (<var-list>)
所以迭代函数为list.next. 这个函数只需要一个恒定状态(第一次是list, 其实就是头节点, 后面是每个node)即可了, 因为直接可以通过next访问到下一个. 当下一个就是头结点时, 循环结束.
for循环定义:
for <var-list> in <exp-list> do
list.lua:
ToLua Timer机制的更多相关文章
- Session Timer机制分析
Session Timer机制分析 功能介绍 会话初始化协议(SIP)并没有为所建立的会话定义存活机制.尽管用户代理可以通过会话特定的机制判断会话是否超时,但是代理服务器却做不到这点.如此一来,代理服 ...
- Timer&TimerTask原理分析
转载地址,请珍惜作者的劳动成果,转载请注明出处:http://www.open-open.com/lib/view/open1337176725619.html 如果你使用Java语言进行开发,对于定 ...
- .NET System.Timers.Timer的原理和使用(开发定时执行程序)
概述(来自MSDN) Timer 组件是基于服务器的计时器,它使您能够指定在应用程序中引发Elapsed 事件的周期性间隔.然后可以操控此事件以提供定期处理.例如,假设您有一台关键性服务器,必须每周7 ...
- tolua++实现lua层调用c++技术分析
tolua++技术分析 cocos2dx+lua 前言 一直都使用 cocos2dx + lua 进行游戏开发,用 Lua 开发可以专注于游戏逻辑的实现,另外一方面可以实现热更新:而且 lua 是一个 ...
- C++ Timer
Timer机制 这里所说的Timer机制是定时器(Timer),例如在Javascript中就提供定时执行代码的功能.但是在C++标准中暂时没有实现这一功能的函数. Javascript中的Timer ...
- Android 双击 Back 键退出程序
双击退出程序的原理无非就是设置一个退出标识(询问是否退出),如果改变了这个标识(确认退出),则再次点击时立马退出,如果短时间内没有退出,则延时重置这个标识(不退出). ================ ...
- Javascript之旅——终点站:困惑的settimeout
有时候结局不是很美好,但起码这也算是一种结局,这个系列的最后一篇settimeout,这是一个让人困惑的函数,也是我一直在吐槽JS的 原因,我们看不到JS的源代码,setimeout同样也是,从始到终 ...
- Linux系统时间与RTC时间【转】
http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=3637782 Linux的RTC驱动相对还是比较简单的,可以将它作为一个普通的字符 ...
- QT GUI总结
QT提供了设计师工具,可以很方便的使用鼠标拖拽的方式绘制界面.绘制完毕后自动生成一个界面的.h文件(如ui_mainwindow.h),其中含有一个自动生成的Ui_MainWindow类,这个类中 ...
随机推荐
- Win10电脑桌面壁纸自动变成黑色无法更换怎么解决
很多用户在升级到win10之后,发现在使用过程中经常会碰到一些问题,就是电脑桌面壁纸总是会自动变成黑色,而且无法设置桌面背景壁纸,这是怎么回事呢,出现这样的问题可能是因为系统不是正版,或者是电脑设置不 ...
- Deep High-Resolution Representation Learning for Human Pose Estimation
Deep High-Resolution Representation Learning for Human Pose Estimation 2019-08-30 22:05:59 Paper: CV ...
- IDEA2019.2中文字体变粗缺字等问题
idea的中文字体渲染问题 IDEA 2018.2升级到 IDEA 2019.2,中文字体渲染问题修改一下备用字体就可以共需要修改两处:1.Setting -> Editor -> Fon ...
- FactorVAE论文学习-1
Disentangling by Factorising 我们定义和解决了从变量的独立因素生成的数据的解耦表征的无监督学习问题.我们提出了FactorVAE方法,通过鼓励表征的分布因素化且在维度上独立 ...
- 【Shell常用命令二】管道符 通配符
======================================
- .net core在Linux本地化Localization的一次填坑
使用ABP框架开发.net core程序已经有一段时间了,因为之前部署在windows服务器上,使用一直很正常.自从前段时间切换服务器上了Linux的Centos服务器,发现之前中文的语言变成了英文, ...
- SpringMVC的基本概念
1.1关于三层架构和MVC 1.1.1 三层架构 我们的开发架构一般都是基于两种形式,一种是 C/S 架构,也就是客户端/服务器,另一种是 B/S 架构,也就 是浏览器服务器.在 JavaEE 开发中 ...
- Centos安装nodejs,并运行项目
不建议编译安装,对gcc要求比较高 安装nodejs yum install nodejs nodejs升级 npm i -g n --force n stable npm升级 npm install ...
- 【LeetCode】整数反转【不能借助辅助空间,需要处理溢出】
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转. 示例 1: 输入: 123 输出: 321 示例 2: 输入: -123 输出: -321 示例 3: 输入: 120 输出: ...
- Integer的parseInt和valueOf的区别
先来看一下下面这段代码 String s = "1"; System.out.println(Integer.valueOf(s)); System.out.println(Int ...