FX::QThread Class Reference

#include <QThread.h>

Inheritance diagram for FX::QThread:

Inheritance graph
[legend]

List of all members.


Detailed Description

The base class for all threads in FOX (Qt compatible).

QThread is designed to be one of the most comprehensive C++ thread classes around. Some of its many features include:

Writing easily cancellable thread code

On POSIX Unix, operations which waits on the kernel except for mutexes can never return and instead jump directly to the cleanup() routine (precisely which calls are subject to this vary substantially between Unices but it's best to assume that all wait operations are vulnerable - this includes printing things to stdio like using fxmessage). Because of this, stack-constructed objects will not be unwound and hence there will be memory and resource leaks.

To solve this, place all data used by your thread during its execution within its private member variables. Use a system of checking pointers for zero to determine if they need to be delete'd in your cleanup(). cleanup() is guaranteed to be called irrespective of how the thread ends - naturally, or by request. Do not hold any mutexs as they will remain locked by a dead thread (you shouldn't hold them anyway as it causes deadlocks).

Obviously one thing you cannot protect against is calling other code which does create objects on the stack and waits on the kernel. For these situations alone you should use disableTermination() and enableTermination() around the appropriate calls. These two functions can be nested ie; you must call enableTermination() as many times as disableTermination() for termination to be actually reenabled. See FX::QThread_DTHold.

My suggestion is to try and keep as much code cancellable as possible. Doing this pays off when your thread is asked to quit and the user isn't left with a thread that won't quit for five or ten seconds. While it may seem annoying, you quickly find yourself automatically designing your code to allow it.

If your thread is too complex to allow arbitrary cancellation like above or you find yourself constantly disabling termination, firstly consider a redesign. If you can't do that, the traditional OS/2 or Win32 approach of creating a flag (consider FXAtomicInt as a thread-safe flag) that the thread polls can work well.

Note:
On Win32 there is no such thing as cleanup handlers, so QThread emulates them along with special support by various other classes such as QWaitCondition, QPipe and QBlkSocket. Some FOX file i/o functions do not have this special support, so it is strongly recommended that for identical cross-platform behaviour you regularly call checkForTerminate() which on Win32 is implemented rather brutally by calling the cleanup handler and immediately exiting the thread.

On Apple MacOS X, curiously the select() call does not permit thread cancellation despite being required to do so by the POSIX threads spec. Hence special support code is compiled in on MacOS X to emulate a similar behaviour. This comes with a cost of an extra two file handles open per thread object.

Warning:
Cleanup handlers are invoked on most POSIX platforms via a synchronous signal. This implies that all the FXRESTRICTions placed on signal handling code also apply to cleanup code - the most annoying is that QWaitCondition is totally unusable.

Self-destruction:

In some circumstances, you may wish to spin off self-managed thread objects which perform their function before self-destructing when they determine they are no longer necessary. You can not and must not use delete this because during destruction the cleanup handler is invoked by POSIX threads thus reentering object destruction and crashing the program.

Instead, use setAutoDelete() which has QThread safely destroy the object after POSIX threads has been moved out of the way. Any attempts to delete this should be trapped as a fatal exception.

Stack sizes:

Due to some POSIX architectures having ludicrously small default thread stack sizes (eg; FreeBSD with 64Kb), as of v0.75 TnFOX enforces a default of 512Kb. This may be too little for your requirements but similarly, a stack of 1Mb can lead to premature virtual address space exhaustion on 32 bit architectures. Furthermore some systems treat custom stack sizes specially, causing degraded performance so you can still specify zero to have the system default. Lastly, note than on Win32 the PE binary's stack size setting is the smallest size possible - any value specified here smaller than that will be rounded up.

Thread location:

On some systems you can choose which scheduler runs your thread, the kernel or the process. If the kernel, it usually means the thread can run on different processors whereas if the process, it stays local but with lower overheads. Even if you specify one or the other, it may be ignored according to what the host OS supports.

In general if your thread doesn't do much heavy processing and spends a lot of time waiting on other threads, it should be in-process so the threading implementation can avoid expensive calls into the kernel.

