Multithreaded graphics

Hello,

I have a compiled ROOT C++ program and created a canvas that needs some refresh.
I usually use gSystem->ProcessEvent(). However there is a time consuming calculation part.
I was thinking to make my computation on any other thread and keep the graphics on the main side

Would you have any suggestion to do so with ROOT ? Should I just use std::thread maybe ?
In particular how to detect when histogram are ready ? It seems the drawing can only be done in the main thread

I have been able to do what I needed with std::thread… but sometimes I get my program to crash.
I made sure that my workers finished before refreshing and going to the next iteration.

It looks like my canvas don’t like to be modified while redrawing it. Any suggestion ?

Assertion failed: ((!E || isa<DeclRefExpr>(E) || isa<MemberExpr>(E) || isa<FunctionParmPackExpr>(E)) && "Invalid Expr argument to DoMarkVarDeclReferenced"), function DoMarkVarDeclReferenced, file SemaExpr.cpp, line 18538.
Abort trap: 6

Hi,

Yes, if you are using normal ROOT graphics, it is only possible to update graphics from the main thread - where ROOT application runs and event processing is performed. You also should be aware that histograms or graphs, shown in the canvases, should not be modified from other threads - otherwise you can get concurrency conflicts.

In your case you can run computation with histograms/graphs filling in separate thread and use any signaling mechanism - condition_variable, semaphore, simple atomic variable - to transfer these graphical objects to main thread, there they will be displayed.

Since recently ROOT provides web-based implementation for TCanvas. It always can be tested
by running root --web=chrome or specifying gROOT->SetWebDisplay("chrome");.
It makes much less troubles when used in multi-threaded environment while graphics rendering performed in web browser and not in the applications threads. And it much less depends from gPad.

As alternative, you can try RCanvas and check tutorials/rcanvas/rcanvas_mt.cxx example.
This new canvas was designed to work properly in multithreaded environment.
It fully supports all histograms and graphs classes, which can be displayed with web-based TCanvas.

Regards,
Sergey

Yes, I frequently use the traditional ROOT graphics on one thread, and the heavy calculation staff on a separate thread. I used TThread for this in the beginning, but then switched to std::thread because of some issues with TThread::Printf (Data races TThread TTimer TApplication · Issue #8365 · root-project/root · GitHub) as well as longer latency (TThreadTimer behavior · Issue #8582 · root-project/root · GitHub).

Did you call ROOT::EnableThreadSafety?

If you need one working example, check out the “Steps to reproduce” on Data races TThread TTimer TApplication · Issue #8365 · root-project/root · GitHub

There you also have the instructions to find out where your program has races, using valgrind.

Note that whenever you want to modify something from the GUI, you should use add the following in the member functions accessed by external threads:

R__LOCKGUARD(&fGuiMutex); // scoped mutex

where fGuiMutex is a private member of the GUI TGMainFrame-derived custom class:

TMutex fGuiMutex{}; ///< GUI control mutex

1 Like

Dear @ferhue,

Thank you so much ! This was great input :))
I eventually started using std::thread as it is simple to use.

May I ask about the difference between these two lines ? Sorry, I don’t get explicit and implement MT…

ROOT::EnableImplicitMT();
ROOT::EnableThreadSafety();

Additionally, when you say:

Note that whenever you want to modify something from the GUI, you should use add the following in the member functions accessed by external threads
What do you mean by “modify something” ?
Deleting an histogram should be done before calling R__LOCKGUARD(&fGuiMutex); ?

Explicit vs implicit, see here:

What I was thinking of is that you have 5 threads, each of them calling fMainWindow->ModifyHistogram(), so to avoid races, you need sth like:

TGMainWindow::ModifyHistogram(x,y) {
R__LOCKGUARD(&fGuiMutex);
fHistogram->Fill(x,y);
fCanvas->Modified();
fCanvas->Update();
}
``
1 Like

I have actually a set of histograms that is being deleted on the fly (to free some memory), and some other being created… if I understand well, I should call something like:

TGMainWindow::ModifyHistogram(x,y) {
R__LOCKGUARD(&fGuiMutex);
fCanvas->Modified();
fCanvas->Update();
}

At least… should I make sure the canvas containing a deleted histogram should be deleted too ?

No need to delete the canvas, just delete the histogram from within the GUI class, after you call the lockguard.

TGMainWindow::DeleteHistogram(TH1* h) {
R__LOCKGUARD(&fGuiMutex);
delete h;
fCanvas->Modified();
fCanvas->Update();
}

I usually just create a canvas. Is there a way to do so from a TCanvas ?

You could create a class derived from TCanvas, such as:

class MyCanvas : public TCanvas {
MyCanvas(args) : TCanvas(args) {}
UpdateHistogram(h) { Lockguard...};
TMutex fGuiMutex{};
}

Another option is to derive from TGMainFrame and then have a private member with a TRootEmbeddedCanvas. See full example here: Oscilloscope class for binary waveforms 16-bit - #3 by ferhue
But the first option is simpler.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.