我正在使用独立的Asio库来托管简单的TCP服务器。该代码位于动态库中,该动态库由我无法控制的宿主应用程序加载。
该库公开了一个Open
和Close
函数,主机应用程序应在加载库之后/卸载库之前调用该函数。
在Open
函数中,我在单独的线程中运行asio::io_service
:
// start accepting connections
server.accept();
// start the asio service in a new thread
serviceThread = std::make_unique<std::thread>([this]() {
ioService.run();
});
在Close
函数中,我停止服务并加入线程:
// stop the asio service
ioService.stop();
serviceThread->join();
服务器的accept
功能实现如下:
void Server::accept() {
acceptor.async_accept(socket, [this](std::error_code err) {
if (!err) {
std::lock_guard<std::mutex> lock(sessionsMutex);
sessions.emplace_back(new Session(*this, ioService, std::move(socket)));
}
accept();
});
}
要由asio::io_service
调用的其他函数也以类似的方式实现,即将lambda函数绑定this
传递给asio async
函数。
当主机应用程序根据需要调用Open
和Close
时,此代码可以正常工作。
但是,有时,主机应用程序在卸载库之前不会调用Close
函数,这会导致由错误的内存读取导致的分段错误:
Thread 14 Crashed:
0 com. 0x0000000121f1b14b asio::detail::scheduler::post_immediate_completion(asio::detail::scheduler_operation*, bool) + 27 (scheduler.ipp:281)
1 com. 0x0000000121f1f17c void asio::io_context::executor_type::post<asio::detail::work_dispatcher<myproject::Server::Session::write()::$_3>, std::__1::allocator<void> >(asio::detail::work_dispatcher<myproject::Server::Session::write()::$_3>&&, std::__1::allocator<void> const&) const + 124 (io_context.hpp:270)
2 com. 0x0000000121f1f027 asio::async_result<std::__1::decay<myproject::Server::Session::write()::$_3>::type, void ()>::return_type asio::post<asio::io_context::executor_type, myproject::Server::Session::write()::$_3>(asio::io_context::executor_type const&, myproject::Server::Session::write()::$_3&&, std::__1::enable_if<is_executor<asio::io_context::executor_type>::value, void>::type*) + 87 (post.hpp:58)
3 com. 0x0000000121f11cb2 asio::async_result<std::__1::decay<myproject::Server::Session::write()::$_3>::type, void ()>::return_type asio::post<asio::io_context, myproject::Server::Session::write()::$_3>(asio::io_context&, myproject::Server::Session::write()::$_3&&, std::__1::enable_if<is_convertible<asio::io_context&, asio::execution_context&>::value, void>::type*) + 50 (post.hpp:69)
[...]
我有什么选择?是否有跨平台的方法来检测库的卸载(必须在macOS和Windows上工作),这使我无论如何都可以执行清理工作?
我可以修改Asio的用法来处理此行为吗?
答案 0 :(得分:1)
您最好的选择是在代码运行时保持库加载,例如在启动线程以增加引用计数时,LoadLibrary
,在工作线程死亡时,FreeLibraryAndExitThread
。
主机应用程序可能会感到不满意,就像被询问时未卸载该库一样。...但这是通过不调用Close()
打破规则的程序。
不,关于此,甚至没有远程跨平台的东西。但是动态库的处理是特定于操作系统的。也许您可以找到一个特别用于动态库处理的跨平台包装器。