#include <QThread.h>
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.
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 |
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 |
typedef Generic::Functor<Generic::TL::create<void, QThread *>::value> FX::QThread::CreationUpcallSpec |
The specification type of a thread creation upcall vector.
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.
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).
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 |
) |
time | Time in milliseconds to wait. Use FXINFINITE to wait forever |
void FX::QThread::start | ( | bool | waitTillStarted = false |
) |
waitTillStarted | True if you want the call to wait until the thread is running before returning |
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
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()
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.
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 |
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.
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] |
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.
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.
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 *);
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] |
Implemented in FX::FXIPCChannel, and FX::TnFXAppEventLoop.