awtk icon indicating copy to clipboard operation
awtk copied to clipboard

emitter越界问题

Open zxc3489 opened this issue 4 years ago • 8 comments

我最近遇到一个问题,注册一个emitter事件后,在hadler里调用emitter_off解除该注册,并且在hadler中向同一个emitter分发其它事件;有检测出越界问题。故作出相关修改,详见 https://gitee.com/zengqianyin/awtk.git 25c332c6efb0364f12c4bd2cc932cba5bde65bff;看是否有必要合并至主分支中

zxc3489 avatar Dec 28 '21 13:12 zxc3489

你的意思是,在一个触发事件中删除当前触发的事件,会导致越界的问题吗?我看了你修改的代码和 emitter_dispatch 和 emitter_remove 的代码,还是没有很明白的你意思,或者你可以给我们一个测试代码,我们这边测试一下啊?

WNsACE avatar Dec 29 '21 02:12 WNsACE

越界的情况比较复杂,不好编写demo,给你一段test代码,你应该能明白我代码中恢复上下文的目的了。 `#include "tkc/emitter.h"

#define EVT_TEST_1 0x2000 #define EVT_TEST_2 0x2001

emitter_t test_emitter; uint32_t event1 = 0; uint32_t event2 = 0; static ret_t __global_user_emitter_event_handler1(void *ctx, event_t *event) { printf("%p,%d\n",test_emitter.curr_iter,test_emitter.remove_curr_iter); emitter_off(&test_emitter,event1); printf("%p,%d\n",test_emitter.curr_iter,test_emitter.remove_curr_iter); emitter_dispatch_simple_event(&test_emitter, EVT_TEST_2); printf("%p,%d\n",test_emitter.curr_iter,test_emitter.remove_curr_iter); return RET_OK; } static ret_t __global_user_emitter_event_handler2(void *ctx, event_t *event) { return RET_OK; }

void test() { emitter_init(&test_emitter); event1 = emitter_on(&test_emitter, EVT_TEST_1, __global_user_emitter_event_handler1, NULL); event2 = emitter_on(&test_emitter, EVT_TEST_2, __global_user_emitter_event_handler2, NULL); emitter_dispatch_simple_event(&test_emitter, EVT_TEST_1); emitter_off(&test_emitter, event2); emitter_deinit(&test_emitter); }`

zxc3489 avatar Dec 29 '21 03:12 zxc3489

调换下顺序就有越界了 `#include "tkc/emitter.h"

#define EVT_TEST_1 0x2000 #define EVT_TEST_2 0x2001

emitter_t test_emitter; uint32_t event1 = 0; uint32_t event2 = 0; static ret_t __global_user_emitter_event_handler1(void *ctx, event_t *event) { printf("%p,%d\n",test_emitter.curr_iter,test_emitter.remove_curr_iter); emitter_dispatch_simple_event(&test_emitter, EVT_TEST_2); printf("%p,%d\n",test_emitter.curr_iter,test_emitter.remove_curr_iter); emitter_off(&test_emitter,event1); printf("%p,%d\n",test_emitter.curr_iter,test_emitter.remove_curr_iter); return RET_OK; } static ret_t __global_user_emitter_event_handler2(void *ctx, event_t *event) { return RET_OK; }

void test() { emitter_init(&test_emitter); event1 = emitter_on(&test_emitter, EVT_TEST_1, __global_user_emitter_event_handler1, NULL); event2 = emitter_on(&test_emitter, EVT_TEST_2, __global_user_emitter_event_handler2, NULL); emitter_dispatch_simple_event(&test_emitter, EVT_TEST_1); emitter_off(&test_emitter, event2); emitter_deinit(&test_emitter); }`

zxc3489 avatar Dec 29 '21 05:12 zxc3489

意思是上面的例子不会有问题,下面的会出现越界?我明天试一下

WNsACE avatar Dec 29 '21 11:12 WNsACE

是的,下面的代码会越界

zxc3489 avatar Dec 29 '21 11:12 zxc3489

上下两份代码,应该只有 __global_user_emitter_event_handler1 函数中的逻辑不一样吧?我测试了一下,反而是第一个份代码是有问题(但这个问题不是越界,而是删除 EVT_TEST_2 的节点),而第二份代码好像没有问题吧?

第二份代码中的 emitter_dispatch_simple_event(&test_emitter, EVT_TEST_2); 虽然把 test_emitter.curr_iter 和 test_emitter.remove_curr_iter 变量都改为0,但是在下面的 emitter_off(&test_emitter,event1); 删除的时候,会直接删除的,也没有出现重复删除的情况,所以我没有看到第二份代码有问题。

但是我大概明白你加这两个临时变量的意图了。

WNsACE avatar Dec 30 '21 01:12 WNsACE

你用valgrind跑一下就知道了。本来emitter_off的时候不应该删除的,而是应该在本次emitter_dispatch的单次循环结束后删除的。emitter_off删除之后,emitter_dispatch里的iter就是野指针了,iter->next就是一个未知值了

zxc3489 avatar Dec 30 '21 02:12 zxc3489

恩恩,是的,谢谢提出 bug,你提供的修复补丁应该也没有修复第一份代码的问题,我们这边研究一下怎样修改一下。

WNsACE avatar Dec 30 '21 02:12 WNsACE