Why crash when double click on Pad?

I am using root 5.17.04 on Windows XP with Visual Studio 8. I am running in Release mode.

I wrote a simple standalone program which works fine if the user does not interact with the canvas’s. But after several double clicks on the histogram canvas I always get a crash with:

libGpad.dll!TObjLink::GetObject()  Line 129 + 0x11 bytes	C++
 	libGpad.dll!TPad::Pick(int px=607, int py=162, TObjLink * & pickobj=0x01436f08)  Line 3954 + 0x8 bytes	C++
 	libGpad.dll!TCanvas::Pick(int px=607, int py=162, TObjLink * & pickobj=0x01436f08)  Line 193 + 0x22 bytes	C++
 	libGpad.dll!TCanvas::Pick(int px=607, int py=162, TObject * prevSelObj=0x03b17880)  Line 1343 + 0x1e bytes	C++
 	libGpad.dll!TCanvas::HandleInput(EEventType event=kMouseMotion, int px=607, int py=162)  Line 1041 + 0x1e bytes	C++
 	libGui.dll!TRootCanvas::HandleContainerMotion(Event_t * event=0x0012fdac)  Line 1623 + 0x28 bytes	C++
 	libGui.dll!TRootContainer::HandleMotion(Event_t * ev=0x0012fdac)  Line 237 + 0x20 bytes	C++
 	libGui.dll!TGFrame::HandleEvent(Event_t * event=0x0012fdac)  Line 487 + 0x16 bytes	C++
 	libGui.dll!TGClient::HandleEvent(Event_t * event=0x0012fdac)  Line 753 + 0x16 bytes	C++
 	libGui.dll!TGClient::ProcessOneEvent()  Line 587	C++
 	libGui.dll!TGClient::HandleInput()  Line 633 + 0x8 bytes	C++
 	libGui.dll!TGInputHandler::Notify()  Line 81	C++
 	libCore.dll!TWinNTSystem::DispatchOneEvent(bool pendingOnly=false)  Line 1388 + 0x1b bytes	C++
 	libCore.dll!TSystem::InnerLoop()  Line 356 + 0x14 bytes	C++
 	libCore.dll!TSystem::Run()  Line 324 + 0x12 bytes	C++
 	libCore.dll!TApplication::Run(bool retrn=false)  Line 902 + 0x1c bytes	C++
 	Plotter.exe!main(int argc=1, char * * argv=0x02043810)  Line 60	C++
 	Plotter.exe!__tmainCRTStartup()  Line 597 + 0x17 bytes	C
 	kernel32.dll!7c816fd7() 		

My main loop is shown below:

#include "Plotter.h"

// other includes (have to come after root includes)
#include <iostream>

using namespace AWPlotter;
using namespace std;

namespace
{
    PlotterPtr plotter(new Plotter());
    vector<double>  X;
    vector<double>  Y;
    TRandom *r3 = new TRandom3();
}

//called from a thread
void* thePlotter(void *args)
{
    double cnt = 0;

    while (plotter->start())
    {
        double val = r3->Gaus();
        cnt        = cnt + 0.01;

        X.push_back(cnt);
        Y.push_back(val);

        plotter->plotHistogram(val);
        plotter->plotScatter(X, Y);

        gSystem->Sleep(300);            //300 ms
        //gSystem->ProcessEvents();
    }

    std::cout << " We exit thread thePlotter\n";

    return 0;
}

int main(int argc, char **argv)
{
    TApplication theApp("App", &argc, argv);

    plotter->init(20);
    plotter->pad(1)->Connect("Closed()", "TApplication", &theApp, "Terminate()"); 
    plotter->pad(2)->Connect("Closed()", "TApplication", &theApp, "Terminate()"); 

    TThread *th1 = new TThread("th1", thePlotter);
    th1->Run();

    try
    {
        theApp.Run();
    }
    catch (...)
    {
        int iDum = 0;
    }

    plotter->setStart(false);
    gSystem->Sleep(700); 

    th1->SetCancelAsynchronous();
    th1->Kill();

    std::cout << " We exited\n";

    return 0;
}

I tried adding gSystem->ProcessEvents() with no luck. I tried replacing theApp.Run() with theApp.Run(kTRUE) with no luck. Each loop I create a TGraph and protect it with a TThread::Lock(). Do I need to explicitely try to process a mouse click event?

