gpt4 book ai didi

c++ - gtest DEATH_TEST 提示 fork() 和线程,但只有找到的线程已加入

转载 作者:IT王子 更新时间:2023-10-29 00:35:04 25 4
gpt4 key购买 nike

我正在使用 gtest 进行单元测试,特别是在调试版本中对某些断言使用了一些 DEATH_TESTS。为了 SetUp() 测试,我必须创建一个对象,它会创建另一个线程,关闭并执行一些工作,返回一些数据,然后加入对象的线程。最后,测试夹具的 SetUp() 返回,允许测试主体运行。

我注意到有时 DEATH_TEST 会提示 死亡测试使用 fork(),这在线程上下文中尤其不安全。对于此测试,Google Test 检测到 2 个线程。这当然是一个有效问题,如果实际上有多个线程在运行。但是,有时不存在此类警告。这似乎是一种竞争条件。

所以仔细研究,我发现 gtest 使用 /proc/self/task 伪文件系统来发现线程。由于我的所有线程都已命名,因此我决定使用 /proc/self/task/[tid]/comm 来发现哪个线程可能会挥之不去。实际上,它与被 join() 编辑的线程完全相同。所以我想出了一个示例源代码来重现问题,1) 为 gtest 重现 gtest 的线程检测,2) 如果目标线程持续存在,则发出一个消息到标准输出。

// g++ test.cpp --std=c++11 -pthread
#include <iostream>
#include <fstream>
#include <string>
#include <thread>

#include <dirent.h> // DIR*, dirent*, opendir(), closedir(); enumerate pseudo-fs /proc/self/task
#include <string.h> // strcmp();

#include <sys/prctl.h> // prctl(), PR_SET_NAME; sets name of current thread

std::string get_thread_name(std::string tid_str) {
std::fstream f(std::string("/proc/self/task/") + tid_str + std::string("/comm"));
tid_str.clear();
std::getline(f, tid_str);
return tid_str;
}

int main(int argc, char **argv) {
// until SIGTERM (ctrl-c)
while (true) {
std::thread a([](){
prctl(PR_SET_NAME,"TARGET",0,0,0);
});
a.join();
if (DIR *dir = opendir("/proc/self/task")) {
bool found = false;
while (dirent *entry = readdir(dir)) {
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
std::string name = get_thread_name(entry->d_name);
if ( found = (name == "TARGET") ) {
std::cout << "THREAD " << entry->d_name << " -- " << name << std::endl;
}
}
}
closedir(dir);
if ( not found ) {
std::cout << "Not found" << std::endl;
}
} else {
std::cout << "Cannot enumerate" << std::endl;
}
}

return 0;
}

使用 Ubuntu 14.04 和 GCC 4.8.2-19ubuntu1 以及示例源代码第一行中注释的命令,我最终将输出输出到 stdout,表明竞争条件似乎确实存在。大多数输出​​状态为“未找到”,而有时输出中散布着以 TARGET 命名的线程的 TID。我可以禁用“未找到”的输出并观察发出的 TID 发生变化。

在研究这个过程中,我发现系统的线程 ID(/proc/self/task/[tid] 中的[tid])不同于 pthread 的 pthread_t 符合 pthread_getname_np() 中的预期。我发现有 prctlPR_GET_NAME 但它似乎只检索当前(调用)线程的名称。所以我的一个问题是:如果给定系统 TID,是否有一个记录的 API 来检索线程的名称(例如,这样您就不必阅读 /proc/self/task/[tid]/comm)? 但这只是一个附带问题。

更重要的是,有没有办法保证就 fork() 问题而言这是误报?,以及相关问题:有没有比 join() 更好的方法来确保 std::thread 实际上已经完成?

最佳答案

  1. 我认为没有跟踪系统 TID <-> pthread ID 映射你自己你运气不好; pthread ID 是一个不透明的值,专门用于将它与特定于平台的进程抽象分离,我认为没有任何公共(public) API 可以提取它。

  2. 我认为您的 procfs 和 std::thread::join/pthread_join 竞争可能是不可避免的,至少在目前的 Linux 实现中是这样。 pthread_join 等待内核清除已注册的内存位置并在线程退出期间发出 futex 信号。这发生在 mm_release (linux/kernel/fork.c) 中,它在 do_exit 中间被调用,在所有任务统计之前结构已更新。我怀疑在 pthread_join 完成后立即遍历 procfs 很容易与剩余的进程拆卸竞争。

就您要解决的问题而言,这是一个令人不满意的答案,但我希望这对您有所帮助。

关于c++ - gtest DEATH_TEST 提示 fork() 和线程,但只有找到的线程已加入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27321913/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com