FX::QThread Class Reference

#include <QThread.h>

Inheritance diagram for FX::QThread:

FX::FXIPCChannel FX::FXNetworkService FX::TnFXAppEventLoop 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

Definition at line 971 of file QThread.h.

Public Types

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

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


The documentation for this class was generated from the following file:
(C) 2002-2009 Niall Douglas. Some parts (C) to assorted authors.
Generated on Fri Nov 20 18:37:46 2009 for TnFOX by doxygen v1.4.7