Signals (POSIX only):

Signals are a real hash on POSIX threads. Each thread maintains its own signal mask which determines what signals it can receive. Handlers are installed on a per-process basis and which thread installed the handler makes no difference. Therefore if two threads are running two pipes and a SIGPIPE is generated, how can the handler know which pipe it refers to?

TnFOX code makes the assumption that a signal is delivered to the thread which caused it unless it's a process-wide signal like SIGTERM. This is not at all guaranteed and so things will break on some POSIX platforms - however the above situation is unresolvable without this behaviour, so I'm hoping that most POSIX implementations will do as above (Linux and FreeBSD do). Certainly for SIGPIPE itself it's logical that most implementations of write() raise the signal there & then so QPipe's assumption will be correct.

One major problem is if you are modifying data which a signal handler also modifies. If the signal is raised right in the middle of your changes then the handler will also get the lock (as it recurses) and thus corrupt your data. To prevent this instantiate a FX::QThread_DisableSignals around the critical code.

FXProcess sets up most of the fatal signals to call an internal handler which performs the fatal exit upcall before printing some info to stderr and exiting. This handler has been designed to be called in the context of any thread at all.

Qt

You may notice an uncanny similarity with QThread in Qt. This is because QThread was originally a drop-in superset replacement for QThread.

See also:
QThread_DTHold

Public Types

enum  ThreadScheduler { Auto, InProcess, InKernel }
typedef Generic::Functor
< Generic::TL::create< void,
QThread * >::value > 
CreationUpcallSpec

Public Member Functions

 QThread (const char *name=0, bool autodelete=false, FXuval stacksize=524288, ThreadScheduler schedloc=Auto)
virtual ~QThread ()
const char * name () const throw ()
FXuval stackSize () const
void setStackSize (FXuval newsize)
ThreadScheduler threadLocation () const
void setThreadLocation (ThreadScheduler threadloc)
bool wait (FXuint time=FXINFINITE)
void start (bool waitTillStarted=false)
bool finished () const throw ()
bool running () const throw ()
bool inCleanup () const throw ()
bool isValid () const throw ()
bool setAutoDelete (bool doso) throw ()
void requestTermination ()
FXulong myId () const
QThreadcreator () const
signed char priority () const
void setPriority (signed char pri)
FXulong processorAffinity () const
void setProcessorAffinity (FXulong mask=(FXulong)-1, bool recursive=false)
virtual void selfDestruct ()
void * result () const throw ()
void disableTermination ()
bool checkForTerminate ()
void enableTermination ()
Generic::BoundFunctorVaddCleanupCall (FXAutoPtr< Generic::BoundFunctorV > handler, bool inThread=false)
bool removeCleanupCall (Generic::BoundFunctorV *handler)

Static Public Member Functions

static FXulong id () throw ()
static QThreadcurrent ()
static QThreadprimaryThread () throw ()
static void sleep (FXuint secs)
static void msleep (FXuint millisecs)
static void yield ()
static void exit (void *retcode)
static void addCreationUpcall (CreationUpcallSpec upcallv, bool inThread=false)
static bool removeCreationUpcall (CreationUpcallSpec upcallv)
static FXDLLLOCAL void * int_cancelWaiterHandle ()

Protected Member Functions

virtual void run ()=0
virtual void * cleanup ()=0

Friends

class QThreadPrivate
class FXPrimaryThread
class QThread_DisableSignals

Member Typedef Documentation

The specification type of a thread creation upcall vector.


Member Enumeration Documentation

Specifies where this thread shall run.

Enumerator:
InProcess  Place wherever the system feels is best.
InKernel  Try to run in-process.

Try to run in-kernel


Constructor & Destructor Documentation

FX::QThread::QThread ( const char *  name = 0,
bool  autodelete = false,
FXuval  stacksize = 524288,
ThreadScheduler  schedloc = Auto 
)

Constructs a thread object named _name. The thread is constructed in the calling thread's context and does not actually run the new thread until you call start()

virtual FX::QThread::~QThread (  )  [virtual]

