|
|
|
/*
|
|
|
|
This file declares the Weaver, Job and Thread classes.
|
|
|
|
|
|
|
|
$ Author: Mirko Boehm $
|
|
|
|
$ Copyright: (C) 2004, Mirko Boehm $
|
|
|
|
$ Contact: mirko@kde.org
|
|
|
|
http://www.kde.org
|
|
|
|
http://www.hackerbuero.org $
|
|
|
|
$ License: LGPL with the following explicit clarification:
|
|
|
|
This code may be linked against any version of the TQt toolkit
|
|
|
|
from Troll Tech, Norway. $
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef WEAVER_H
|
|
|
|
#define WEAVER_H
|
|
|
|
|
|
|
|
extern "C"
|
|
|
|
{
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
}
|
|
|
|
|
|
|
|
#include <tqobject.h>
|
|
|
|
#include <tqptrlist.h>
|
|
|
|
#include <tqthread.h>
|
|
|
|
#include <tqwaitcondition.h>
|
|
|
|
#include <tqmutex.h>
|
|
|
|
#include <tqevent.h>
|
|
|
|
|
|
|
|
#include <tdemacros.h>
|
|
|
|
|
|
|
|
namespace KPIM {
|
|
|
|
namespace ThreadWeaver {
|
|
|
|
|
|
|
|
/** This method prints a text message on the screen, if debugging is
|
|
|
|
enabled. Otherwise, it does nothing. The message is thread safe,
|
|
|
|
therefore providing that the messages appear in the order they where
|
|
|
|
issued by the different threads.
|
|
|
|
All messages are suppressed when Debug is false. All messages with a
|
|
|
|
lower importance (higher number) than DebugLevel will be suppressed,
|
|
|
|
too. Debug level 0 messages will always be printed as long as
|
|
|
|
Debug is true.
|
|
|
|
We use our own debugging method, since debugging threads is a more
|
|
|
|
complicated experience than debugging single threaded
|
|
|
|
contexts. This might change in future in the way that debug
|
|
|
|
prints it's messages to another logging facility provided by
|
|
|
|
the platform.
|
|
|
|
Use setDebugLevel () to integrate adapt debug () to your platform.
|
|
|
|
*/
|
|
|
|
|
|
|
|
TDE_EXPORT extern bool Debug;
|
|
|
|
TDE_EXPORT extern int DebugLevel;
|
|
|
|
|
|
|
|
TDE_EXPORT inline void setDebugLevel (bool debug, int level)
|
|
|
|
{
|
|
|
|
Debug = debug;
|
|
|
|
DebugLevel = level;
|
|
|
|
}
|
|
|
|
|
|
|
|
TDE_EXPORT inline void debug(int severity, const char * cformat, ...)
|
|
|
|
#ifdef __GNUC__
|
|
|
|
__attribute__ ( (format (printf, 2, 3 ) ) )
|
|
|
|
#endif
|
|
|
|
;
|
|
|
|
|
|
|
|
TDE_EXPORT inline void debug(int severity, const char * cformat, ...)
|
|
|
|
{
|
|
|
|
if ( Debug == true && ( severity<=DebugLevel || severity == 0) )
|
|
|
|
{
|
|
|
|
static TQMutex mutex;
|
|
|
|
TQString text;
|
|
|
|
|
|
|
|
mutex.lock();
|
|
|
|
va_list ap;
|
|
|
|
va_start( ap, cformat );
|
|
|
|
vprintf (cformat, ap);
|
|
|
|
va_end (ap);
|
|
|
|
mutex.unlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class Thread;
|
|
|
|
class Job;
|
|
|
|
|
|
|
|
/** A class to represent the events threads generate and send to the
|
|
|
|
Weaver object. Examples include the start or end of the processing of a
|
|
|
|
job. Threads create the event objects and discard them after posting
|
|
|
|
the event, since the event receiver will assume ownership of the
|
|
|
|
event.
|
|
|
|
Events are associated to the sending thread and possibly to a
|
|
|
|
processed job.
|
|
|
|
|
|
|
|
Note: Do not create and use SPR/APR events, use Job::triggerSPR or
|
|
|
|
Job::triggerAPR to create the requests. */
|
|
|
|
|
|
|
|
class TDE_EXPORT Event : public TQCustomEvent
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum Action {
|
|
|
|
NoAction = 0,
|
|
|
|
Finished, /// All jobs in the queue are done.
|
|
|
|
Suspended, /// Thread queueing halted.
|
|
|
|
ThreadStarted,
|
|
|
|
ThreadExiting,
|
|
|
|
ThreadBusy,
|
|
|
|
ThreadSuspended,
|
|
|
|
JobStarted,
|
|
|
|
JobFinished,
|
|
|
|
JobSPR, /// Synchronous Process Request
|
|
|
|
JobAPR /// Asynchronous Process Request
|
|
|
|
};
|
|
|
|
Event ( Action = NoAction, Thread * = 0, Job *job = 0);
|
|
|
|
/** Return the (custom defined) event type. */
|
|
|
|
static int type ();
|
|
|
|
/** The ID of the sender thread. */
|
|
|
|
Thread* thread () const;
|
|
|
|
/** The associated job. */
|
|
|
|
Job* job () const;
|
|
|
|
/** The action. */
|
|
|
|
Action action () const;
|
|
|
|
private:
|
|
|
|
Action m_action;
|
|
|
|
Thread *m_thread;
|
|
|
|
Job *m_job;
|
|
|
|
static const int Type;
|
|
|
|
};
|
|
|
|
|
|
|
|
/** A Job is a simple abstraction of an action that is to be
|
|
|
|
executed in a thread context.
|
|
|
|
It is essential for the ThreadWeaver library that as a kind of
|
|
|
|
convention, the different creators of Job objects do not touch the
|
|
|
|
protected data members of the Job until somehow notified by the
|
|
|
|
Job. See the SPR signal for an example.
|
|
|
|
|
|
|
|
Jobs may emit process requests as signals. Consider process requests
|
|
|
|
as a kind of synchronized call to the main thread.
|
|
|
|
Process Requests are a generic means for Job derivate programmers to have
|
|
|
|
the jobs interact with the creators (in the main thread) during
|
|
|
|
processing time. To avoid race
|
|
|
|
conditions and extensive locking and unlocking, the thread executing the
|
|
|
|
job is suspended during the period needed to process the request.
|
|
|
|
|
|
|
|
There are two kinds of process requests (we introduce abbreviations,
|
|
|
|
also in the signal names and the code,
|
|
|
|
only to save typing). Both are emitted by signals in the main thread:
|
|
|
|
- Synchronous Process Requests (SPR): Synchronous requests expect that the
|
|
|
|
complete request is performed in the slots connected to the signals. For
|
|
|
|
example, to update a widget according to the progress of the job, a SPR
|
|
|
|
may be used. In such cases, the Job's execution will be resumed
|
|
|
|
immediately after the signal has been processed.
|
|
|
|
- Asynchronous Process Requests (APR): For APRs, the job emitting the
|
|
|
|
signal does not assume anything about the amount of time needed to
|
|
|
|
perform the operation. Therefore, the thread is not waked after the
|
|
|
|
signal returns. The creator has to wake to thread whenever it is
|
|
|
|
ready by calling the wakeAPR method.
|
|
|
|
|
|
|
|
Note: When using an APR, you better make sure to receive the signal
|
|
|
|
with some object, otherwise the calling thread will block forever!
|
|
|
|
*/
|
|
|
|
class TDE_EXPORT Job : public TQObject
|
|
|
|
{
|
|
|
|
TQ_OBJECT
|
|
|
|
|
|
|
|
public:
|
|
|
|
/** Construct a Job object. */
|
|
|
|
Job(TQObject* parent=0, const char* name=0);
|
|
|
|
|
|
|
|
/** Destructor. */
|
|
|
|
virtual ~Job();
|
|
|
|
|
|
|
|
/** Perform the job. The thread in which this job is executed
|
|
|
|
is given as a parameter.
|
|
|
|
Do not overload this method to create your own Job
|
|
|
|
implementation, overload run(). */
|
|
|
|
virtual void execute(Thread*);
|
|
|
|
|
|
|
|
/** Returns true if the jobs's execute method finished. */
|
|
|
|
virtual bool isFinished() const;
|
|
|
|
|
|
|
|
/** Wake the thread after an APR has been processed. */
|
|
|
|
void wakeAPR ();
|
|
|
|
|
|
|
|
/** Process events related to this job (created by the processing
|
|
|
|
thread or the weaver or whoever). */
|
|
|
|
virtual void processEvent ( Event* );
|
|
|
|
|
|
|
|
signals:
|
|
|
|
/** This signal is emitted when a thread starts to process a job. */
|
|
|
|
void started ();
|
|
|
|
/** This signal is emitted when a job has been finished. */
|
|
|
|
void done ();
|
|
|
|
/** This signal is emitted when the job needs some operation done by
|
|
|
|
the main thread (usually the creator of the job).
|
|
|
|
It is important to understand that the emitting thread is
|
|
|
|
suspended until the signal returns.
|
|
|
|
When
|
|
|
|
the operation requested has been performed and this signal is
|
|
|
|
finished, the thread is automatically waked.
|
|
|
|
What operation needs to be performed has to be negotiated between
|
|
|
|
the two objects.
|
|
|
|
Note: This signal is an attempt to provide job programmers with a
|
|
|
|
generic way to interact while the job is executed. I am interested
|
|
|
|
in feedback about it's use. */
|
|
|
|
void SPR ();
|
|
|
|
/** Perform an Asynchronous Process Request. See SPR and the generic
|
|
|
|
Job documentation for a comparison. */
|
|
|
|
void APR ();
|
|
|
|
protected:
|
|
|
|
/** Lock this Job's mutex. */
|
|
|
|
void lock();
|
|
|
|
/** Unlock this Job's mutex. */
|
|
|
|
void unlock();
|
|
|
|
/** The method that actually performs the job. It is called from
|
|
|
|
execute(). This method is the one to overload it with the
|
|
|
|
job's task. */
|
|
|
|
virtual void run () = 0;
|
|
|
|
/** Return the thread that executes this job.
|
|
|
|
Returns zero of the job is not currently executed. */
|
|
|
|
Thread *thread();
|
|
|
|
/** Call with status = true to mark this job as done. */
|
|
|
|
virtual void setFinished(bool status);
|
|
|
|
/** Trigger a SPR.
|
|
|
|
This emits a signal in the main thread indicating the necessity of
|
|
|
|
a synchronized operation. */
|
|
|
|
void triggerSPR ();
|
|
|
|
/** Trigger an APR.
|
|
|
|
This emit a signal in the main thread indicating the necessity of
|
|
|
|
an unsynchronized operation.
|
|
|
|
The calling thread needs to ensure to wake the thread when the
|
|
|
|
operation is done. */
|
|
|
|
void triggerAPR ();
|
|
|
|
|
|
|
|
bool m_finished;
|
|
|
|
|
|
|
|
TQMutex *m_mutex;
|
|
|
|
|
|
|
|
Thread * m_thread;
|
|
|
|
|
|
|
|
TQWaitCondition *m_wc;
|
|
|
|
};
|
|
|
|
|
|
|
|
class Weaver;
|
|
|
|
|
|
|
|
/** The class Thread is used to represent the worker threads in
|
|
|
|
the weaver's inventory. It is not meant to be overloaded. */
|
|
|
|
class TDE_EXPORT Thread : public TQThread
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
/** Create a thread.
|
|
|
|
These thread objects are only used inside the Weaver parent
|
|
|
|
object. */
|
|
|
|
Thread(Weaver *parent);
|
|
|
|
|
|
|
|
/** The destructor. */
|
|
|
|
~Thread();
|
|
|
|
|
|
|
|
/** Overloaded to execute the assigned job.
|
|
|
|
This will NOT return until shutdown() is called. The
|
|
|
|
thread will try to execute one job after the other, asking
|
|
|
|
the Weaver parent for a new job when the assigned one is
|
|
|
|
finished.
|
|
|
|
If no jobs are available, the thread will suspend.
|
|
|
|
After shutdown() is called, the thread will end as soon as
|
|
|
|
the currently assigned job is done.
|
|
|
|
*/
|
|
|
|
void run();
|
|
|
|
|
|
|
|
/* Provide the msleep() method (protected in TQThread) to be
|
|
|
|
available for executed jobs. */
|
|
|
|
void msleep(unsigned long msec);
|
|
|
|
|
|
|
|
/** Returns the thread id.
|
|
|
|
This id marks the respective Thread object, and must
|
|
|
|
therefore not be confused with, e.g., the pthread thread
|
|
|
|
ID. */
|
|
|
|
unsigned int id() const;
|
|
|
|
|
|
|
|
/** Post an event, will be received and processed by the Weaver. */
|
|
|
|
void post (Event::Action, Job* = 0);
|
|
|
|
|
|
|
|
private:
|
|
|
|
Weaver *m_parent;
|
|
|
|
|
|
|
|
const unsigned int m_id;
|
|
|
|
|
|
|
|
static unsigned int sm_Id;
|
|
|
|
|
|
|
|
static unsigned int makeId();
|
|
|
|
};
|
|
|
|
|
|
|
|
/** A weaver is the manager of worker threads (Thread objects) to
|
|
|
|
which it assigns jobs from it's queue. */
|
|
|
|
class TDE_EXPORT Weaver : public TQObject
|
|
|
|
{
|
|
|
|
TQ_OBJECT
|
|
|
|
|
|
|
|
public:
|
|
|
|
Weaver (TQObject* parent=0, const char* name=0,
|
|
|
|
int inventoryMin = 4, // minimal number of provided threads
|
|
|
|
int inventoryMax = 32); // maximum number of provided threads
|
|
|
|
virtual ~Weaver ();
|
|
|
|
/** Add a job to be executed. */
|
|
|
|
virtual void enqueue (Job*);
|
|
|
|
/** Enqueue all jobs in the given list.
|
|
|
|
This is an atomic operation, no jobs will start
|
|
|
|
before all jobs in the list are enqueued.
|
|
|
|
If you need a couple of jobs done and want to receive the
|
|
|
|
finished () signal afterwards, use this method to queue
|
|
|
|
them. Otherwise, when enqueueing your jobs
|
|
|
|
individually, there is a chance that you receive more than
|
|
|
|
one finished signal. */
|
|
|
|
void enqueue (TQPtrList<Job> jobs);
|
|
|
|
/** Remove a job from the queue.
|
|
|
|
If the job qas queued but not started so far, it is simple
|
|
|
|
removed from the queue. For now, it is unsupported to
|
|
|
|
dequeue a job once its execution has started.
|
|
|
|
For that case, you will have to provide a method to interrupt your
|
|
|
|
job's execution (and receive the done signal).
|
|
|
|
Returns true if the job has been dequeued, false if the
|
|
|
|
job has already been started or is not found in the
|
|
|
|
queue. */
|
|
|
|
virtual bool dequeue (Job*);
|
|
|
|
/** Remove all queued jobs.
|
|
|
|
Please note that this will not kill the threads, therefore
|
|
|
|
all jobs that are being processed will be continued. */
|
|
|
|
virtual void dequeue ();
|
|
|
|
/** Get notified when a thread has finished a job.
|
|
|
|
This is done automatically. */
|
|
|
|
// virtual void jobFinished(Thread *);
|
|
|
|
/** Finish all queued operations, then return.
|
|
|
|
This method is used in imperative programs that cannot react on
|
|
|
|
events to have the controlling (main) thread wait wait for the
|
|
|
|
jobs to finish.
|
|
|
|
Warning: This will suspend your thread!
|
|
|
|
Warning: If your jobs enter for example an infinite loop, this
|
|
|
|
will never return! */
|
|
|
|
virtual void finish();
|
|
|
|
/** Suspend job execution if state = true, otherwise resume
|
|
|
|
job execution if it was suspended.
|
|
|
|
When suspending, all threads are allowed to finish the
|
|
|
|
currently assigned job but will not receive a new
|
|
|
|
assignment.
|
|
|
|
When all threads are done processing the assigned job, the
|
|
|
|
signal suspended will() be emitted.
|
|
|
|
If you call suspend (true) and there are no jobs left to
|
|
|
|
be done, you will immidiately receive the suspended()
|
|
|
|
signal. */
|
|
|
|
virtual void suspend (bool state);
|
|
|
|
/** Is the queue empty? */
|
|
|
|
bool isEmpty () const;
|
|
|
|
/** Is the weaver idle?
|
|
|
|
The weaver is idle if no jobs are queued and no jobs are processed
|
|
|
|
by the threads (m_active is zero). */
|
|
|
|
bool isIdle () const;
|
|
|
|
/** Returns the number of pending jobs. */
|
|
|
|
int queueLength ();
|
|
|
|
/** Assign a job to the calling thread.
|
|
|
|
This is supposed to be called from the Thread objects in
|
|
|
|
the inventory.
|
|
|
|
Returns 0 if the weaver is shutting down, telling the
|
|
|
|
calling thread to finish and exit.
|
|
|
|
If no jobs are available and shut down is not in progress,
|
|
|
|
the calling thread is suspended until either condition is
|
|
|
|
met.
|
|
|
|
In previous, threads give the job they have completed. If this is
|
|
|
|
the first job, previous is zero. */
|
|
|
|
virtual Job* applyForWork (Thread *thread, Job *previous);
|
|
|
|
/** Lock the mutex for this weaver. The threads in the
|
|
|
|
inventory need to lock the weaver's mutex to synchronize
|
|
|
|
the job management. */
|
|
|
|
void lock ();
|
|
|
|
/** Unlock. See lock(). */
|
|
|
|
void unlock ();
|
|
|
|
/** Post an event that is handled by this object, but in the main
|
|
|
|
(GUI) thread. Different threads may use this method to communicate
|
|
|
|
with the main thread.
|
|
|
|
thread and job mark the objects associated with this event. */
|
|
|
|
void post (Event::Action, Thread* = 0, Job* = 0);
|
|
|
|
/** Returns the current number of threads in the inventory. */
|
|
|
|
int threads () const;
|
|
|
|
signals:
|
|
|
|
/** This signal is emitted when the Weaver has finished ALL currently
|
|
|
|
queued jobs.
|
|
|
|
If a number of jobs is enqueued sequentially, this signal might be
|
|
|
|
emitted a couple of times (what happens is that all already queued
|
|
|
|
jobs have been processed while you still add new ones). This is
|
|
|
|
not a bug, but the intended behaviour. */
|
|
|
|
void finished ();
|
|
|
|
/** Thread queueing has been suspended.
|
|
|
|
When suspend is called with state = true, all threads are
|
|
|
|
allowed to finish their job. When the last thread
|
|
|
|
finished, this signal is emitted. */
|
|
|
|
void suspended ();
|
|
|
|
/** This signal is emitted when a job is done. It is up to the
|
|
|
|
programmer if this signal or the done signal of the job is more
|
|
|
|
handy. */
|
|
|
|
void jobDone (Job*);
|
|
|
|
// The following signals are used mainly for debugging purposes.
|
|
|
|
void threadCreated (Thread *);
|
|
|
|
void threadDestroyed (Thread *);
|
|
|
|
void threadBusy (Thread *);
|
|
|
|
void threadSuspended (Thread *);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
/** Schedule enqueued jobs to be executed by idle threads.
|
|
|
|
This will try to distribute as many jobs as possible
|
|
|
|
to all idle threads. */
|
|
|
|
void assignJobs();
|
|
|
|
/** Check incoming events for user defined ones. The threads use user
|
|
|
|
defined events to communicate with the Weaver. */
|
|
|
|
bool event ( TQEvent* );
|
|
|
|
/** The thread inventory. */
|
|
|
|
TQPtrList<Thread> m_inventory;
|
|
|
|
/** The job queue. */
|
|
|
|
TQPtrList<Job> m_assignments;
|
|
|
|
/** The number of jobs that are assigned to the worker
|
|
|
|
threads, but not finished. */
|
|
|
|
int m_active;
|
|
|
|
/** Stored setting. */
|
|
|
|
int m_inventoryMin;
|
|
|
|
/** Stored setting . */
|
|
|
|
int m_inventoryMax;
|
|
|
|
/** Wait condition all idle or done threads wait for. */
|
|
|
|
TQWaitCondition m_jobAvailable;
|
|
|
|
/** Wait for a job to finish. */
|
|
|
|
TQWaitCondition m_jobFinished;
|
|
|
|
/** Indicates if the weaver is shutting down and exiting it's
|
|
|
|
threads. */
|
|
|
|
bool m_shuttingDown;
|
|
|
|
/** m_running is set to true when a job is enqueued and set to false
|
|
|
|
when the job finishes that was the last in the queue.
|
|
|
|
E.g., this will flip from false to true to false when you
|
|
|
|
continuously enqueue one single job. */
|
|
|
|
bool m_running;
|
|
|
|
/** If m_suspend is true, no new jobs will be assigned to
|
|
|
|
threads.
|
|
|
|
Jobs may be queued, but will not be processed until suspend
|
|
|
|
(false) is called. */
|
|
|
|
bool m_suspend;
|
|
|
|
private:
|
|
|
|
/** Mutex to serialize operations. */
|
|
|
|
TQMutex *m_mutex;
|
|
|
|
};
|
|
|
|
} // namespace ThreadWeaver
|
|
|
|
} // namespace KPIM
|
|
|
|
|
|
|
|
#endif // defined WEAVER_H
|