// @(#)root/thread:$Id$
// Author: Fons Rademakers 02/07/97
/*************************************************************************
* Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
* All rights reserved. *
* *
* For the licensing terms see $ROOTSYS/LICENSE. *
* For the list of contributors see $ROOTSYS/README/CREDITS. *
*************************************************************************/
//////////////////////////////////////////////////////////////////////////
// //
// TThread //
// //
// This class implements threads. A thread is an execution environment //
// much lighter than a process. A single process can have multiple //
// threads. The actual work is done via the TThreadImp class (either //
// TPosixThread or TWin32Thread). //
// //
//////////////////////////////////////////////////////////////////////////
#include "RConfigure.h"
#include "TThread.h"
#include "TThreadImp.h"
#include "TThreadFactory.h"
#include "TROOT.h"
#include "TApplication.h"
#include "TVirtualPad.h"
#include "TMethodCall.h"
#include "TTimeStamp.h"
#include "TError.h"
#include "snprintf.h"
#include "Api.h"
#include "Varargs.h"
TThreadImp *TThread::fgThreadImp = 0;
Long_t TThread::fgMainId = 0;
TThread *TThread::fgMain = 0;
TMutex *TThread::fgMainMutex;
char *volatile TThread::fgXAct = 0;
TMutex *TThread::fgXActMutex = 0;
TCondition *TThread::fgXActCondi = 0;
void **volatile TThread::fgXArr = 0;
volatile Int_t TThread::fgXAnb = 0;
volatile Int_t TThread::fgXArt = 0;
static void CINT_alloc_lock() { gGlobalMutex->Lock(); }
static void CINT_alloc_unlock() { gGlobalMutex->UnLock(); }
//------------------------------------------------------------------------------
// Set gGlobalMutex to 0 when Thread library gets unloaded
class TGlobalMutexGuard {
public:
TGlobalMutexGuard() { }
~TGlobalMutexGuard() { gGlobalMutex = 0; }
};
static TGlobalMutexGuard gGlobalMutexGuardInit;
//------------------------------------------------------------------------------
class TJoinHelper {
private:
TThread *fT;
TThread *fH;
void **fRet;
Long_t fRc;
TMutex *fM;
TCondition *fC;
static void JoinFunc(void *p);
public:
TJoinHelper(TThread *th, void **ret);
~TJoinHelper();
Int_t Join();
};
//______________________________________________________________________________
TJoinHelper::TJoinHelper(TThread *th, void **ret)
: fT(th), fRet(ret), fRc(0), fM(new TMutex), fC(new TCondition(fM))
{
// Constructor of Thread helper class.
fH = new TThread("JoinHelper", JoinFunc, this);
}
//______________________________________________________________________________
TJoinHelper::~TJoinHelper()
{
// Destructor.
delete fC;
delete fM;
delete fH;
}
//______________________________________________________________________________
void TJoinHelper::JoinFunc(void *p)
{
// Static method which runs in a separate thread to handle thread
// joins without blocking the main thread.
TJoinHelper *jp = (TJoinHelper*)p;
jp->fRc = jp->fT->Join(jp->fRet);
jp->fM->Lock();
jp->fC->Signal();
jp->fM->UnLock();
TThread::Exit(0);
}
//______________________________________________________________________________
Int_t TJoinHelper::Join()
{
// Thread join function.
fM->Lock();
fH->Run();
while (kTRUE) {
int r = fC->TimedWaitRelative(100); // 100 ms
if (r == 0) break;
gSystem->ProcessEvents();
}
fM->UnLock();
return fRc;
}
//------------------------------------------------------------------------------
ClassImp(TThread)
//______________________________________________________________________________
TThread::TThread(VoidRtnFunc_t fn, void *arg, EPriority pri)
: TNamed("<anon>", "")
{
// Create a thread. Specify the function or static class method
// to be executed by the thread and a pointer to the argument structure.
// The user function should return a void*. To start the thread call Run().
fDetached = kFALSE;
fFcnVoid = 0;
fFcnRetn = fn;
fPriority = pri;
fThreadArg = arg;
Constructor();
fNamed = kFALSE;
}
//______________________________________________________________________________
TThread::TThread(VoidFunc_t fn, void *arg, EPriority pri)
: TNamed("<anon>", "")
{
// Create a detached thread. Specify the function or class method
// to be executed by the thread and a pointer to the argument structure.
// To start the thread call Run().
fDetached = kTRUE;
fFcnRetn = 0;
fFcnVoid = fn;
fPriority = pri;
fThreadArg = arg;
Constructor();
fNamed = kFALSE;
}
//______________________________________________________________________________
TThread::TThread(const char *thname, VoidRtnFunc_t fn, void *arg,
EPriority pri) : TNamed(thname, "")
{
// Create thread with a name. Specify the function or class method
// to be executed by the thread and a pointer to the argument structure.
// The user function should return a void*. To start the thread call Run().
fDetached = kFALSE;
fFcnVoid = 0;
fFcnRetn = fn;
fPriority = pri;
fThreadArg = arg;
Constructor();
fNamed = kTRUE;
}
//______________________________________________________________________________
TThread::TThread(const char *thname, VoidFunc_t fn, void *arg,
EPriority pri) : TNamed(thname, "")
{
// Create a detached thread with a name. Specify the function or class
// method to be executed by the thread and a pointer to the argument
// structure. To start the thread call Run().
fDetached = kTRUE;
fFcnRetn = 0;
fFcnVoid = fn;
fPriority = pri;
fThreadArg = arg;
Constructor();
fNamed = kTRUE;
}
//______________________________________________________________________________
TThread::TThread(Int_t id)
{
// Create a TThread for a already running thread.
fDetached = kTRUE;
fFcnRetn = 0;
fFcnVoid = 0;
fPriority = kNormalPriority;
fThreadArg = 0;
Constructor();
fNamed = kFALSE;
fId = (id ? id : SelfId());
fState = kRunningState;
if (gDebug)
Info("TThread::TThread", "TThread attached to running thread");
}
//______________________________________________________________________________
void TThread::Init()
{
// Initialize global state and variables once.
if (fgThreadImp) return;
fgThreadImp = gThreadFactory->CreateThreadImp();
fgMainId = fgThreadImp->SelfId();
fgMainMutex = new TMutex(kTRUE);
gThreadTsd = TThread::Tsd;
gThreadXAR = TThread::XARequest;
// Create the single global mutex
gGlobalMutex = new TMutex(kTRUE);
G__set_alloclockfunc(CINT_alloc_lock);
G__set_allocunlockfunc(CINT_alloc_unlock);
}
//______________________________________________________________________________
void TThread::Constructor()
{
// Common thread constructor.
fHolder = 0;
fClean = 0;
fState = kNewState;
fId = -1;
fHandle= 0;
if (!fgThreadImp) Init();
SetComment("Constructor: MainMutex Locking");
Lock();
SetComment("Constructor: MainMutex Locked");
fTsd[0] = gPad;
fTsd[1] = 0; // For TClass
if (fgMain) fgMain->fPrev = this;
fNext = fgMain; fPrev = 0; fgMain = this;
UnLock();
SetComment();
// thread is set up in initialisation routine or Run().
}
//______________________________________________________________________________
TThread::~TThread()
{
// Cleanup the thread.
if (gDebug)
Info("TThread::~TThread", "thread deleted");
// Disconnect thread instance
SetComment("Destructor: MainMutex Locking");
Lock();
SetComment("Destructor: MainMutex Locked");
if (fPrev) fPrev->fNext = fNext;
if (fNext) fNext->fPrev = fPrev;
if (fgMain == this) fgMain = fNext;
UnLock();
SetComment();
if (fHolder) *fHolder = 0;
}
//______________________________________________________________________________
Int_t TThread::Delete(TThread *&th)
{
// Static method to delete the specified thread.
if (!th) return 0;
th->fHolder = &th;
if (th->fState == kRunningState) { // Cancel if running
th->fState = kDeletingState;
if (gDebug)
th->Info("TThread::Delete", "deleting thread");
th->Kill();
return -1;
}
CleanUp();
return 0;
}
//______________________________________________________________________________
Int_t TThread::Exists()
{
// Static method to check if threads exist.
// returns the number of running threads.
Lock();
Int_t num = 0;
for (TThread *l = fgMain; l; l = l->fNext)
num++; //count threads
UnLock();
return num;
}
//______________________________________________________________________________
void TThread::SetPriority(EPriority pri)
{
// Set thread priority.
fPriority = pri;
}
//______________________________________________________________________________
TThread *TThread::GetThread(Long_t id)
{
// Static method to find a thread by id.
TThread *myTh;
Lock();
for (myTh = fgMain; myTh && (myTh->fId != id); myTh = myTh->fNext) { }
UnLock();
return myTh;
}
//______________________________________________________________________________
TThread *TThread::GetThread(const char *name)
{
// Static method to find a thread by name.
TThread *myTh;
Lock();
for (myTh = fgMain; myTh && (strcmp(name, myTh->GetName())); myTh = myTh->fNext) { }
UnLock();
return myTh;
}
//______________________________________________________________________________
TThread *TThread::Self()
{
// Static method returning pointer to current thread.
return GetThread(SelfId());
}
//______________________________________________________________________________
Long_t TThread::Join(void **ret)
{
// Join this thread.
if (fId == -1) {
Error("Join", "thread not running");
return -1;
}
if (fDetached) {
Error("Join", "cannot join detached thread");
return -1;
}
if (SelfId() != fgMainId)
return fgThreadImp->Join(this, ret);
// do not block the main thread, use helper thread
TJoinHelper helper(this, ret);
return helper.Join();
}
//______________________________________________________________________________
Long_t TThread::Join(Long_t jid, void **ret)
{
// Static method to join a thread by id.
TThread *myTh = GetThread(jid);
if (!myTh) {
::Error("TThread::Join", "cannot find thread 0x%lx", jid);
return -1L;
}
return myTh->Join(ret);
}
//______________________________________________________________________________
Long_t TThread::SelfId()
{
// Static method returning the id for the current thread.
if (!fgThreadImp) Init();
return fgThreadImp->SelfId();
}
//______________________________________________________________________________
Int_t TThread::Run(void *arg)
{
// Start the thread. This starts the static method TThread::Function()
// which calls the user function specified in the TThread ctor with
// the arg argument.
if (arg) fThreadArg = arg;
SetComment("Run: MainMutex locking");
Lock();
SetComment("Run: MainMutex locked");
int iret = fgThreadImp->Run(this);
fState = iret ? kInvalidState : kRunningState;
if (gDebug)
Info("TThread::Run", "thread run requested");
UnLock();
SetComment();
return iret;
}
//______________________________________________________________________________
Int_t TThread::Kill()
{
// Kill this thread.
if (fState != kRunningState && fState != kDeletingState) {
if (gDebug)
Warning("TThread::Kill", "thread is not running");
return 13;
} else {
if (fState == kRunningState ) fState = kCancelingState;
return fgThreadImp->Kill(this);
}
}
//______________________________________________________________________________
Int_t TThread::Kill(Long_t id)
{
// Static method to kill the thread by id.
TThread *th = GetThread(id);
if (th) {
return fgThreadImp->Kill(th);
} else {
if (gDebug)
::Warning("TThread::Kill(Long_t)", "thread 0x%lx not found", id);
return 13;
}
}
//______________________________________________________________________________
Int_t TThread::Kill(const char *name)
{
// Static method to kill thread by name.
TThread *th = GetThread(name);
if (th) {
return fgThreadImp->Kill(th);
} else {
if (gDebug)
::Warning("TThread::Kill(const char*)", "thread %s not found", name);
return 13;
}
}
//______________________________________________________________________________
Int_t TThread::SetCancelOff()
{
// Static method to turn off thread cancellation.
return fgThreadImp->SetCancelOff();
}
//______________________________________________________________________________
Int_t TThread::SetCancelOn()
{
// Static method to turn on thread cancellation.
return fgThreadImp->SetCancelOn();
}
//______________________________________________________________________________
Int_t TThread::SetCancelAsynchronous()
{
// Static method to set asynchronous cancellation.
return fgThreadImp->SetCancelAsynchronous();
}
//______________________________________________________________________________
Int_t TThread::SetCancelDeferred()
{
// Static method to set deffered cancellation.
return fgThreadImp->SetCancelDeferred();
}
//______________________________________________________________________________
Int_t TThread::CancelPoint()
{
// Static method to set a cancellation point.
return fgThreadImp->CancelPoint();
}
//______________________________________________________________________________
Int_t TThread::CleanUpPush(void *free, void *arg)
{
// Static method which pushes thread cleanup method on stack.
// Returns 0 in case of success and -1 in case of error.
TThread *th = Self();
if (th)
return fgThreadImp->CleanUpPush(&(th->fClean), free, arg);
return -1;
}
//______________________________________________________________________________
Int_t TThread::CleanUpPop(Int_t exe)
{
// Static method which pops thread cleanup method off stack.
// Returns 0 in case of success and -1 in case of error.
TThread *th = Self();
if (th)
return fgThreadImp->CleanUpPop(&(th->fClean), exe);
return -1;
}
//______________________________________________________________________________
Int_t TThread::CleanUp()
{
// Static method to cleanup the calling thread.
TThread *th = Self();
if (!th) return 13;
fgThreadImp->CleanUp(&(th->fClean));
fgMainMutex->CleanUp();
if (fgXActMutex)
fgXActMutex->CleanUp();
if (th->fHolder)
delete th;
return 0;
}
//______________________________________________________________________________
void TThread::AfterCancel(TThread *th)
{
// Static method which is called after the thread has been canceled.
if (th) {
th->fState = kCanceledState;
if (gDebug)
th->Info("TThread::AfterCancel", "thread is canceled");
} else
::Error("TThread::AfterCancel", "zero thread pointer passed");
}
//______________________________________________________________________________
Int_t TThread::Exit(void *ret)
{
// Static method which terminates the execution of the calling thread.
return fgThreadImp->Exit(ret);
}
//______________________________________________________________________________
Int_t TThread::Sleep(ULong_t secs, ULong_t nanos)
{
// Static method to sleep the calling thread.
UInt_t ms = UInt_t(secs * 1000) + UInt_t(nanos / 1000000);
if (gSystem) gSystem->Sleep(ms);
return 0;
}
//______________________________________________________________________________
Int_t TThread::GetTime(ULong_t *absSec, ULong_t *absNanoSec)
{
// Static method to get the current time. Returns
// the number of seconds.
TTimeStamp t;
if (absSec) *absSec = t.GetSec();
if (absNanoSec) *absNanoSec = t.GetNanoSec();
return t.GetSec();
}
//______________________________________________________________________________
Int_t TThread::Lock()
{
// Static method to lock the main thread mutex.
return (fgMainMutex ? fgMainMutex->Lock() : 0);
}
//______________________________________________________________________________
Int_t TThread::TryLock()
{
// Static method to try to lock the main thread mutex.
return (fgMainMutex ? fgMainMutex->TryLock() : 0);
}
//______________________________________________________________________________
Int_t TThread::UnLock()
{
// Static method to unlock the main thread mutex.
return (fgMainMutex ? fgMainMutex->UnLock() : 0);
}
//______________________________________________________________________________
void *TThread::Function(void *ptr)
{
// Static method which is called by the system thread function and
// which in turn calls the actual user function.
TThread *th;
void *ret, *arg;
TThreadCleaner dummy;
th = (TThread *)ptr;
// Default cancel state is OFF
// Default cancel type is DEFERRED
// User can change it by call SetCancelOn() and SetCancelAsynchronous()
SetCancelOff();
SetCancelDeferred();
CleanUpPush((void *)&AfterCancel, th); // Enable standard cancelling function
if (gDebug)
th->Info("TThread::Function", "thread is running");
arg = th->fThreadArg;
th->fState = kRunningState;
if (th->fDetached) {
//Detached, non joinable thread
(th->fFcnVoid)(arg);
ret = 0;
th->fState = kFinishedState;
} else {
//UnDetached, joinable thread
ret = (th->fFcnRetn)(arg);
th->fState = kTerminatedState;
}
CleanUpPop(1); // Disable standard canceling function
if (gDebug)
th->Info("TThread::Function", "thread has finished");
TThread::Exit(ret);
return ret;
}
//______________________________________________________________________________
void TThread::Ps()
{
// Static method listing the existing threads.
TThread *l;
int i;
if (!fgMain) {
::Info("TThread::Ps", "no threads have been created");
return;
}
Lock();
int num = 0;
for (l = fgMain; l; l = l->fNext)
num++;
char cbuf[256];
printf(" Thread State\n");
for (l = fgMain; l; l = l->fNext) { // loop over threads
memset(cbuf, ' ', sizeof(cbuf));
snprintf(cbuf, sizeof(cbuf), "%3d %s:0x%lx", num--, l->GetName(), l->fId);
i = strlen(cbuf);
if (i < 30)
cbuf[i] = ' ';
cbuf[30] = 0;
printf("%30s", cbuf);
switch (l->fState) { // print states
case kNewState: printf("Idle "); break;
case kRunningState: printf("Running "); break;
case kTerminatedState: printf("Terminated "); break;
case kFinishedState: printf("Finished "); break;
case kCancelingState: printf("Canceling "); break;
case kCanceledState: printf("Canceled "); break;
case kDeletingState: printf("Deleting "); break;
default: printf("Invalid ");
}
if (l->fComment[0]) printf(" // %s", l->fComment);
printf("\n");
} // end of loop
UnLock();
}
//______________________________________________________________________________
void **TThread::Tsd(void *dflt, Int_t k)
{
// Static method returning a pointer to thread specific data container
// of the calling thread.
TThread *th = TThread::Self();
if (!th) { //Main thread
return (void**)dflt;
} else {
return &(th->fTsd[k]);
}
}
//______________________________________________________________________________
void TThread::Printf(const char *va_(fmt), ...)
{
// Static method providing a thread safe printf. Appends a newline.
va_list ap;
va_start(ap,va_(fmt));
Int_t buf_size = 2048;
char *buf;
again:
buf = new char[buf_size];
int n = vsnprintf(buf, buf_size, va_(fmt), ap);
// old vsnprintf's return -1 if string is truncated new ones return
// total number of characters that would have been written
if (n == -1 || n >= buf_size) {
buf_size *= 2;
delete [] buf;
goto again;
}
va_end(ap);
void *arr[2];
arr[1] = (void*) buf;
if (XARequest("PRTF", 2, arr, 0)) return;
printf("%s\n", buf);
fflush(stdout);
delete [] buf;
}
//______________________________________________________________________________
void TThread::ErrorHandler(int level, const char *location, const char *fmt,
va_list ap) const
{
// Thread specific error handler function.
// It calls the user set error handler in the main thread.
Int_t buf_size = 2048;
char *buf, *bp;
again:
buf = new char[buf_size];
int n = vsnprintf(buf, buf_size, fmt, ap);
// old vsnprintf's return -1 if string is truncated new ones return
// total number of characters that would have been written
if (n == -1 || n >= buf_size) {
buf_size *= 2;
delete [] buf;
goto again;
}
if (level >= kSysError && level < kFatal) {
char *buf1 = new char[buf_size + strlen(gSystem->GetError()) + 5];
sprintf(buf1, "%s (%s)", buf, gSystem->GetError());
bp = buf1;
delete [] buf;
} else
bp = buf;
void *arr[4];
arr[1] = (void*) Long_t(level);
arr[2] = (void*) location;
arr[3] = (void*) bp;
if (XARequest("ERRO", 4, arr, 0)) return;
if (level != kFatal)
::GetErrorHandler()(level, level >= gErrorAbortLevel, location, bp);
else
::GetErrorHandler()(level, kTRUE, location, bp);
delete [] bp;
}
//______________________________________________________________________________
void TThread::DoError(int level, const char *location, const char *fmt,
va_list va) const
{
// Interface to ErrorHandler. User has to specify the class name as
// part of the location, just like for the global Info(), Warning() and
// Error() functions.
char *loc = 0;
if (location) {
loc = new char[strlen(location) + strlen(GetName()) + 32];
sprintf(loc, "%s %s:0x%lx", location, GetName(), fId);
} else {
loc = new char[strlen(GetName()) + 32];
sprintf(loc, "%s:0x%lx", GetName(), fId);
}
ErrorHandler(level, loc, fmt, va);
delete [] loc;
}
//______________________________________________________________________________
Int_t TThread::XARequest(const char *xact, Int_t nb, void **ar, Int_t *iret)
{
// Static method used to allow commands to be executed by the main thread.
if (!gApplication || !gApplication->IsRunning()) return 0;
// The first time, create the related static vars
if (!fgXActMutex) {
fgXActMutex = new TMutex(kTRUE);
new TThreadTimer;
fgXActCondi = new TCondition;
}
TThread *th = Self();
if (th && th->fId != fgMainId) { // we are in the thread
TConditionImp *condimp = fgXActCondi->fConditionImp;
TMutexImp *condmutex = fgXActCondi->GetMutex()->fMutexImp;
th->SetComment("XARequest: XActMutex Locking");
fgXActMutex->Lock();
th->SetComment("XARequest: XActMutex Locked");
// Lock now, so the XAction signal will wait
// and never come before the wait
condmutex->Lock();
fgXAnb = nb;
fgXArr = ar;
fgXArt = 0;
fgXAct = (char*) xact;
th->SetComment(fgXAct);
if (condimp) condimp->Wait();
condmutex->UnLock();
if (iret) *iret = fgXArt;
fgXActMutex->UnLock();
th->SetComment();
return 1997;
} else //we are in the main thread
return 0;
}
//______________________________________________________________________________
void TThread::XAction()
{
// Static method called via the thread timer to execute in the main
// thread certain commands. This to avoid sophisticated locking and
// possible deadlocking.
TConditionImp *condimp = fgXActCondi->fConditionImp;
TMutexImp *condmutex = fgXActCondi->GetMutex()->fMutexImp;
condmutex->Lock();
char const acts[] = "PRTF CUPD CANV CDEL PDCD METH ERRO";
enum { kPRTF = 0, kCUPD = 5, kCANV = 10, kCDEL = 15,
kPDCD = 20, kMETH = 25, kERRO = 30 };
int iact = strstr(acts, fgXAct) - acts;
char *cmd = 0;
switch (iact) {
case kPRTF:
printf("%s\n", (const char*)fgXArr[1]);
fflush(stdout);
break;
case kERRO:
{
int level = (int)Long_t(fgXArr[1]);
const char *location = (const char*)fgXArr[2];
char *mess = (char*)fgXArr[3];
if (level != kFatal)
GetErrorHandler()(level, level >= gErrorAbortLevel, location, mess);
else
GetErrorHandler()(level, kTRUE, location, mess);
delete [] mess;
}
break;
case kCUPD:
//((TCanvas *)fgXArr[1])->Update();
cmd = Form("((TCanvas *)0x%lx)->Update();",(Long_t)fgXArr[1]);
gROOT->ProcessLine(cmd);
break;
case kCANV:
switch(fgXAnb) { // Over TCanvas constructors
case 2:
//((TCanvas*)fgXArr[1])->Constructor();
cmd = Form("((TCanvas *)0x%lx)->Constructor();",(Long_t)fgXArr[1]);
gROOT->ProcessLine(cmd);
break;
case 5:
//((TCanvas*)fgXArr[1])->Constructor(
// (Text_t*)fgXArr[2],
// (Text_t*)fgXArr[3],
// *((Int_t*)(fgXArr[4])));
cmd = Form("((TCanvas *)0x%lx)->Constructor((Text_t*)0x%lx,(Text_t*)0x%lx,*((Int_t*)(0x%lx)));",(Long_t)fgXArr[1],(Long_t)fgXArr[2],(Long_t)fgXArr[3],(Long_t)fgXArr[4]);
gROOT->ProcessLine(cmd);
break;
case 6:
//((TCanvas*)fgXArr[1])->Constructor(
// (char*)fgXArr[2],
// (char*)fgXArr[3],
// *((Int_t*)(fgXArr[4])),
// *((Int_t*)(fgXArr[5])));
cmd = Form("((TCanvas *)0x%lx)->Constructor((Text_t*)0x%lx,(Text_t*)0x%lx,*((Int_t*)(0x%lx)),*((Int_t*)(0x%lx)));",(Long_t)fgXArr[1],(Long_t)fgXArr[2],(Long_t)fgXArr[3],(Long_t)fgXArr[4],(Long_t)fgXArr[5]);
gROOT->ProcessLine(cmd);
break;
case 8:
//((TCanvas*)fgXArr[1])->Constructor(
// (char*)fgXArr[2],
// (char*)fgXArr[3],
// *((Int_t*)(fgXArr[4])),
// *((Int_t*)(fgXArr[5])),
// *((Int_t*)(fgXArr[6])),
// *((Int_t*)(fgXArr[7])));
cmd = Form("((TCanvas *)0x%lx)->Constructor((Text_t*)0x%lx,(Text_t*)0x%lx,*((Int_t*)(0x%lx)),*((Int_t*)(0x%lx)),*((Int_t*)(0x%lx)),*((Int_t*)(0x%lx)));",(Long_t)fgXArr[1],(Long_t)fgXArr[2],(Long_t)fgXArr[3],(Long_t)fgXArr[4],(Long_t)fgXArr[5],(Long_t)fgXArr[6],(Long_t)fgXArr[7]);
gROOT->ProcessLine(cmd);
break;
}
break;
case kCDEL:
//((TCanvas*)fgXArr[1])->Destructor();
cmd = Form("((TCanvas *)0x%lx)->Destructor();",(Long_t)fgXArr[1]);
gROOT->ProcessLine(cmd);
break;
case kPDCD:
((TVirtualPad*) fgXArr[1])->Divide( *((Int_t*)(fgXArr[2])),
*((Int_t*)(fgXArr[3])),
*((Float_t*)(fgXArr[4])),
*((Float_t*)(fgXArr[5])),
*((Int_t*)(fgXArr[6])));
break;
case kMETH:
((TMethodCall *) fgXArr[1])->Execute((void*)(fgXArr[2]),(const char*)(fgXArr[3]));
break;
default:
::Error("TThread::XAction", "wrong case");
}
fgXAct = 0;
if (condimp) condimp->Signal();
condmutex->UnLock();
}
//////////////////////////////////////////////////////////////////////////
// //
// TThreadTimer //
// //
//////////////////////////////////////////////////////////////////////////
//______________________________________________________________________________
TThreadTimer::TThreadTimer(Long_t ms) : TTimer(ms, kTRUE)
{
// Create thread timer.
gSystem->AddTimer(this);
}
//______________________________________________________________________________
Bool_t TThreadTimer::Notify()
{
// Periodically execute the TThread::XAxtion() method in the main thread.
R__LOCKGUARD2(gROOTMutex);
if (TThread::fgXAct) { TThread::XAction(); }
Reset();
return kFALSE;
}
//////////////////////////////////////////////////////////////////////////
// //
// TThreadCleaner //
// //
//////////////////////////////////////////////////////////////////////////
//______________________________________________________________________________
TThreadCleaner::~TThreadCleaner()
{
// Call user clean up routines.
TThread::CleanUp();
}