Gtest DEATH_TEST complains about fork () and threads, but only found threads were connected

I use gtest for unit testing and in particular have some DEATH_TESTS for some of the assertions in debug builds. For a SetUp()

test, I have to create an object that creates another thread, disconnects and does some work, returns some data, and then attaches to the object's thread. Finally, the test fixture is returned SetUp()

, allowing the test to be tested.

I noticed that sometimes DEATH_TEST will complain about Death tests use fork(), which is unsafe particularly in a threaded context. For this test, Google Test detected 2 threads.

, which is of course a valid problem if there are actually multiple threads running. However, sometimes no such warning exists. It looks like a race condition.

So while researching this, I found that gtest uses a /proc/self/task

pseudo filesystem to detect threads. Since all my threads are named, I decided to use /proc/self/task/[tid]/comm

to find out which thread might be delaying. Indeed, this is the same stream that was join()

ed. So I came up with a source code example to reproduce an issue that 1) reproduces the detection of a gtest stream for gtest, and 2) if the target stream is delayed, then emits a message to 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;
}

      

Using Ubuntu 14.04 and GCC 4.8.2-19ubuntu1 and the command commented out on the first line of the example source, I get an output to stdout indicating that a race condition seems to exist. Most of the output states are "Not Found", while sometimes the output is interspersed with the TID of a stream named TARGET. I can turn off the Not Found output and notice that the changed TID is changing.

While working on this, I found that the id of the system thread ( [tid]

in /proc/self/task/[tid]

) is different from pthread pthread_t

as expected in pthread_getname_np()

. I found that there is prctl

with PR_GET_NAME

, but it only seems to return the name of the current (calling) thread. So one of my questions is, is there a documented API to get the stream name if the system TID is given (for example, you don't need to read /proc/self/task/[tid]/comm

)?
But that's just a side issue.

More importantly, is there a way to ensure this is a false positive issue with fork()

?
, and a related question: is there a better way to ensure that a std::thread

really ended up with whatjoin()

?

+3


source to share


1 answer


  • I think without a TID tracking system ↔ pthread ID displays itself, which is out of luck for you; the pthread id is an opaque value that deliberately separates it from the platform process abstractions and I don't believe there are any public APIs to retrieve it.

  • I think your procfs and race std::thread::join

    / pthread_join

    are probably inevitable, at least in current Linux implementations. pthread_join

    waits for the kernel to clear the registered memory location and report futex when the thread exits. This happens in mm_release

    (linux / kernel / fork.c) which is called right in the middle do_exit

    before all the accounting structures are updated. I suspect that passing procfs right after completion pthread_join

    can easily chase the rest of the process.



Unsatisfactory answer in terms of the problem you are trying to solve, but I hope this helps.

+2


source







All Articles