I am attaching the 3 files in my project as well as the project.

Thanks,
Sanjeev
Plotter.zip (4.47 KB)
Plotter.cpp (2.58 KB)
Plotter.h (1.46 KB)
main.cpp (1.37 KB)

Hi,

I cannot reproduce the problem with svn trunk…
Could you try with the latest version of Root?

And BTW, in your code, you can replace:gSystem->Sleep(300); with:TThread::Sleep(0, 300000000); (just to avoid calls to globals inside the thread function)

Cheers,
Bertrand.

Hi,

I took your advise and upgraded to 5.19.04. I also now use TThread::Sleep(0, 300000000) to avoid using globals. However, not when I click on the histogram I get a

Warning in <TWinNTSystem::Run>: handle uncaugth exception, terminating

and theApp.Run(kTRUE) terminates. Note I click on the histogram about 5-10 times before this happens.

My new main is

#include "Plotter.h"

// other includes (have to come after root includes)
#include <iostream>

using namespace AWPlotter;
using namespace std;

namespace
{
    PlotterPtr plotter(new Plotter());
    vector<double>  X;
    vector<double>  Y;
    TRandom *r3 = new TRandom3();
}

//called from a thread
void* thePlotter(void *args)
{
    double cnt = 0;

    while (plotter->start())
    {
        double val = r3->Gaus();
        cnt        = cnt + 0.01;

        X.push_back(cnt);
        Y.push_back(val);

        plotter->plotHistogram(val);
        plotter->plotScatter(X, Y);

        TThread::Sleep(0, 300000000);     //300ms
    }

    std::cout << " We exit thread thePlotter\n";

    return 0;
}

int main(int argc, char **argv)
{
    TApplication theApp("App", &argc, argv);

    plotter->init(20);
    plotter->pad(1)->Connect("Closed()", "TApplication", &theApp, "Terminate()"); 
    plotter->pad(2)->Connect("Closed()", "TApplication", &theApp, "Terminate()"); 

    TThread *th1 = new TThread("th1", thePlotter);
    th1->Run();

    try
    {
        theApp.Run(kTRUE);
    }
    catch (...)
    {
        int iDum = 0;
    }

    plotter->setStart(false);
    TThread::Sleep(0, 700000000);     //700ms

    th1->SetCancelAsynchronous();
    th1->Kill();

    std::cout << " We exited\n";

    return 0;
}

p.s. Here is the new Project file. I had to add #include “w32pragma.h” and libMathCore.lib to make it work with 5.19.4

Thanks,
Sanjeev
Plotter.zip (8.38 KB)

Hi,

I cannot reproduce the problem with ROOT svn head/debug/MSVC++9.0
I will try with 5.19.04/MSVC++9.0 in debug/release mode and let you know (we don’t have MSVC++ 8.0 anymore…)

Cheers,
Bertrand.

May I ask you to try application with ROOT from BNL (goto root.bnl.gov/QtRoot/downloads/root.5.18.00.exe to install) to see whether your problem persists. Thank you

Hi Sanjeev,

Sorry, but I didn’t manage to reproduce the problem so far…
I’ll keep you in touch whenever there is something new on this issue…

Cheers,
Bertrand.

Thank you Bertrand and Valeri for looking into this. I am off on vacation in about 1 hour. Will try with VC9 and also the BNL build when I get back next week.

Regards,
Sanjeev

Hello,

Since I do not have access to VS9 I tried the following. First I used VS8 with root_v5.18.00.win32.vc80.msi but got the same crash as before.

 	libGpad.dll!00e12030() 	
 	[Frames below may be incorrect and/or missing, no symbols loaded for libGpad.dll]	
 	libGpad.dll!00df4d5f() 	
 	libGpad.dll!00df6e3b() 	
 	libGui.dll!02433b5e() 	
 	libGui.dll!024ca82d() 	
 	libGui.dll!024559a1() 	
 	libGui.dll!02433ee3() 	
 	gdk-1.3.dll!01f42da8() 	
 	libWin32gdk.dll!01ebc23f() 	
 	libGui.dll!024346b1() 	
 	kernel32.dll!7c802532() 	
 	ntdll.dll!7c90e57c() 	
 	kernel32.dll!7c80a027() 	
 	libGui.dll!024347ce() 	
 	kernel32.dll!7c80a017() 	
 	libCore.dll!1008b8ac() 	
 	kernel32.dll!7c809eff() 	
 	libThread.dll!003a4845() 	
 	libCore.dll!1003e843() 	
 	libCore.dll!1003e717() 	
 	libCore.dll!1003e6e9() 	
 	libThread.dll!003a494d() 	
 	libCore.dll!10004af0() 	
