背景:
阅读文章

call_out()函数小结

[日期:2021-04-07] 来源:  作者:回到未来望过去 [字体: ]

转:(我也不知道哪年从internat上 copy save下来的)

因为reallove的帖子,我算是第一次较为仔细的看了一遍call_out()在driver中的机理,也明确了不少以前似是而非的概念。现整理如下:

 

1. 关于call_out()的driver级宏开关有两个(均在options.h当中):

 

    a. THIS_PLAYER_IN_CALL_OUT

 

    该宏被define时,我们可以在被延时呼叫的函数中肆无忌惮的使用this_player()来找到call_out定义时的this_player(),反之则无法找到准确的this_player()(也就是说,可能找到一个谁,但这个“谁”应该与定义call_out时的“谁”毫无瓜葛,肯定不是我们希望的那一个就对了)

 

    b. CALLOUT_HANDLES

 

    该宏被定义时,call_out()原型变成了 int call_out(function foo,int delay, ..),返回值是该call_out的唯一句柄,我们可以在remove_call_out()时以此为参数清理掉“这一个”而非“这一类”call_outs

 

 

 

2. 关于call_out的作用时机和delay的详细含义:

 

    已知call_out记录是以链表与delay的某个余数分类的形式进行存储的。

 

    call_out会在每个系统心跳(不是每个人的心跳,而是说,系统每次呼叫完所有心跳)之后进入对call_out们的处理(仔细的说,driver中call_out.c中的call_out()函数就是干这个事情的)。

 

    因此,就我现在阅读driver代码得到的结论,可以确定的说,call_out一定在心跳点上发作,但会有累积发作的模式。

 

   

 

   在解释delay之前,有几个“时间”需要说明:

 

   a. heart_beat()的发作时间:driver通过一个alarm/singal事件来设置hb的标志位,并在backend()的大循环当中不停检查该标志位的状态以决定是否应该触发hb,因此通常可以认为hb的发作较为准确。虽然从代码中看,在不同系统下的编译中,driver使用了alarm和ualarm两种不同方式进行按秒或按微秒级别的触发事件,但不那么精确的描述下,还是可以认为心跳的准时程度较高,更接近与真实系统时间。

 

   b. call_out被插入和判断是否被执行(注意不是是否进入call_out执行过程,而是进入后的“判断”),依据的是另外一个时间标记current_time。该标记在系统启动的时候被设置为当前时间的秒数(unix_time,也就是我们常看到的1239xxxxxxxx这种),其后只在系统心跳时被重置为“当前”。因此这个时间标并不总是等于真实的时间。特别是在hb大于1秒的情况下,如果以真实的秒为ticker来审视它,可能会是:0,0,0,[1,2,3],3,3,3,[4,5,6]这样的情况,即很多秒不变化,然后在同一个时间点上依次++直到追上真实时间。

 

 

 

    于是,关于delay有如下看法:

 

    a.在插入一条新的call_out时,delay看起来是从“现在”加上delay秒数后执行被呼叫函数,但这个“现在”是current_time指定的现在,hb>1sec时,他通常指的是过去。。。,因此当我们call_out(foo,3)的时候,他不一定指的是现在+3秒,而是指time()+3秒,他也不一定真的在三秒后执行。不考虑毫秒位的话,通常只有hb为1秒的时候,上面的描述才较为精确。但另外一个方面,delay的含义更倾向于秒,而不是hb。。。(我觉得我已经无法用人类的语言解释清楚这个破玩意儿了。。。。)

 

    b.如前述,call_out被执行一定是在hb的执行点上,具体的执行方式是:在某个HB执行点上,系统扫描小于current_time的call_out记录,然后依次执行(链表的原因,对于应该在“同一个时间点被执行的callout”,先入先执行)。然后系统对current_time++,继续上一步操作,知道current_time追赶上“现在”。

 

 

 

    所以说,如果你期望call_out表现比较正常,那最好把hb设为1秒(1000000微秒);否则hb越大,callout的表现就越奇怪。hb非整秒数则会带来其他更微妙的改变。。。。

 

 

 

 

 

    最后,关于call_out(foo,0)这个特例。

 

    一般来说,我们无法期待这个东西立刻执行。他可能会在下一次hb的时候才被执行。

 

    之所以用“可能”,是因为这个特例还有个更奇怪的特例。该call_out本身处于另外一个call_out的呼叫函数当中。因此这个call_out进行的记录插入动作本身就是在call_out执行点上。可想而知,他立刻就被执行了!!!

 

 

 

    我们举两个例子来说明吧:

 

    我们假设hb是10个seconds。查看这样一段函数:

 

void test()

 

{

 

        call_out("test_callout",0);

 

}

 

 

 

void test_callout()

 

{

 

        tell_object(this_object(),sprintf("CO:%d\n",time()));

 

        call_out("test_callout",0);

 

}

 

 

 

当我们以某种形式执行了test()的时候,会出现什么状况呢?

 

首先是test()中的:call_out("test_callout",0)被执行,插入了一条记录,这个记录应当被触发的时间点是current_time+0。(我们假设current_time是333秒吧)

 

但考虑到hb点还没到。这个时候我们什么都看不到。

 

等到下一个hb点,也许作为现实时间,是337秒的时候吧(举例)。

 

上次插入的callout被执行了!

 

因此我们收到一条CO:333的显示信息(因为current_time这个时候还没++完呢)

 

接下来就坏掉了,因为又一个call_out("test_callout",0)被执行了!

 

要注意,这一条call_out插入的触发时间点依然是current_time+0,也就是333。

 

这时这个test_callout函数被执行完,退出,回到系统的call_out执行循环当中。

 

坏掉了,系统发现在333这个时间点上还有个未处理的记录(刚插进去的。。)

 

于是进入了死循环。。。

 

我们会看到系统不断刷屏CO:333

 

CO:333

 

CO:333

 

CO:333

 

。。。。

 

死循环下去了。

 

 

 

上面这个例子表明,callout 0是否立刻被执行,要看他所处的环境,不能一概而论,即时hb是1也如此。

 

 

 

再来一个例子:

 

同样hb是10seconds。

 

 

 

void test()

 

{

 

        call_out("test_callout",0);

 

}

 

 

 

void test_callout(){

 

        tell_object(this_object(),sprintf("CO:%d\n",time()));        call_out("test_callout",1);

 

}差别是后一个call_out的delay是1。

 

 

 

不详细描述了,我们得到的结果应该是类似:

 

系统等待发呆。。。。一次性刷屏:

 

CO:333

 

CO:334

 

CO:335

 

CO:336

 

CO:337

 

继续等待发呆个10秒。。。一次性刷屏:

 

CO:338

 

CO:339

 

CO:340

 

CO:341

 

CO:342

 

CO:343

 

CO:344

 

CO:345

 

CO:346

 

CO:347

 

等待。。。刷屏。。。等待。。。刷屏。。。。

————————————————

版权声明:本文为CSDN博主「回到未来望过去」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/viviliving/article/details/100553285

收藏 推荐 打印 | 录入:sbso | 阅读:
相关内容      
本文评论   [发表评论]   全部评论 (0)
内容推送
52mud提供
一起回忆泥巴游戏QQ群68186072
52mud官方微信公众平台
热门评论