1、问题
套按字绑定失败导致的loop退出异常
2、环境
libuv版本:1.42.0及以下
OS版本:win10 x64
3、现象
uv_async_send的回调方法中,使用uv_udp_bind(或者uv_tcp_bind,后文以udp举例),将一个本机不存在的IP,绑定到uv_udp_t(或者uv_tcp_t)代表的handle(new创建),绑定返回失败后,对handle进行释放(delete);在退出loop的过程中,应用发生异常。
备注:由于主机IP的人为修改等业务性操作流程的存在,意外导致应用配置文件未同步变化,此类问题在实际环境中确实存在。
4、分析
1、在绑定本机上的已有IP时,退出过程中,并未发生异常;
2、退出loop前,使用uv_walk迭代关闭libuv内部维护的handle,发现无论是正常还是异常情况的绑定,均存在一个handle(尽管可能还有别的handle,比如复现demo中的uv_async_t,但我们可以聚焦于问题相关的那个),怀疑此handle是对应的udp类型的handle。在绑定失败的情况下,进入uv_walk后,handle的type是个随机的野值;
3、查看libuv源码中的src/win/udp.c文件,在uv_udp_maybe_bind方法中,当bind失败后,直接返回了WSAGetLastError()方法返回的错误值;
4、第3步导致的问题是,在bind操作前,已经使用了uv_udp_init将handle与loop进行了关联,而bind失败后,并没有关闭(closesocket)创建的handle->socket,也没有撤销handle与loop的关联(QUEUE_REMOVE),导致loop中已经存在了一个udp handle,而bind失败后,又接着对handle占用的内存进行了释放;
5、退出loop时,uv_walk依然发现queue里有个handle,然而,我们在上一步中,已经释放了对应地址的handle(甚至此地址已经又包含着另外的对象),从而导致了连锁的异常;
5、复现
我们的复现demo,其主要流程是:应用进程的主线程内,首先创建一个工程线程,在后者内,初始化、运行libuv的loop(即uv_run位于工作线程中);然后,主线程内,通过uv_async_send的异步方式,传递一个请求,使用libuv的loop创建并绑定udp handle;最后,主线程等待用户输入字符q,以退出整个应用。
在demo中,当绑定使用的是ipNormal的值(即本机存在的IP时),无异常;当绑定使用的是ipAbnormal的值(即非本机存在的IP)时,退出发生异常;
6、解决
可能的解决方案有以下:
方案一
1、没有libuv源代码(无法完成源码修改)的情况下,我们只能从应用的实现、释放流程上,避免发生对bind失败的handle,立刻进行内存释放的操作,而应该在loop彻底退出后,再释放handle占用的内存(尽管这样,让人感觉些许的难受,此外,loop全程也维护了一个无法行使职能的handle);

本文详细分析了libuv在win10环境下,因尝试绑定本机不存在IP而导致uv_async_send回调中uv_udp_bind失败的异常情况。问题在于bind失败后未关闭socket和解除handle与loop关联,导致loop退出时异常。提出了两种解决方案,一是应用层面避免立即释放失败的handle,二是修改libuv源码确保资源释放。同时提供了代码复现和解决思路。
最低0.47元/天 解锁文章
293

被折叠的 条评论
为什么被折叠?



