2014-12-05 44 views
3

我使用gtest進行單元測試,特別是在調試版本中對某些斷言有一些DEATH_TESTS。對於SetUp()測試,我必須創建一個對象,它創建另一個線程,關閉並執行一些工作,返回一些數據,然後加入對象的線程。最後返回測試夾具的SetUp(),允許測試體運行。gtest DEATH_TEST抱怨fork()和線程,但只發現線程已加入

我注意到,有時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()版。因此,我想出了一個示例源代碼來重現gtest的gtest線程檢測問題,如果目標線程滯留,然後向stdout發送消息,則會返回問題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改變。

在這方面的工作,我發現,隨着預期pthread_getname_np()系統的線程ID([tid]/proc/self/task/[tid])是並行線程的pthread_t不同。我發現prctlPR_GET_NAME但似乎只檢索當前(調用)線程的名稱。所以我的一個問題是:是否有一個記錄的API來檢索一個線程的名稱,如果給定一個系統TID(例如,所以你不必讀/proc/self/task/[tid]/comm)?但這只是一個側面的問題。

更重要的是,有沒有辦法保證這是一個誤報,只要fork()問題有關?,以及相關的問題:有沒有更好的方法來確保std::thread實際上已經完成比join()

+0

您看過這篇文章:https://code.google.com/p/googletest/wiki/AdvancedGuide#Death_Tests_And_Threads?他們提到「衆所周知的線程存在的問題 - 但我不知道這個問題...... – PiotrNycz 2015-01-02 08:03:56

+0

是的,我讀過它,這就是我的問題試圖討論 - 試圖確保只有存在一個單線程(主線程)。我使用的所有靜態庫都不會創建自己的線程。此外,我提供了一個沒有第三方靜態庫的MCVE。 – inetknght 2015-01-02 15:13:47

回答

2
  1. 我認爲,W/O跟蹤系統TID < - 自己>並行線程ID映射你的運氣了; pthread ID是一個不透明的值,專門用於將其從平臺特定的流程抽象中分離出來,我不相信有任何公共API可以提取它。

  2. 我認爲你的procfs & std::thread::join/pthread_join比賽很可能是不可避免的,至少在目前的Linux實現。 pthread_join等待內核清除註冊內存位置&在線程退出期間發出futex信號。在mm_releaselinux/kernel/fork.c)中發生這種情況,在更新所有任務會計結構之前,在do_exit的正中間調用它。我懷疑在pthread_join完成後立即遍歷procfs可以輕鬆地與其餘的進程拆卸進行比賽。

對於您試圖解決的問題而言不滿意的答案,但我希望這有助於解決問題。

+0

在某些方面令人不滿,但它確實指向了內核讀取方向。至少這是有幫助的。看起來像'do_exit'函數試圖設置'tsk-> state = TASK_DEAD'。我想知道*未來的Linux內核版本*是否可以在執行任何聯合信號釋放執行之前將任務狀態設置爲指示任務正在退出的某個狀態。那麼可以檢查和調查?在google上搜索「linux get task state」並不會顯示一個API來檢索一個任務的狀態,如果給定一個pid或者tid的話。我需要更多的研究。 – inetknght 2015-01-02 17:38:00