#include <QThread.h>
Inheritance diagram for FX::QThread:
QThread is designed to be one of the most comprehensive C++ thread classes around. Some of its many features include:
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.
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.
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.
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.
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 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.
You may notice an uncanny similarity with QThread in Qt. This is because QThread was originally a drop-in superset replacement for QThread.
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 |
QThread * | creator () 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::BoundFunctorV * | addCleanupCall (FXAutoPtr< Generic::BoundFunctorV > handler, bool inThread=false) |
bool | removeCleanupCall (Generic::BoundFunctorV *handler) |
Static Public Member Functions | |
static FXulong | id () throw () |
static QThread * | current () |
static QThread * | primaryThread () 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 |