Destructs the thread. Causes an exception if the thread is still running.


Member Function Documentation

const char* FX::QThread::name (  )  const throw () [inline]

Returns the name of the thread.

FXuval FX::QThread::stackSize (  )  const

Returns the stack size that the thread is given.

void FX::QThread::setStackSize ( FXuval  newsize  ) 

Sets the stack size that the thread shall use (set before calling start()) which must be a multiple of FXProcess::pageSize(). Setting zero causes system default to be used (usually 1 or 2Mb).

Note:
Most systems cannot automatically extend their stack when it runs out. This I personally find amazing given Acorn RISC-OS could do it happily fifteen years ago but I'd imagine on 64 bit memory architectures it won't matter again for another fifteen years or so.

ThreadScheduler FX::QThread::threadLocation (  )  const

Returns where this thread should run.

void FX::QThread::setThreadLocation ( ThreadScheduler  threadloc  ) 

Sets where this thread should run (set before starting).

bool FX::QThread::wait ( FXuint  time = FXINFINITE  ) 

Parameters:
time Time in milliseconds to wait. Use FXINFINITE to wait forever
Returns:
True if thread finished in time alloted, false if wait timed out
Waits for the thread to finish. A thread waiting on itself will never succeed.
Warning:
On POSIX Unix, when specified with something other than FXINFINITE, if the thread died in a way that didn't permit QThread to be notified then this may wait forever

void FX::QThread::start ( bool  waitTillStarted = false  ) 

Parameters:
waitTillStarted True if you want the call to wait until the thread is running before returning
Call this to start the separate parallel execution of this thread object from its run(). Remember never to access any data in the QThread object after calling this method without first synchronising access. Our suggestion is to also subclass QMutex so then you can simply use lock() and unlock() (preferably via QMtxHold) around every access.

bool FX::QThread::finished (  )  const throw () [inline]

Returns true if the thread has run and finished.

bool FX::QThread::running (  )  const throw () [inline]

Returns true if the thread is running right now.

bool FX::QThread::inCleanup (  )  const throw () [inline]

Returns true if the thread is in the process of finishing (in its cleanup handler).

bool FX::QThread::isValid (  )  const throw ()

Returns true if this object is valid (ie; not deleted).

bool FX::QThread::setAutoDelete ( bool  doso  )  throw ()

Sets if this thread object deletes itself when the thread terminates. Returns the former setting

Warning:
You must thus allocate the thread object using new

void FX::QThread::requestTermination (  ) 

Any other thread may call this to request that this thread object finish at the earliest possible moment. This may be extremely inconvenient because under Unix, any system call which involves a wait may jump directly to the cleanup routine. On Win32, this must be emulated using regular polls to checkForTerminate() which all TnFOX classes involving waits do for you. Note that this call does not wait for the thread to terminate, this can be done using wait()

See also:
Detailed Description above

static FXulong FX::QThread::id (  )  throw () [static]

Returns a unique number identifying this thread within this kernel.

Referenced by FX::QMutex::tryLock().

FXulong FX::QThread::myId (  )  const

Returns a unique number identifying the thread represented by this instance.

static QThread* FX::QThread::current (  )  [static]

Returns the currently executing QThread object. Note that the primary thread (ie; the one main() was called by first thing) correctly returns a pointer to an internal thread object.

Warning:
All QThread usage is non-trivial and thus should not be used during static data initialisation and destruction. I've put in an assert() to help you catch these in debug mode.

static QThread* FX::QThread::primaryThread (  )  throw () [static]

Returns the primary thread object in the process.

QThread* FX::QThread::creator (  )  const

Returns the QThread which created this thread. Is zero if a non-QThread thread created this thread.

Warning:
The pointer returned by this function will be invalid if the creating thread has since been deleted. Use isValid() to determine any returned pointer's validity.

signed char FX::QThread::priority (  )  const

Returns the scheduling priority of the thread where -127 is lowest priority and 128 is maximum. 0 always equals system default ie; normal.

See also:
setPriority()

void FX::QThread::setPriority ( signed char  pri  ) 

