Make the ServiceListDelegate use less handcoded values and more general variables...
[amarok.git] / src / threadmanager.h
blob91bc128f10a42a43a934bbf29041a6103f0c261d
1 // Author: Max Howell (C) Copyright 2004
2 // (c) 2005 Jeff Mitchell <kde-dev@emailgoeshere.com>
3 // See COPYING file that comes with this distribution
4 //
6 #ifndef THREADMANAGER_H
7 #define THREADMANAGER_H
9 #include "amarok_export.h"
11 #include <q3valuelist.h>
12 #include <QByteArray>
13 #include <QEvent> //baseclass
14 #include <QMap>
15 #include <QMutex>
16 #include <QObject>
17 #include <QPointer>
18 #include <QThread>
20 #define DISABLE_GENERATED_MEMBER_FUNCTIONS_3( T ) \
21 T( const T& ); \
22 T &operator=( const T& ); \
23 bool operator==( const T& ) const;
25 #define DISABLE_GENERATED_MEMBER_FUNCTIONS_4( T ) \
26 T(); \
27 DISABLE_GENERATED_MEMBER_FUNCTIONS_3( T )
30 /**
31 * @class ThreadManager
32 * @author Max Howell <max.howell@methylblue.com>
33 * @short ThreadManager is designed to encourage you to use threads and to make their use easy.
35 * You create Jobs on the heap and ThreadManager allows you to easily queue them,
36 * abort them, ensure only one runs at once, ensure that bad data is never acted
37 * on and even cleans up for you should the class that wants the Job's results
38 * get deleted while a thread is running.
40 * You also will (soon) get thread-safe error handling and thread-safe progress
41 * reporting.
43 * This is a typical use:
45 class MyJob : public ThreadManager::Job
47 public:
48 MyJob( QObject *dependent ) : Job( dependent, "MyJob" ) {}
50 virtual bool doJob() {
51 //do some work in thread...
53 return success;
56 virtual void completeJob {
57 //do completion work in the GUI thread...
61 SomeClass::someFunction()
63 ThreadManager::instance()->queueJob( new MyJob( this ) );
67 * That's it! The queue is fifo, there's one queue per job-type, the
68 * ThreadManager takes ownership of the Job, and the Manager calls
69 * Job::completeJob() on completion which you reimplement to do whatever you
70 * need done.
72 * BEWARE! None of the functions are thread-safe, only call them from the GUI
73 * thread or your application WILL crash!
75 * @see ThreadManager::Job
76 * @see ThreadManager::DependentJob
80 /// This class is because moc "is really good" (no nested Q_OBJECT classes)
81 class AMAROK_EXPORT JobBase : public QObject {
82 Q_OBJECT
83 protected: JobBase() : QObject(), m_aborted( false ) {}
84 public slots: void abort() { m_aborted = true; }
85 protected: bool m_aborted;
88 class ThreadManager : public QObject
90 Q_OBJECT
91 public:
92 class Thread;
93 friend class Thread;
94 typedef Q3ValueList<Thread*> ThreadList;
95 class Job;
96 friend class Job;
97 typedef Q3ValueList<Job*> JobList;
99 AMAROK_EXPORT static ThreadManager *instance();
100 static void deleteInstance();
103 * If the ThreadManager is already handling a job of this type then the job
104 * will be queued, otherwise the job will be processed immediately. Allocate
105 * the job on the heap, and ThreadManager will delete it for you.
107 * This is not thread-safe - only call it from the GUI-thread!
109 * @return number of jobs in the queue after the call
110 * @see ThreadManager::Job
112 AMAROK_EXPORT int queueJob( Job* );
115 * Queue multiple jobs simultaneously, you should use this to avoid the race
116 * condition where the first job finishes before you can queue the next one.
117 * This isn't a fatal condition, but it does cause wasteful thread deletion
118 * and re-creation. The only valid usage, is when the jobs are the same type!
120 * This is not thread-safe - only call it from the GUI-thread!
122 * @return number of jobs in the queue after the call
124 int queueJobs( const JobList& );
127 * If there are other jobs of the same type running, they will be aborted,
128 * then this one will be started afterwards. Aborted jobs will not have
129 * completeJob() called for them.
131 * This is not thread-safe - only call it from the GUI-thread!
133 void onlyOneJob( Job* );
136 * All the named jobs will be halted and deleted. You cannot use any data
137 * from the jobs reliably after this point. Job::completeJob() will not be
138 * called for any of these jobs.
140 * This is not thread-safe - only call it from the GUI-thread!
142 * @return how many jobs were aborted, or -1 if no thread was found
144 int abortAllJobsNamed( const QByteArray &name );
147 * @return true if a Job with name is queued or is running
149 bool isJobPending( const QByteArray &name ) { return jobCount( name ) > 0; }
152 * @return the number of jobs running, pending, aborted and otherwise.
154 uint jobCount( const QByteArray &name );
156 private:
157 ThreadManager();
158 ~ThreadManager();
160 enum EventType { JobEventType = 20202, OverrideCursorEventType, RestoreOverrideCursorEventType };
162 virtual bool event( QEvent* );
164 /// checks the pool for an available thread, creates a new one if required
165 Thread *gimmeThread();
167 /// safe disposal for threads that may not have finished
168 void dispose( Thread* );
170 /// all pending and running jobs
171 JobList m_jobs;
173 /// a thread-pool, ready for use or running jobs currently
174 ThreadList m_threads;
176 public:
178 * Class Thread
180 class Thread : public QThread
182 public:
183 Thread();
185 virtual void run();
187 void runJob( Job* );
188 void msleep( int ms ) { QThread::msleep( ms ); } //we need to make this public for class Job
190 Job *job() const { return m_job; }
192 private:
193 Job *m_job;
195 //private so I don't break something in the distant future
196 ~Thread();
198 //we can delete threads here only
199 friend bool ThreadManager::event( QEvent* );
201 protected:
202 DISABLE_GENERATED_MEMBER_FUNCTIONS_3( Thread )
206 * @class Job
207 * @short A small class for doing work in a background thread
209 * Derive a job, do the work in doJob(), do GUI-safe operations in
210 * completeJob(). If you return false from doJob() completeJob() won't be
211 * called. Name your Job well as like-named Jobs are queued together.
213 * Be sensible and pass data members to the Job, rather than operate on
214 * volatile data members in the GUI-thread.
216 * Things can change while you are in a separate thread. Stuff in the GUI
217 * thread may not be there anymore by the time you finish the job. @see
218 * ThreadManager::dependentJob for a solution.
220 * Do your cleanup in the destructor not completeJob(), as completeJob()
221 * doesn't have to be called.
224 class AMAROK_EXPORT Job : public JobBase, public QEvent
226 friend class ThreadManager; //access to m_thread
227 friend class ThreadManager::Thread; //access to m_aborted
229 public:
231 * Like-named jobs are queued and run FIFO. Always allocate Jobs on the
232 * heap, ThreadManager will take ownership of the memory.
234 Job( const char *name );
235 virtual ~Job();
238 * These are used by @class DependentJob, but are made available for
239 * your use should you need them.
241 enum EventType { JobFinishedEventType = ThreadManager::JobEventType, JobStartedEventType };
243 const char *name() const { return m_name; }
246 * If this returns true then in the worst case the entire Amarok UI is
247 * frozen waiting for your Job to abort! You should check for this
248 * often, but not so often that your code's readability suffers as a
249 * result.
251 * Aborted jobs will not have completeJob() called for them, even if
252 * they return true from doJob()
254 bool isAborted() const { return m_aborted; }
256 ///convenience function
257 bool wasSuccessful() const { return !m_aborted; }
260 * Calls QThread::msleep( int )
262 void msleep( int ms ) { m_thread->msleep( ms ); }
265 * You should set @param description if you set progress information
266 * do this in the ctor, or it won't have an effect
268 void setDescription( const QString &description ) { m_description = description; }
271 * If you set progress information, you should set this too, changing it when appropriate
273 void setStatus( const QString &status );
276 * This shows the progressBar too, the user will be able to abort
277 * the thread
279 void setProgressTotalSteps( uint steps );
282 * Does a thread-safe update of the progressBar
284 void setProgress( uint progress );
285 void setProgress100Percent() { setProgress( m_totalSteps ); }
288 * Convenience function, increments the progress by 1
290 void incrementProgress();
293 * Sometimes you want to hide the progressBar etc. generally you
294 * should show one, but perhaps you are a reimplemented class
295 * that doesn't want one?
297 //void setVisible( bool );
299 protected:
301 * Executed inside the thread, this should be reimplemented to do the
302 * job's work. Be thread-safe! Don't interact with the GUI-thread.
304 * @return true if you want completeJob() to be called from the GUI
305 * thread
307 virtual bool doJob() = 0;
310 * This is executed in the GUI thread if doJob() returns true;
312 virtual void completeJob() = 0;
314 /// be sure to call the base function in your reimplementation
315 virtual void customEvent( QEvent* );
317 private:
318 char const * const m_name;
319 Thread *m_thread;
321 protected: //FIXME
322 uint m_percentDone;
323 uint m_progressDone;
324 uint m_totalSteps;
326 QString m_description;
327 QString m_status;
329 protected:
330 DISABLE_GENERATED_MEMBER_FUNCTIONS_4( Job )
335 * @class DependentJob
336 * @short A Job that depends on the existence of a QObject
338 * This Job type is dependent on a QObject instance, if that instance is
339 * deleted, this Job will be aborted and safely deleted.
341 * ThreadManager::DependentJob (and Job, the baseclass) isa QEvent,
342 * and completeJob() is reimplemented to send the job to the dependent.
343 * Of course you can still reimplement completeJob() yourself.
345 * The dependent will receive a JobStartedEvent just after the creation of
346 * the Job (not after it has started unfortunately), and a JobFinishedEventType
347 * after the Job has finished.
349 * The dependent is a QPointer, so you can reference the pointer returned
350 * from dependent() safely provided you always test for 0 first. However
351 * safest of all is to not rely on that pointer at all! Pass required
352 * data-members with the job, only operate on the dependent in
353 * completeJob(). completeJob() will not be called if the dependent no
354 * longer exists
356 * It is only safe to have one dependent, if you depend on multiple objects
357 * that might get deleted while you are running you should instead try to
358 * make the multiple objects children of one QObject and depend on the
359 * top-most parent or best of all would be to make copies of the data you
360 * need instead of being dependent.
363 class AMAROK_EXPORT DependentJob : public Job
365 public:
366 DependentJob( QObject *dependent, const char *name );
368 virtual void completeJob();
370 QObject *dependent() { return m_dependent; }
372 private:
373 const QPointer<QObject> m_dependent;
375 protected:
376 DISABLE_GENERATED_MEMBER_FUNCTIONS_4( DependentJob )
379 protected:
380 ThreadManager( const ThreadManager& );
381 ThreadManager &operator=( const ThreadManager& );
382 private:
383 class JobCompletedEvent;
386 //useful debug thingy
387 #define DEBUG_THREAD_FUNC_INFO kDebug() << Debug::indent() << "thread: " << long( QThread::currentThread() );
389 #define SHOULD_BE_GUI if( QThread::currentThread() != QCoreApplication::instance()->thread() ) std::cout \
390 << "Should not be Threaded, but is running in" << \
391 long (QThread::currentThread()) <<std::endl;
394 inline ThreadManager*
395 ThreadManager::instance()
397 static ThreadManager* instance = new ThreadManager();
399 return instance;
402 inline void
403 ThreadManager::deleteInstance()
405 delete instance();
409 #endif