c – gtest DEATH_TEST抱怨fork()和线程,但只发现了已找到的线程

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

我注意到有时DEATH_TEST会抱怨死亡测试使用fork(),这在特定的线程上下文中是不安全的.对于此测试,Google Test检测到2个主题.当然,如果实际上有多个线程在运行,那么这是一个有效的问题.但是,有时候不存在这样的警告.这似乎是一场竞争.

所以看一下,我发现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(/ proc / self / task / [tid]中的[tid])与pthread的pthread_t不同,正如pthread_getname_np()中预期的那样.我发现PR_GET_NAME有prctl,但似乎只检索当前(调用)线程的名称.所以我的一个问题是:如果给定系统TID,是否有文档化的API来检索线程的名称(例如,所以你不必读取/ proc / self / task / [tid] / comm)?但这只是一个侧面问题.

更重要的是,就fork()问题而言,有没有办法保证这是一个误报?和相关的问题:是否有更好的方法来确保std :: thread实际上已完成而不是加入( )?

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

>我认为你的procfs&至少在目前的Linux实现中,std :: thread :: join / pthread_join race可能是不可避免的. pthread_join等待内核清除已注册的内存位置&在线程退出期间发出一个futex信号.这发生在mm_release(linux / kernel / fork.c)中,在更新所有任务记帐结构之前,它在do_exit中间调用.我怀疑在pthread_join完成后立即遍历procfs可以很容易地与进程拆除的其余部分竞争.

对于您试图解决的问题,我不满意的答案,但我希望这会有所帮助.

点赞