Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

下一个版本解决问题。 #29

Closed
wlgq2 opened this issue Sep 10, 2020 · 21 comments
Closed

下一个版本解决问题。 #29

wlgq2 opened this issue Sep 10, 2020 · 21 comments

Comments

@wlgq2
Copy link
Owner

wlgq2 commented Sep 10, 2020

1.Sever及EventLoop退出机制。
2.UDP组播。

@x724172556
Copy link

我尝试过,直接用runinthisloop,去执行async_->close,和uv_stop(handle),但是产生一个问题,eventloop中的async_的uv handle_是只能在之后的事件循环中被回调delete,像这样
::uv_close((uv_handle_t*)handle_, [](uv_handle_t* handle)
{
delete (uv_async_t*)handle;
});
但是loop在当前循环中已经被停止了,所以这个回调不能被调用,导致最终的内存泄漏,我觉得async 中的handle_的销毁应该交给async的析构,而不应当在事件回调中

@x724172556
Copy link

类似signal也是一样,只能主动调用close,并且当前的eventloop是正常执行的。

@wlgq2
Copy link
Owner Author

wlgq2 commented Sep 11, 2020

@x724172556
在析构里面释放也会有问题。句柄必须在回调中释放。
所以这是我下一个版本要考虑的问题。
因为我自己使用Loop没有释放的需求,正常的程序里面也不会频繁创建Loop(想想只有动态扩展线程池才有这个需求)。
后面看看怎么弄比较安全,会解决这个问题。

@x724172556
Copy link

我想到了一个方法,但没有尝试过,就是eventloop添加一个接口,close(callback),我们在callback回调中执行关闭其他句柄的操作,例如tcpserver,此时uv_loop是已经停止了,下一次是无法进入循环的,真正的关闭句柄操作是无法执行的,那么我们再启动一次uv_loop,这次试用uv_run(onces),这样他就只执行一次循环就退出了,这个方法是否可行

@x724172556
Copy link

或者是在,callback执行以后,请求一个异步的关闭操作,在下个循环执行uv_stop

@x724172556
Copy link

void EventLoop2::stop(StopCallback callback)
{

runInThisLoop([this,callback](){
    callback();
    async_->close(nullptr);

     uv_stop(handle());
});

}实际发现这样写,就能结束循环,并且能将 async_的uv句柄释放掉,只需要保证在uv_stop之前关闭掉其他句柄就可以了

@wlgq2
Copy link
Owner Author

wlgq2 commented Sep 21, 2020

计划10.1更。

@wlgq2
Copy link
Owner Author

wlgq2 commented Oct 6, 2020

v1.5.1 版本,增加eventloop退出机制,windows下测试无内存泄漏。例程:https://github.com/wlgq2/uv-cpp/tree/master/examples/loop_exit

@wlgq2
Copy link
Owner Author

wlgq2 commented Oct 8, 2020

增加server退出机制。

@x724172556
Copy link

最近看了很多EventLoop库,包括muduo,其实打多数库存在的问题是回调陷阱,就是回调发生期间,所依赖的对象已经被销毁的情况,导致段错误等问题,muduo这个问题会更加严重,uv-cpp至少能够在close的回调中delete掉对象,,muduo大部分情况下不知道在什么时候去销毁对象,可能在你刚销毁后,就遇到了捆绑此对象的回调,导致段错误。。
我觉得一个好的方法是全面使用智能指针,使用shared_ptr阻止其在回调没有执行前不能销毁,或者使用weak_ptr判断对象如果已经销毁了就不执行回调。
另外就是使用qt或者sigc这种信号和槽的机制,关注对象的生命周期,如果绑定的对象被销毁了,那么他就不会再去执行对应的回调了
这可能是最影响各种库稳定性的问题,还是那个最基本的问题,析构函数并没有清理掉所有相关联的资源,包括其他对象对这个对象的依赖。

@wlgq2
Copy link
Owner Author

wlgq2 commented Oct 19, 2020

@x724172556 因为uv-cpp无法控制libuv不对一个析构对象的调用。接口最初设计成这样是为了向libuv妥协。后面看看有没有更好的办法。

@x724172556
Copy link

将需要回调的对象(tcpclient)继承object,然后object会将自身注册进eventloop,析构的时候注销,回调的话,就可以判断对象的生命周期,存活就回调。。。这是传统c++面对对象的事件框架机制,我不知道你对这种方案是否认同

@wlgq2
Copy link
Owner Author

wlgq2 commented Oct 23, 2020

其实自己在对象外面包一层,析构里面Close,回调删对象,也不用手动调Close,直接删对象就行。参考HttpClient中删TcpClient。
https://github.com/wlgq2/uv-cpp/blob/master/uv/http/HttpClient.cpp
HttpClient::~HttpClient()
{
client_->close([](uv::TcpClient* client)
{
delete client;
});
}

@wlgq2
Copy link
Owner Author

wlgq2 commented Oct 23, 2020

@x724172556 问题是,回调是从libuv中触发,我无法把智能指针保存到libuv里面不被析构。还有一个问题,就算智能指针保证了C++的析构后的对象不被调用。但是不能维护libuv自己指针的生命周期,比如在libuv自己loop未结束时,智能指针删除了某个libuv的句柄。程序core。
如果你觉得可行,可以尝试提交个dome,或者pr试试。

@wlgq2
Copy link
Owner Author

wlgq2 commented Oct 23, 2020

给libuv的相关句柄在做一个 封装,这个封装不对外接口暴露,只是内部对象析构的调用这个句柄close,然后删句柄就行。有时间改改看。

@x724172556
Copy link

对,我的其中一个想法就是,析构函数应该执行一次close,因为我不知道libuv的close设置为null还会不会依赖uv_handle,所以我让他在所有的libuv的回调中先判断对象是否存活

@x724172556
Copy link

httpclient也是一个挺好的方法,把原来的tcpclient隐藏,作为一个内部类

@x724172556
Copy link

唯一的问题可能就是,如果eventloop stop之后,再析构,可能会导致close回调不会调用,导致内存泄漏

@wlgq2
Copy link
Owner Author

wlgq2 commented Oct 23, 2020

你看runInLoop实现,如果当前loop是被停止的,是直接在调用线程中运行回调的。如果是loop没开始,则等待开始后在loop中回调。

@x724172556
Copy link

那应该没有问题

@sjhgithub
Copy link

sjhgithub commented Oct 31, 2020

我的解决思路如下所述,不过实际代码感觉很烂,需要的话可以提交
解决死亡之吻(对象实例销毁后,如果之前提交了很多回调,会回调进入函数代码,由于对象已经销毁,将造成内存访问异常,发生崩溃)
主要步骤:
一是在上述实例(母体)中new一个that类(子体)(that对应this),并在子体中记录母体对象(body->this),在母体回调函数中根据该that对象,判断body(实体)是否已经销毁了,采取相应的动作
二是在母体中加上一个门卫(std::recursive_mutex),母体析构函数和回调函数通过该门卫同步执行代码,避免析构后,多线程同时操作造成崩溃,该门卫可以放入步骤一中的that类合并实现,注意不能用std::mutex(有可能产生递归加锁)
三是母体析构函数中如果一时处理不完,有后续动作,可在that类中加入回调功能,在母体中设置一个回调函数,在该函数中可以做一些善后工作(说明:函数代码不会随对象销毁而销毁,该代码中不能处理已经销毁/释放的数据)

@wlgq2 wlgq2 closed this as completed Dec 18, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants