std::函数析构函数中的Segfault

Segfault in std::function destructor

本文关键字:Segfault 析构函数 函数 std      更新时间:2023-10-16

我目前正在维护一个用C++开发的C++REST服务器。它提供了一些功能,如中间件和路由。

路由存储在路由器类的内部结构中:

//! The http router
//!
//! allow us to parse route on a server using regex to match the good route for a given url
//! and extract the possible url variables
class router {
private:
//! routes datas
//!
//! contains:
//! * the regex to parse the routes
//! * an std::vector with the list of variable inside the routes
//! * 4 std::function, one for each REST methods
struct rest_routes {
std::regex regex;
std::vector<std::string> vars_name;
std::pair<std::function<http::response(http::request&)>, std::vector<std::string>> get;
std::pair<std::function<http::response(http::request&)>, std::vector<std::string>> post;
std::pair<std::function<http::response(http::request&)>, std::vector<std::string>> put;
std::pair<std::function<http::response(http::request&)>, std::vector<std::string>> del;
};
};

在执行过程中一切都很好:可以配置路由并将其添加到路由器中,如果有人在现有路由上请求服务器,则会执行回调,服务器会按预期发送响应。

下面是一个路由配置的例子,我们为HTTP DELETE请求创建一个路由/admin/cameras/:cam_id

// delete a camera
router.del("/admin/cameras/:cam_id",
std::bind(&admin_service::remove_camera, service, std::placeholders::_1));

在本例中,admin_service::remove_camera是成员函数,service是包含指向admin_service对象的指针的shared_ptr。如果有人请求此路由,则会调用admin_service::remove_camera

但是,服务器在执行结束时(当我们退出服务器时)会发生segfault。

我已经追踪到segfault的起源,它来自…的析构函数。。。std::function。更准确地说,它发生在std::pairgetpostputdel中包含的std::function之一被破坏的过程中。

我之所以这么说是因为,当我放入以下调试代码时:

struct rest_routes {
~rest_routes() {
std::cout << "BEGIN DTOR rest_routes" << std::endl;
std::cout << "BEGIN get" << std::endl;
get.first = nullptr;
std::cout << "END get" << std::endl;
std::cout << "BEGIN post" << std::endl;
post.first = nullptr;
std::cout << "END post" << std::endl;
std::cout << "BEGIN put" << std::endl;
put.first = nullptr;
std::cout << "END put" << std::endl;
std::cout << "BEGIN del" << std::endl;
del.first = nullptr;
std::cout << "END del" << std::endl;
std::cout << "END DTOR rest_routes" << std::endl;
}
std::regex regex;
std::vector<std::string> vars_name;
std::pair<std::function<http::response(http::request&)>, std::vector<std::string>> get;
std::pair<std::function<http::response(http::request&)>, std::vector<std::string>> post;
std::pair<std::function<http::response(http::request&)>, std::vector<std::string>> put;
std::pair<std::function<http::response(http::request&)>, std::vector<std::string>> del;
};

我得到了以下输出:

BEGIN DTOR rest_routes
BEGIN get
END get
BEGIN post
END post
BEGIN put
END put
BEGIN del
Segmentation Fault

我不知道std::函数如何在销毁或赋值过程中出错。。。

我最初认为,也许std::function引用了std::shared_ptr service,而不是按值,并多次删除了它包含的原始指针。但是,当我放入一些调试输出时,我可以看到shared_ptr计数器在调用router.del之后递增。

有人知道这个问题吗?

这看起来像是内存损坏问题。我会试试:

  • valgrind下运行。如果可能的话,因为valgrind模拟CPU,所以应用程序在单个虚拟线程下的运行速度要慢50倍。在CCD_ 21下,由竞争条件引起的错误可能不会表现出来
  • 通过重新编译并使用-fsanitize=address命令行选项链接,使用gcc/clang地址清理程序。这个程序的工作原理令人惊讶,因为它的开销相对较低:一个装有Address Sanitizer的程序通常会比未装有地址的程序慢两倍,并且通常会多消耗20%的内存