我正在使用gtest进行单元测试,特别是在调试版本中有一些DEATH_TESTS用于某些断言。要SetUp()
测试,我必须创建一个对象,它创建另一个线程,关闭并做一些工作,返回一些数据,然后加入对象的线程。最后,测试夹具的SetUp()
返回,允许测试体运行。
我注意到有时DEATH_TEST会抱怨Death tests use fork(), which is unsafe particularly in a threaded context. For this test, Google Test detected 2 threads.
,当然,如果实际上有多个线程在运行,那么是一个有效的问题。但是,有时候不存在这样的警告。这似乎是一场竞争。
因此,我发现gtest正在使用/proc/self/task
伪文件系统来发现线程。由于我的所有线程都已命名,因此我决定使用/proc/self/task/[tid]/comm
来发现哪个线程可能会延迟。实际上,它与join()
ed完全相同。所以我想出了一个示例源代码来重现 1)为gtest重现gtest的线程检测的问题,如果目标线程在延迟,则 2),然后发出一个给stdout的消息。
// 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([tid]
中的/proc/self/task/[tid]
)与pthread_t
中预期的pthread pthread_getname_np()
不同。我发现prctl
有PR_GET_NAME
,但似乎只检索当前(调用)线程的名称。所以我的一个问题是:是否有文档化的API来检索线程的名称,如果给定系统TID(例如,所以你不必阅读/proc/self/task/[tid]/comm
)?但这只是一个附带问题。
更重要的是,有没有办法保证这是一个误报,就fork()
问题而言?,以及相关的问题:是否有更好的方法确保std::thread
实际上已完成而不是join()
?
答案 0 :(得分:2)
我认为没有跟踪系统TID&lt; - &gt; pthread ID自己映射你运气不好; pthread ID是一个不透明的值,专门用于将其与特定于平台的进程抽象分离,我不相信有任何公共API可以提取它。
我认为你的procfs&amp; std::thread::join
/ pthread_join
种族可能是不可避免的,至少在目前的Linux实施中是这样。 pthread_join
等待内核清除已注册的内存位置&amp;在线程退出期间发出一个futex信号。这发生在mm_release
( linux / kernel / fork.c )中,在do_exit
中间调用,然后更新所有任务记帐结构。我怀疑在pthread_join
完成后立即遍历procfs很容易与剩余的进程拆解竞争。
对于您尝试解决的问题,我不满意答案,但我希望这会有所帮助。