DLL卸载时Asio TCP服务器崩溃

时间:2018-11-26 14:14:38

标签: c++ boost-asio

我正在使用独立的Asio库来托管简单的TCP服务器。该代码位于动态库中,该动态库由我无法控制的宿主应用程序加载。

该库公开了一个OpenClose函数,主机应用程序应在加载库之后/卸载库之前调用该函数。
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函数。

当主机应用程序根据需要调用OpenClose时,此代码可以正常工作。
但是,有时,主机应用程序在卸载库之前不会调用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的用法来处理此行为吗?

1 个答案:

答案 0 :(得分:1)

您最好的选择是在代码运行时保持库加载,例如在启动线程以增加引用计数时,LoadLibrary,在工作线程死亡时,FreeLibraryAndExitThread

主机应用程序可能会感到不满意,就像被询问时未卸载该库一样。...但这是通过不调用Close()打破规则的程序。

不,关于此,甚至没有远程跨平台的东西。但是动态库的处理是特定于操作系统的。也许您可以找到一个特别用于动态库处理的跨平台包装器。