Seeing the fixes of ROOT release 5.34.20 concerning thread safety, I have tried again my threaded-code in vain. I still get segmentation violations. I am using gcc-4.9.1 in C++11 mode to compile everything.
I am using one C++ functor object per thread whose “operator()” is supposed to open the very same file – in read mode only – to retrieve several TH2F’s. I have tried pretty much everything, from having the TFile as a member, to reading the file in a separate function to feed every functor object with a vector of TH2F’s. Whenever I use the TH2F with the ProjectionX method, and GetRandom on the projected histogram, I get various segmentation violations. I have tried using mutex locks virtually everywhere without any results.
Lauching threads with one “myStruct” object per thread (to use the operator() of “myStruct” )
void launchThreads(vector<myStruct> toDo){
start a thread per element in "toDo" and join them
}
void main(){
vector<TH2F> map;
TFile myFile("myfile.root");
fill "map" with all the TH2F's in "myFile"
vector<myStruct> toDo;
fill "toDo" with contents of "map"
launchThreads(toDo);
}
Please note that this code runs if I use only thread at a time (instead of launching many functors in many threads) .I have also tried the same code with ROOT 6.02 (thinking Cling would be enough to ensure thread safety of everything).
the std::vector::front() method should be thread safe (even if I am having troubles understanding the code you submitted) cplusplus.com/reference/vector/vector/front/
what is the stack trace of the segmentation fault?
Do you resize the vector somewhere?
Thank you very much for your answer. I do not resize the vector and the segmentation fault (which really varies from one execution to the other, I will try to quote a few) is really bound to the use of a TH2 and ProjectionX or GetRandom. If I replace the following lines by anything that doesn’t use a the TH2 vector, the code runs fine.
Sure, I have written a very basic code that reproduces the problem (it’s stupid and doesn’t output anything useful, but the problem is the very same as that in my real code).
#include <thread>
#include <vector>
#include <TFile.h>
#include <TH1D.h>
#include <TH2F.h>
#include <TRandom3.h>
using namespace std;
struct myStruct{
const unsigned nEvents;
const vector<TH2F> map;
myStruct(const unsigned nEvents, const vector<TH2F>& map):nEvents(nEvents),map(map) {}
void operator()(){
for(unsigned i = 0; i<nEvents; ++i){
TH1D proj = *(map.front().ProjectionX("proj",1, 1));
proj.GetRandom();
}
};
};
template <class T> void launchThreads(vector<T>& workers){
unsigned nThreads = thread::hardware_concurrency();//get the number of working cores
// unsigned nThreads = 1;
if(nThreads == 0) nThreads += 1;
vector<thread> threads(nThreads);
auto itWk = workers.begin();
while(itWk != workers.end()){
auto itTh = threads.begin();
while(itTh != threads.end() && itWk != workers.end()){
*itTh = thread(*itWk);//start each threads
++itTh;
++itWk;
}
for(thread& th : threads) th.join();//join them all to the current thread
}
}
void generateLiBranches(const unsigned nEvents = 1e4){
vector<myStruct> workers;//speed up the branch generations with a thread per branch
vector<TH2F> betaMapLi (5);
TFile LiFile("spectres_beta/cartes_li9.root");
betaMapLi[0] = *static_cast<TH2F*>(LiFile.Get("carte_0"));
betaMapLi[1] = *static_cast<TH2F*>(LiFile.Get("carte_1"));
betaMapLi[2] = *static_cast<TH2F*>(LiFile.Get("carte_2"));
betaMapLi[3] = *static_cast<TH2F*>(LiFile.Get("carte_3"));
betaMapLi[4] = *static_cast<TH2F*>(LiFile.Get("carte_4"));
workers.push_back(myStruct(nEvents, betaMapLi));
workers.push_back(myStruct(nEvents, betaMapLi));
workers.push_back(myStruct(nEvents, betaMapLi));
workers.push_back(myStruct(nEvents, betaMapLi));
workers.push_back(myStruct(nEvents, betaMapLi));
workers.push_back(myStruct(nEvents, betaMapLi));
workers.push_back(myStruct(nEvents, betaMapLi));
launchThreads(workers);
}
int main(int argc, char* argv[]){
if(argc == 2) generateLiBranches(stod(argv[1]));
else generateLiBranches();
return 0;
}
I have also attached an archive that contains the root file to read in the code, a simple Makefile and the source.
Please note that the code runs fine if one uncomments the line containing “unsigned nThreads = 1”. Reproducer.tar.bz2 (742 KB)
Well actually, if you set nEvents to high values and check your RAM usage you will see it sky-rocketing, and the
happens just after the RAM is full and the program is killed by the OS. So I guess this error is due to the system killing the program filling up the RAM.