>	Plotter.exe!main(int argc=2, char * * argv=0x0012ffc0)  Line 59	C++
 	Plotter.exe!__CxxThrowException@8()  + 0x21d bytes	C++
 	Plotter.exe!__SEH_epilog4()  + 0x13 bytes	Asm
 	kernel32.dll!7c839aa8() 	

Than I tried the BNL version (root.5.18.00.exe) but this also failed but with different symptons. First the pads kept updating but part of them would not be drawn (ie no axis but only point). Than after clicking a few more times I got the following in the crash.

 	libGraf.dll!00b395ec() 	
 	[Frames below may be incorrect and/or missing, no symbols loaded for libGraf.dll]	
 	qt-mt337.dll!39d71cfc() 	
 	qt-mt337.dll!39d1ef45() 	
 	libGQt.dll!0179f752() 	
 	libThread.dll!003a48da() 	
 	libThread.dll!003a155c() 	
 	libThread.dll!003a490a() 	
 	libThread.dll!003a48da() 	
 	libThread.dll!003a155c() 	
 	libThread.dll!003a490a() 	
 	libThread.dll!003a2dba() 	
 	libThread.dll!003a3a15() 	
 	libGpad.dll!00df6fa9() 	
 	libGQt.dll!017a6a93() 	
 	qt-mt337.dll!39d6d4e7() 	
 	qt-mt337.dll!39d963d5() 	
 	qt-mt337.dll!39f4a560() 	
 	qt-mt337.dll!39d32551() 	
 	qt-mt337.dll!39d33783() 	
 	user32.dll!7e41c57a() 	
 	qt-mt337.dll!39d05741() 	
 	user32.dll!7e41ead2() 	
 	qt-mt337.dll!39d076d9() 	
 	user32.dll!7e43e195() 	
 	user32.dll!7e43e172() 	
 	usrhook1.dll!01ff104b() 	
 	user32.dll!7e431913() 	
 	user32.dll!7e418724() 	
 	user32.dll!7e418806() 	
 	user32.dll!7e4189bd() 	
 	user32.dll!7e4193f2() 	
 	user32.dll!7e418a00() 	
 	qt-mt337.dll!39d10481() 	
 	qt-mt337.dll!39d3fa8b() 	
 	qt-mt337.dll!39d33ae5() 	
 	libGQt.dll!01795783() 	
 	libGui.dll!01a2479e() 	
 	libCore.dll!1003e9b2() 	
 	libGui.dll!01a248ce() 	
 	libCore.dll!1008c0c6() 	
 	kernel32.dll!7c809eff() 	
 	libThread.dll!003a4835() 	
 	libCore.dll!1003e813() 	
 	libCore.dll!1003e6e7() 	
 	libCore.dll!1003e6b9() 	
 	libThread.dll!003a493d() 	
 	libCore.dll!10004ab0() 	
>	Plotter.exe!main(int argc=2, char * * argv=0x0012ffc0)  Line 59	C++
 	Plotter.exe!__CxxThrowException@8()  + 0x21d bytes	C++
 	Plotter.exe!__SEH_epilog4()  + 0x13 bytes	Asm
 	kernel32.dll!7c839aa8() 	

Since I am the only one seeing this failure mode I assume its something wrong in my setup or perhaps the way I link. One quick question. Does the order in which one links the libraries matter? I link in the following order:

Winmm.lib
libCore.lib
libCint.lib
libHist.lib
libGraf.lib
libGraf3d.lib
libGpad.lib
libTree.lib
libRint.lib
libPostscript.lib
libMatrix.lib
libPhysics.lib
libThread.lib

Regards,
Sanjeev

The bottom line:
ROOT is NOT thread-safe application and what you got is the expected outcome. ROOT Signals /Slots are not thread either.