Sets the scheduling priority of the thread which can be from -127 to +128. Setting 0 always sets system default. /note priority() may not return the value you set if the granularity available on the particular system isn't sufficient - and on some systems there are as little as three priorities

FXulong FX::QThread::processorAffinity (  )  const

Returns the processor affinity of the thread.

void FX::QThread::setProcessorAffinity ( FXulong  mask = (FXulong)-1,
bool  recursive = false 
)

Sets the processor affinity of the thread with recursive meaning to set the same for all threads this thread starts. (FXulong)-1 means all processors.

static void FX::QThread::sleep ( FXuint  secs  )  [static]

Makes the current thread sleep for secs seconds.

static void FX::QThread::msleep ( FXuint  millisecs  )  [static]

Makes the current thread sleep for millisecs milliseconds.

static void FX::QThread::yield (  )  [static]

Yields the remainder of the current thread's time slice.

Referenced by FX::QShrdMemMutex::lock().

virtual void FX::QThread::selfDestruct (  )  [inline, virtual]

Called at the end when you have enabled self destruction on thread exit using setAutoDelete(true). Default implementation simply does delete this.

static void FX::QThread::exit ( void *  retcode  )  [static]

Used by a thread to exit early with return code retcode.

void* FX::QThread::result (  )  const throw ()

Returns the exit code of the thread (if possible eg; self-destructing threads can't).

void FX::QThread::disableTermination (  ) 

Callable from any thread, this disables termination of the thread if the reference count is above zero. Use enableTermination() to decrease the reference count.

See also:
Detailed Description above

bool FX::QThread::checkForTerminate (  ) 

Checks if termination has been requested - if so, never returns. You may need to call this manually at places to ensure Win32 behaves like POSIX. If thread cancellation has been disabled, returns true instead.

void FX::QThread::enableTermination (  ) 

Callable from any thread, this enables termination of the thread if the reference count is reaches zero. Use disableTermination() to increase the reference count.

See also:
Detailed Description above

static void FX::QThread::addCreationUpcall ( CreationUpcallSpec  upcallv,
bool  inThread = false 
) [static]

Registers code to be called when a thread is created. The form of the code is:

    void function(QThread *);
    void Object::member(QThread *);
The most common is a member function as this can be in some arbitrary object instance, thus making finding your data much easier. If inThread is true, the upcall is made just after setting up the new thread and just before waking the thread who called start() if it requested to wait. If it's false, the upcall is made just after the thread has been successfully started.

static bool FX::QThread::removeCreationUpcall ( CreationUpcallSpec  upcallv  )  [static]

Unregisters a previously installed call upon thread creation.

Generic::BoundFunctorV* FX::QThread::addCleanupCall ( FXAutoPtr< Generic::BoundFunctorV handler,
bool  inThread = false 
)

Registers a cleanup handler to be called after the thread's cleanup() is called. The thread takes ownership of the handler pointer. The inThread parameter when true means that the handler is called in the context of the thread just before termination - note that if it's a self-deleting thread, that means just before deletion. When false, the handler is called when the thread object itself is being deleted (again, be cautious of self-deleting threads as these delete themselves before terminating).

bool FX::QThread::removeCleanupCall ( Generic::BoundFunctorV handler  ) 

Unregisters a previously installed cleanup handler, returning false if not found. You get back ownership of the handler pointer

virtual void FX::QThread::run (  )  [protected, pure virtual]

Reimplement this in your subclass with the code you wish to run in parallel.

Implemented in FX::FXIPCChannel, and FX::TnFXAppEventLoop.

virtual void* FX::QThread::cleanup (  )  [protected, pure virtual]

Returns:
The return code you wish for the thread's exit Reimplement this in your subclass with the code you wish to run when the thread dies either naturally or because of a request by requestTermination()

Implemented in FX::FXIPCChannel, and FX::TnFXAppEventLoop.


The documentation for this class was generated from the following file:

(C) 2002-2008 Niall Douglas. Some parts (C) to assorted authors.
Generated on Fri Jun 13 22:28:58 2008 for TnFOX by doxygen v1.5.6