Using TTree:Draw Concurrently in TThreads

Hi All,

I’d like to use TTree::Draw on the same TTree inside of multiple TThreads. The idea is to make a TEventList in each thread by making a different selection…run numbers in this case. And then process those events inside the thread. The problem is that TTree::Draw results in a seg fault when it is called concurrently from multiple threads even though the “>>objectToWriteTo” argument is different in each thread.

I have attached the source code I’m using as well as the ROOT file with the tree that I’m accessing. To run the code and reproduce the results do the following in a ROOT session
.L drawFromThreads.C+
drawFromThreads()

After reproducing the error try moving the line

  inTree->Draw(Form(">>elist_%d",threadIndex),Form("RunNum==%d",rNum));

which exists in void *threadFunction() to inside the TThread lock/Unlock block above to see that the code works when TTree::Draw is called sequentially from each thread.

Of course, moving the TTree::Draw call to inside the lock/unlock block is not a permanent solution because it is the operation which takes the longest and is the primary reason for doing multiple threads.

I’m using the following system configuration:
ROOT v5.34.04
Ubuntu 12.10
gcc version 4.7.2

Many thanks in advance for your time and suggestions. Or, If you know of an easier way of accomplishing what I want to do I’m all ears…

-Chris
RunAndEventTree.root (535 KB)
drawFromThreads.C (2.53 KB)

How about “PROOF-Lite”?

I have heard of PROOF-Lite, but I don’t have any experience with it. Thanks for the suggestion though…I’ll certainly look further into it as I plan on writing multithreaded analysis scripts.

Okay…so I’m still trying to get this to work. I’ve run into some weird errors that I’ve never seen before. As a result I’m wondering if there is something internal to ROOT that is preventing me from doing what I want to do.

I’ve written another script which has a different parallel implementation than before. I now have two separate trees - an event tree and a track tree - which I load from the same file. I want to process them in separate threads. In each thread an operation on the respective tree is called.

In the track thread I utilize the TTree::CopyTree() function…

[code]void *findTracksThreadFunc(void *trackThreadArgs){

while (trackRunsComplete < runNumberVec.size()){

TThread::Lock();
cout <<"Track Thread working on: " <<trackRunsComplete <<endl;
TThread::UnLock();

TTree *tree = new TTree();

//TThread::Lock();
tree = trackTree->CopyTree(Form(“runNum==%ld”,runNumberVec[trackRunsComplete]));
//TThread::UnLock();

TThread::Lock();
trackSelTreeVec.push_back(tree);
trackRunsComplete++;
TThread::UnLock();

};

return 0;
}
[/code]

while in the event thread I use the TTree::Draw() function.

[code]void *findEventsThreadFunc(void *eventThreadArgs){

while (eventRunsComplete < runNumberVec.size()){

TThread::Lock();
cout <<"Event Thread working on: " <<eventRunsComplete <<endl;
TThread::UnLock();

TEventList *eventList = new TEventList();
//TThread::Lock();
eventTree->Draw(">>elist",Form("runNum==%ld",runNumberVec[eventRunsComplete]));
eventList = (TEventList*)gDirectory->Get("elist");
//TThread::UnLock();

TThread::Lock();
eventSelTreeVec.push_back(eventList);
eventRunsComplete++;
TThread::UnLock();

};

return 0;
}[/code]

Each thread runs fine when it is running alone and the whole script runs fine if I TThread::Lock/UnLock the operation on the tree. However, if I remove the Lock/Unlock I get errors and crashes.

Does anyone know if there is something deep inside ROOT which prevents concurrent operations on TTrees? Its starting to seem like there is…and this seems like a really unfortunate hindrance/bug.

Thanks for you thoughts,
Chris

Hi Chris,

The TTree objects contains both static and transient state. In first approximation you can not use the same TTree object from two different thread has they will be stepping on each other (i.e. reading entry n will update the ‘single’ internal buffer to show the value of entry n, and if at the same time you read entry m the ‘single’ internal buffer will be updated to show the other values … usually inconsistently).

To do what you want, you can use ProofLite (which will use separate process to access the file on disk) or you must use several instance of the TFile and TTree so that only one thread access each TTree instance at once.

Cheers,
Philippe.