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类,这个类中 ...
随机推荐
- Hotspot研究-工程结构
- Redis代码示例
RedisTemplate 如果想要在java中使用Redis相关的数据结构,要先注入RedisTemplate. @Autowired private RedisTemplate<K,V> ...
- python opencv PyQt5
import cv2 import numpy as np import sys from PyQt5.QtGui import * from PyQt5.QtCore import * from P ...
- php常用命令
--------------------------------------------------------------- 重启phpservice php-fpm restart ------- ...
- C# 实现HTTP的POST(完整可运行并且通过测试的代码)
https://blog.csdn.net/qq_21381465/article/details/80016159 我是通过VS2010 ,新建一个winform窗体项目,然后写了一个测试软件,软件 ...
- Spring cloud微服务安全实战-7-7自定义metrics监控指标(2)
Gauge用来显示单词一个数的 勾选,这里编程仪表盘 设置仪表盘的最大值.最小值 保存 直接保存 保存成功的提示 返回 这就是我们做的一个简单的仪表盘 这个不适合我们的counter,因为没有最大值 ...
- matlab学习笔记7-定时器
一起来学matlab-matlab学习笔记7-定时器 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考书籍 <matlab 程序设计与综合应用>张德丰等著 感谢张老师的书籍,让 ...
- 【翻译】Flink Table Api & SQL —— 数据类型
本文翻译自官网:https://ci.apache.org/projects/flink/flink-docs-release-1.9/dev/table/types.html Flink Table ...
- tp3.2升级tp5需要注意的变化
I方法用input代替:D和M方法没了,需要的话自己封装.C方法没了,需要自己封装,配置文件说明官方文档写的不对,无法使用 Session的使用发生很大变化 模板渲染,控制器传空值会报错 模板内置标签 ...
- [LeetCode] 460. LFU Cache 最近最不常用页面置换缓存器
Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the f ...