I’ll try to investigate your concrete application later on. However such analysis can not change the general conclusion, namely, one should not call the ROOT event loop and engage the ROOT Signals/ Slots across of the thread borders.

See my post above

Thanks for looking into this.

I was hoping to get around the thread safety issue by protecting sensitive areas of the code by locking the main mutex as below:

TThread::Lock();
m_graph.reset(new TGraph(N, x, y)); 
TMarker* m  = new TMarker(x[N-1], y[N-1], 22);
TThread::UnLock();

I saw in one post that all “new” calls needed to be protected via the mutex. Are there other calls as well that need to be protected? ie. SetMarkerColor.

[quote=“supandey”]Thanks for looking into this.
I was hoping to get around the thread safety issue by protecting sensitive areas of the code by locking the main mutex as below:[/quote]The problem is slightly :unamused: more complicated. You are trying to make your piece of the code thread-safe. Yes, one has to do that. However, it can not help as soon as the core ROOT /Cint /ROOT Signal / ROOT Slot are not thread-safe. It is out of your control :frowning: . You can not :cry: change the ROOT code to make it thread safe.[quote=“supandey”] TThread::Lock(); m_graph.reset(new TGraph(N, x, y)); TMarker* m = new TMarker(x[N-1], y[N-1], 22); TThread::UnLock();
I saw in one post that all “new” calls needed to be protected via the mutex. [/quote]What you did above is redundant. The memory allocation is thread safe under VC++as soon as one compiles the code with the “mulit-thread” VC++ compiler option. It is not where your problem comes from.[quote=“supandey”]Are there other calls as well that need to be protected? ie. SetMarkerColor.[/quote]You need to change your :exclamation: approach.
Can you redesign your code to make your worker thread('s) ROOT free. That may have been the ideal solution.
I would like to call your attention that the thread-safeness is NOT only problem you have to resolve keeping your design untouched.
The bottom line - change the design, make the worker thread ROOT-free or at least ROOT GUI / ROOT Signal / ROOT Slot communication free.
In fact, the rule of the thumb is do not use any thread if your goal can be achieved with none. Can you elaborate on why you want very threads, what you want to achieve?

Thanks for the useful information.

My problem is that I have an incoming stream of data, which I sample periodically, do some calculations, and plot some results. The periodic sampling and calculations are easy to move into their own thread. But I am not sure how I communicate to ROOT the new data I want to plot.

Would one solution be to create my TApplication theApp object as before and then call

theApp.Run(kTRUE);

Then when I want to update my histograms/graphs I would programtically exit the event loop, update my histograms/graphs and than enter the event loop again? If this works could you tell me how to exit the event loop via an API call. I know I can do it via “Select “Exit ROOT” from Canvas “File” menu to exit”. Also can I safely exit the event loop from a thread?

Thanks,
Sanjeev

I think what you really need is
[ul] 1. Use the “standard” ROOT main
2. Remove threads
3. Remove boost
4. Use root.cern.ch/root/html/TTimer.ht … SingleShot
5. From within the receiver of the SignleShot.
Do whatever you want ( but Draw() :exclamation: )
followed by the next SignleShot
6. Move your Draw invocation to your init method.
Keep in mind, the TObject::Draw() method does not draw :unamused: . It is to add the object pointer to the list of the TPad primitivies. The “Draw” Invocationfor the second time makes no good. It does entail the TPad list to be updated with the new pointer. Which is redundant at least, under threads is not safe.
Your first post in this thread does manifest the list of the TPad primitives corruption:[quote=“supandey”]

libGpad.dll!TObjLink::GetObject() Line 129 + 0x11 bytes C++ libGpad.dll!TPad::Pick(int px=607, int py=162, TObjLink * & pickobj=0x01436f08) Line 3954 + 0x8 bytes C++ libGpad.dll!TCanvas::Pick(int px=607, int py=162, TObjLink * & pickobj=0x01436f08) Line 193 + 0x22 bytes C++ [/quote]
7. One have to call Draw at once , you MUST not call Draw in loop. [/ul]Please, look up the tutorial root.cern.ch/root/html/examples/hsimple.C.html . Pay your attention for the line

if (i == kUPDATE) hpx->Draw();there.
Can you try that?

I’ll try to create the working example later on. I am typing from the meeting right now :wink:
As soon as your code works with no crash, one can review the second worker thread issue if needed. It will be simple :wink:

Thanks for the tips. I am not sure what the standard ROOT main is but I tried the following code. Instead of Timer::SingleShot I try to use the Timer as show below. Unfortunately I get an exception in Worker::act() when it tried to fill the first histogram. Am I making an obvious mistake?

Unhandled exception at 0x00401050 in testPlot.exe: 0xC0000005: Access violation reading location 0xbaadf00d.

Call Stack:

>	testPlot.exe!Worker::act()  Line 25 + 0x3 bytes	C++
 	testPlot.exe!G__WorkerDict_152_0_2(G__value * result7=0x01d02010, const char * funcname=0x00000000, G__param * libp=0x01d02060, int hash=0)  Line 155	C++
 	libCint.dll!00427b54() 	
 	[Frames below may be incorrect and/or missing, no symbols loaded for libCint.dll]	
 	libCore.dll!1002579e() 	
 	msvcr80.dll!78160e30() 	
 	msvcr80.dll!78160e30() 	
 	libCore.dll!1005f57a() 	
 	libCore.dll!10026b0f() 	
 	libCore.dll!100286d1() 	
 	libCore.dll!100052ed() 	
 	libCore.dll!100484ad() 	
 	libCore.dll!10047fa8() 	
 	libCore.dll!10088ecb() 	
 	kernel32.dll!7c80a017() 	
 	kernel32.dll!7c802532() 	
 	libGui.dll!023e47ce() 	
 	libCore.dll!1008b962() 	
 	ntdll.dll!7c91056d() 	
 	msvcr80.dll!78134c39() 	
 	libCore.dll!1003e843() 	
 	libCore.dll!1003e717() 	
 	libCore.dll!1003e6e9() 	
 	libCore.dll!10004af0() 	
 	testPlot.exe!main(int argc=, char * * argv=)  Line 91	C++
 	testPlot.exe!__tmainCRTStartup()  Line 597 + 0x17 bytes	C
 	kernel32.dll!7c816fd7() 	
//---Worker.cpp
#include "Worker.h"
#include "TApplication.h"
#include "TTimer.h"
#include "Riostream.h"

#include "TGraph.h"
#include "TMath.h"
#include "TCanvas.h"
#include "TRandom.h"

namespace
{
    TCanvas *c1;
    TPad *pad1;
    TPad *pad2;
}

void 
Worker::act()
{
    //std::cout << "QKRQ\n"; 

    double val1 = gRandom->Gaus(-1,1.5);
    m_h1->Fill(val1);

    double val2 = gRandom->Gaus(-1,1.5);
    m_h1->Fill(val2);

    pad1->Modified();
    pad1->Update();

    pad2->Modified();
    pad2->Update();

    c1->Modified();
    c1->Update();
}

void
Worker::init()
{
    TH1F *m_h1 = new TH1F("h1","h1",100,-4,4);
    TH1F *m_h2 = new TH1F("h2","h2",100,-4,4);
    
    double val = gRandom->Gaus(-1,1.5);

    m_h1->Fill(val);
    m_h2->Fill(val);

    pad1->cd();
    m_h1->Draw("e1p");

    pad2->cd();
    m_h2->Draw("e1p");

    pad1->Modified();
    pad1->Update();

    pad2->Modified();
    pad2->Update();

    c1->Modified();
    c1->Update();
}

int main(int argc, char **argv)
{
    TApplication theApp("App", &argc, argv);

    c1 = new TCanvas("c1","The example",800,400);
    pad1 = new TPad("pad1","This is pad1",0.02,0.02,0.48,0.98,33);
    pad2 = new TPad("pad2","This is pad2",0.52,0.02,0.98,0.98,33);

    c1->cd();
    c1->SetGrid();
    pad1->Draw();

    c1->cd();
    pad2->Draw();

    Worker *wrk = new Worker();
    wrk->init();

    TTimer *timer = new TTimer(300); 
    timer->Connect("Timeout()", "Worker", wrk, "act()");
    timer->Start();

    theApp.Run();

    return 0;
}
//---Worker.h
#ifndef SAW_WORKER
#define SAW_WORKER

#include "TH1.h"

class Worker
{
public:
    // Construction
    Worker() {} 
    virtual ~Worker() {}

    // Operations
    void act();
    void init();

private:
    //Data
    TH1D* m_h1;
    TH1D* m_h2;

};

#endif

And I generate the dictionary via

rootcint -f WorkerDict.cxx -c Worker.h LinkDef.h

Where LinkDef.h contains

#ifdef __CINT__

#pragma link off all globals; 
#pragma link off all classes; 
#pragma link off all functions; 

#pragma link C++ class Worker; 

#endif 

[quote=“supandey”]Am I making an obvious mistake?
[/quote]You assigned no value for your data member pointers:

TH1D* m_h1; TH1D* m_h2;

The ROOT session

root [0] .L Worker.cxx++ Info in <TWinNTSystem::ACLiC>: creating shared library E:\cygwin\home\fine\work\ROOT\expert\RootForum28078\Worker_cxx.dll 27440109_cint.cxx srdro_.cxx Creating library E:\cygwin\home\fine\work\ROOT\expert\RootForum28078\Worker_cxx.lib and object E:\cygwin\home\fine\work\ROOT\expert\RootForum28078\Worker_cxx .exp root [1] Worker::RunWorker()
with TWO files:

[code]//—Worker.cxx
#include “Worker.h”
#include “TTimer.h”
#include “Riostream.h”

#include “TGraph.h”
#include “TMath.h”
#include “TCanvas.h”
#include “TRandom.h”
#include “TH1.h”

namespace
{
TCanvas *c1;
TPad *pad1;
TPad *pad2;
}

void
Worker::act()
{
double val1 = gRandom->Gaus(-1,1.5);
m_h1->Fill(val1);

double val2 = gRandom->Gaus(-1,1.5); 
m_h2->Fill(val2); 

pad1->Modified(); 
pad2->Modified(); 
c1->Update();
TTimer::SingleShot(300,"Worker", this, "act()");

}

void
Worker::init()
{
m_h1 = new TH1D(“h1”,“h1”,100,-4,4);
m_h2 = new TH1D(“h2”,“h2”,100,-4,4);

double val = gRandom->Gaus(-1,1.5); 

m_h1->Fill(val); 
m_h2->Fill(val); 

pad1->cd(); 
m_h1->Draw("e1p"); 

pad2->cd(); 
m_h2->Draw("e1p"); 

}

void Worker::RunWorker()
{
c1 = new TCanvas(“c1”,“The example”,800,400);
pad1 = new TPad(“pad1”,“This is pad1”,0.02,0.02,0.48,0.98,33);
pad2 = new TPad(“pad2”,“This is pad2”,0.52,0.02,0.98,0.98,33);

c1->cd(); 
c1->SetGrid(); 
pad1->Draw(); 

c1->cd(); 
pad2->Draw(); 

Worker *wrk = new Worker(); 
wrk->init(); 

wrk->act();

}[/code]
and

[code]//—Worker.h
#ifndef SAW_WORKER
#define SAW_WORKER

class TH1D;

class Worker
{
public:
// Construction
Worker(): m_h1(0),m_h2(0) {;}
virtual ~Worker() {}

// Operations 
void act(); 
void init(); 
static void RunWorker();

private:
//Data
TH1D* m_h1;
TH1D* m_h2;

};

#endif [/code]
Does generate the correct result with no crash


Thank you! That solves my problem.

Your code also worked in my VS8 project. Still not sure why my old version failed but perhaps the key differenec was my not using the TTimer::SingleShot before.

Very appreciative,
Sanjeev

:smiley:

[quote=“supandey”]Still not sure why my old version failed but perhaps the key differenec was my not using the TTimer::SingleShot before.[/quote]Your problem had nothing to do with the “TTimer::SingleShot”.
You made an obvious :unamused: mistake.
Your method void Worker::init() { TH1F *m_h1 = new TH1F("h1","h1",100,-4,4); TH1F *m_h2 = new TH1F("h2","h2",100,-4,4); uses the local :bulb: variables m_h1 and m_h2. However, I believe you wanted to use the data-members Worker::m_h1 and Worker::m_h2 of your class class Worker { ....... private: //Data TH1D* m_h1; TH1D* m_h2; }; instead. As result the method void Worker::act() { double val1 = gRandom->Gaus(-1,1.5); m_h1->Fill(val1); invokes the “Fill” method at the time the variable m_h1 is assigned no value :!: yet, so it crashes :imp: .