How to Use the ROOT
TThread Class
Jörn Adamczewski,
Marc Hemberger
Mar 3, 2000
updated September 26, 2001 (J.A.)
Contents
-
Summary
1.1 Disclaimer
1.2 Problems
-
Installation
-
Threads related ROOT Classes
-
TThread for pedestrians
4.1 TThread user function
-
TThread in more detail
5.1 CINT
5.2 Asynchronous Actions
5.3 Synchronous Actions: TCondition
5.4 Xlib connections
5.5 Canceling a TThread
5.6 Finishing thread
-
Advanced: TThreads with classes
6.1 How to launch a class memberfunction
as TThread
-
Thanks and Credits
-
List of Example files
8.1 Example mhs3
8.2 Example conditions
8.3 Example TMhs3
Thread Usage
Glossary
For the PostScript-text, click here.
1 Summary
1.1 Disclaimer
The ROOT framework is not thread-safe in general. There are some known
problems the core developer team of ROOT is aware of and is working on.
For this reason, it should clearly be stated, that TThread is
by no means a solution for everything. Be aware of possible problems, restrict
your threads to distinct and `simple' duties, and you will benefit from
their use.
The TThread-class can be used on all platforms which provide
a POSIX compliant thread implementation. On Linux Xavier Leroy's LinuxThreads
implementation is widely used, but the TThread implementation
should be usable on all platforms which provide pthreads.
1.2 Problems
-
Linux Xlib on SMP machines is not threadsafe yet, which may cause
a crash during threaded graphics operations; this problem is independent
of ROOT.
-
Object instantiation: no implicit locking mechanism for memory allocation
and global root lists, the user has to take care for this on using threads.
2 Installation
For the time being (i.e. up to ROOT 2.23/xx), it is necessary to compile
a threaded version of ROOT to achieve some very special treatment of the
canvas operations. This will not be necessary later on anymore.
To compile ROOT, just do:
-
./configure linuxdeb2 --with-thread=/usr/lib/libpthread.so
This sets up the environment to use /usr/lib/libpthread.so as the
Pthread library, and to switch on a define R__THREAD. This enables to have
the thread specific treatment of gPad, and to get the $ROOTSYS/lib/libThread.so.
Note: Parameter linuxdeb2 has to be replaced with the appropriate
ROOT keyword for your linux distribution.
-
gmake depend
-
gmake
This compiles ROOT as usual.
3 Threads related ROOT Classes
-
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 (i. e. TPosixThread).
-
TMutex
-
This class implements mutex locks. A mutex is a mutual exclusive lock.
The actual work is done via the TMutexImp class (i. e. TPosixMutex)
-
TCondition
-
This class implements a condition variable. Use a condition variable to
signal threads. The actual work is done via the TConditionImp
class (i. e. TPosixCondition).
-
TSemaphore
-
This class implements a counting semaphore. Use a semaphore to synchronize
threads. The actual work is done via the TMutexImp and TConditionImp
classes.
4 TThread for pedestrians
4.1 TThread user function
To run a thread within ROOT, you have to follow these steps:
-
Initialization
-
Add these lines to your rootlogon.C:
{
printf("\nWelcome to the ROOT session\n\n");
gSystem->Load("/usr/lib/libpthread.so");
gSystem->Load("$ROOTSYS/lib/libThread.so");
}
This loads the library with the TThread class and the Pthread specific
implementation file for Posix threads.
-
Define
-
a function (e.g. void* UserFun(void* UserArgs)) that should run
as a thread. See the example mhs3.cxx
and mhs3.h or CalcPiThread.cxx
and CalcPiThread.h. From
these functions we create with the Makefile.mhs3
or Makefile.CalcPiThread
a shared library to be loaded into the interactive ROOT session. How to
go on with member functions will be explained in section 6.
-
Start
-
your interactive ROOT session
-
Load
-
the shared library with gSystem®Load("mhs3.so");
or
-
gSystem®Load("CalcPiThread.so");
-
-
Create
-
the thread instance (see also example RunMhs3.C
or RunPi.C) by
TThread *th = new TThread(UserFun,UserArgs);
You can also give the thread a self defined name, then it might be called
via
TThread *th = new TThread("MyThread", UserFun, UserArgs);
This method is appropriate to use threads in compiled code if you want
to access them by name. Normally, the function name ``UserFun'' will be
the name of the thread (this does not work if TThread constructor
is called from compiled code).
Arguments to the thread function can be handed over via UserArgs-pointer.
When you want to start a method of a class as a thread, you have to give
the pointer to the class instance as UserArgs (see examples in
section 6).
-
Run thread
-
by th®Run();
-
Control
-
TThread::Ps(); - like UNIX ps command;
With the mhs3-example (example 8.1), you
should be able to see a canvas with two pads on it. Both pads keep histograms
updated and filled by three different threads.
With the CalcPi-example (example 8.4),
you should be able to see two threads calculatng Pi with the given number
of intervals as precision.
5 TThread in more detail
5.1 CINT
CINT is not thread safe yet. CINT execution will block the execution of
the threads untill CINT has finished.
5.2 Asynchronous Actions
Different threads can work simultaneously with the same object. Some actions
can be dangerous. For example, when two threads create a histogram object,
ROOT allocates memory and puts them to the same collection. If it happened
to be at the same time, some clashes are possible. To avoid this, the user
has to synchronize these actions:
TThread::Lock() // Locking the following part of code
... // Create an object, etc...
TThread::UnLock() // Unlocking
The part of code between Lock() and UnLock() will be
performed separately. No other thread can perform actions or access objects/collections
at the same time. The TThread::Lock() and TThread::UnLock()
methods internally use one global TMutex instance for locking.
The user may also define his own TMutex MyMutex instance and may
protect his asynchronous actions by calling MyMutex.Lock() and
MyMutex.UnLock().
5.3 Synchronous Actions: TCondition
To synchronize the actions of different threads the TCondition
class can be applied which provides a signaling mechanism.
The TCondition instance must be accessible by all threads which
want to use it, i.e. it should be a global object (or a member of the class
which owns threaded methods, see section 6). To
create a TCondition object, a
TMutex instance for locking
of Wait and TimedWait methods is required. One can pass
the address of an external mutex to TCondition constructor:
TMutex MyMutex;
TCondition MyCondition(&MyMutex);
If NULL is passed, TCondition creates and uses its own internal
mutex:
TCondition MyCondition(NULL);
From then on it is possible to use the following methods of synchronization:
-
TCondition::Wait() waits until any thread sends a signal of the
same condition instance: MyCondition.Wait() reacts on MyCondition.Signal()
or MyCondition.Broadcast(); MyOtherCondition.Signal()
has no effect.
-
If several threads wait for the signal of the same TCondition MyCondition,
at MyCondition.Signal() only one thread will react; to activate
a further thread another MyCondition.Signal() is required, etc.
-
If several threads wait for the signal of the same TCondition MyCondition,
at MyCondition.Broadcast() all threads waiting for MyCondition
are activated at once. Remark: In some tests of MyCondition using
an internal mutex, Broadcast() activated only one thread (probably
depending whether MyCondition had been signalled before).
-
MyCondition.TimedWait(secs,nanosecs) waits for MyCondition
until the absolute time in seconds and nanoseconds since beginning
of the epoch (January, 1st,1970) is reached; to use relative timeouts ``delta'',
it is required to calculate the absolute time at the beginning of waiting
``now''; a possible coding could look like this:
Ulong_t now,then,delta; // seconds
TDatime myTime; // root daytime class
myTime.Set(); // myTime set to ``now''
now=myTime.Convert(); // to seconds since 1970
then=now+delta; // absolute timeout
wait=MyCondition.TimedWait(then,0); // waiting
Return value wait of MyCondition.TimedWait should be
0, if
MyCondition.Signal() was received, and should be nonzero,
if timeout was reached.
The conditions example (example 8.2) shows how
three threaded functions are synchronized using TCondition: ROOT
macro condstart.C starts the
threads which are defined in a shared library (conditions.cxx,
conditions.h).
5.4 Xlib connections
Usually Xlib is not thread safe. This means that calls to the X could fail,
when it receives X-messages from different threads. At first this depends
very crucially on the Xlib-version you have installed on your system. The
only thing we can do here within ROOT is calling a special function XInitThreads()
(which is part of the Xlib), which should (!) prepare the Xlib for the
usage with threads.
To avoid further problems within ROOT some redefinition of the gPad
pointer was done (that's the main reason for the recompilation). When a
thread creates a TCanvas, actually this object is created in the
main thread, and this is hidden from the user. Actions on the canvas are
controlled via a function which returns a pointer to either thread specific
data (TSD) or the main pointer. This mechanism works currently only for
gPad and will also be implemented for other global Objects as
e. g. gVirtualX, gDirectory, gFile.
5.5 Canceling a TThread
Canceling of a thread is a rather dangerous action. In TThread
canceling is forbidden by default. But the user can change this default
by calling TThread::SetCancelOn(). There are two cancellation
modes:
-
Deferred
-
- Set by TThread::SetCancelDeferred() (default): When the user
knows safe places in his code where a thread can be canceled without interference
with the rest of the system, he can define these points by invoking TThread::CancelPoint().
Then, if a thread is canceled, the cancelation is deferred up to the call
of TThread::CancelPoint() and then the thread is canceled safely.
There are some default cancel points for Pthreads implementation, e.g.
any call of
TCondition::Wait(), TCondition::TimedWait(), TThread::Join().
-
Asynchronous
-
- Set by TThread::SetCancelAsynchronous(): If the user is sure
that his application is cancel safe, he could call:
TThread::SetCancelAsynchronous();
TThread::SetCancelOn();
// Now cancelation in any point is allowed.
...
...
// return to default
TThread::SetCancelOff();
TThread::SetCancelDeferred();
To cancel a thread TThread* th the user can call:
-
th®Kill();
-
thread th is canceled
-
TThread::Kill(name);
-
cancel by thread name;
-
TThread::Kill(tid);
-
cancel by thread Id
-
th®Delete();
-
cancel thread and delete th when cancel finished.
Deleting of the thread instance by the operator delete is dangerous.
Use th®Delete instead. C++ delete
is safe only if thread is not running.
Often during the canceling some clean up actions must be made. To define
clean up functions a user can call:
void UserCleanUp(void *arg){
// here the user cleanup is done
...
}
TThread::CleanUpPush(&UserCleanUp,arg); // push user function
// into cleanup stack
// ``last in, first out''
TThread::CleanUpPop(1); // pop user function out of stack
// and execute it,
// thread resumes after this call
TThread::CleanUpPop(0); // pop user function out of stack
// _without_ executing it
Note: CleanUpPush and CleanUpPop should be used as corresponding
pairs like brackets; unlike Pthreads cleanup stack (which is not
implemented here), TThread does not force this usage.
5.6 Finishing thread
When a thread returns from a user function the thread is finished. But
it also can be finished by TThread::Exit(). Then, in case of Pthread
``detached'' mode, the thread vanishes completely.
By default, on finishing TThread executes the most recent cleanup
function (CleanUpPop(1) is called automatically once).
6 Advanced: TThreads with classes
6.1 How to launch a class memberfunction
as TThread
Consider a class Myclass with a memberfunction void* Myclass::Thread0((void*
arg) that shall be launched as a thread. To start Thread0
as a TThread, class Myclass may provide a method:
Int_t Myclass::Threadstart(){
if(!mTh){
mTh= new TThread("memberfunction",
(void(*) (void *))&Thread0,
(void*) this);
mTh->Run();
return 0;
}
return 1;
}
Here mTh is a TThread* pointer which is member of Myclass
(should be initialized to 0 in the constructor). TThread constructor
is called as for a plain C function (see section 4.1),
except for some differences:
-
Memberfunction Thread0 requires an explicit cast to (void(*)
(void *)) (this may cause a compiler warning like:
Myclass.cxx:98: warning: converting from `void (Myclass::*)(void
*)' to `void *' ), which is annoying but harmless.
-
Strictly speaking, Thread0 must be a static member function to be called
from a thread. Some compilers, for example gcc version 2.95.2, may not
allow the (void(*) (void*))s cast and just stop if Thread0 is
not static. On the other hand, if Thread0 is static, no compiler warnings
are generated at all. Because the 'this' pointer is passed in 'arg' in
the call to Thread0(void *arg), you have access to the instance of the
class even if Thread0 is static. Using the 'this' pointer, non static members
can still be read and written from Thread0, as long as you have provided
Getter and Setter methods for these members.
For example:
Bool_t state = arg->GetRunStatus();
arg->SetRunStatus(state);
Second, the pointer to the current instance of Myclass, i.e.
(void*) this, has to be passed as first argument of the threaded
function Thread0 (C++ member functions internally expect the this
pointer as first argument to have access to class members of the same instance).
pthreads are made for simple C functions and do not know about Thread0
being a member function of a class. Thus, you have to pass this information
by hand, if you want to access all members of the Myclass
instance from the Thread0 function.
Note: Method Thread0 must not be a virtual memberfunction, since
the cast of Thread0 to void(*) in TThread constructor
may raise problems with C++ virtual function table. However, Thread0
may call another virtual memberfunction virtual void Myclass::Func0()
which then can be overridden in a derived class of Myclass. (see
example 8.3).
Class Myclass may also provide a method to stop the running
thread:
Int_t Myclass::Threadstop(){
if(mTh){
TThread::Delete(mTh);
delete mTh;
mTh=0;
return 0;
}
return 1;
}
Example 8.3: Class TThreadframe
( TThreadframe.h, TThreadframe.cxx)
is a simple example of a framework class managing up to four threaded methods.
Class TMhs3 (TMhs3.h, TMhs3.cxx)
inherits from this base class, showing the mhs3 example 8.1
(mhs3.h, mhs3.cxx)
within a class.
Makefile of example builds shared libraries libTThreadframe.so
and libTMhs3.so. These are either loaded and executed by root macro
TMhs3demo.C, or are linked against
executable TMhs3run.cxx.
7 Thanks and Credits
To:
-
Victor Perevoztchikov
-
-
Fons Rademakers
-
-
Rene Brun
-
for their support in reimplementing and putting threads back to ROOT.
8 List of Example files
8.1 Example mhs3
8.2 Example conditions
8.3 Example TMhs3
8.4 Example CalcPiThread
File translated from TEX by TTH,
version 2.30.
On 3 Mar 2000, 10:54.
Updated examples and chapter 6.1 on 26 September
2001 (J.A.)