Revert previous commit, was incorrect
[amarok.git] / src / threadmanager.cpp
blobcf60df209472c1a23cb402b7509b4d591f78d9a2
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 // the asserts we use in this module prevent crashes, so best to abort the application if they fail
7 #define QT_FATAL_ASSERT
8 #define DEBUG_PREFIX "ThreadManager"
10 #include "threadmanager.h"
12 #include "amarokconfig.h"
13 #include "collectiondb.h"
14 #include "debug.h"
15 #include "ContextStatusBar.h"
17 #include <KCursor>
19 #include <QApplication>
20 #include <QByteArray>
21 #include <QEvent>
23 using Amarok::ContextStatusBar;
26 class ThreadManager::JobCompletedEvent: public QEvent
28 public:
29 static const int JobCompletedEventType = 1321;
30 JobCompletedEvent( Job *j ): QEvent( Type( JobCompletedEventType ) ), job( j ) { }
31 Job *job;
34 ThreadManager::ThreadManager()
36 startTimer( 5 * 60 * 1000 ); // prunes the thread pool every 5 minutes
39 ThreadManager::~ThreadManager()
41 DEBUG_BLOCK
43 for( ThreadList::Iterator it = m_threads.begin(), end = m_threads.end(); it != end; ++it )
45 #ifdef HAVE_INOTIFY
46 // we don't delete the thread's job as amarok is gone
47 // and the Job dtor may expect amarok to be there etc.
48 if ( (*it)->job() && (*it)->job()->name() == QByteArray( "INotify" ) )
50 debug() << "Forcibly terminating INotify thread...\n";
51 (*it)->terminate();
52 continue;
54 #endif
56 if( (*it)->job() && (*it)->job()->name() )
57 debug() << "Waiting on thread " << (*it)->job()->name() << "...\n";
58 else
59 debug() << "Waiting on thread...\n";
60 (*it)->wait();
64 uint
65 ThreadManager::jobCount( const QByteArray &name )
67 uint count = 0;
69 for( JobList::Iterator it = m_jobs.begin(), end = m_jobs.end(); it != end; ++it )
70 if ( name == (*it)->name() )
71 count++;
73 return count;
76 int
77 ThreadManager::queueJob( Job *job )
79 SHOULD_BE_GUI
81 if (!job)
82 return -1;
84 // this list contains all pending and running jobs
85 m_jobs += job;
87 const uint count = jobCount( job->name() );
89 if ( count == 1 )
90 gimmeThread()->runJob( job );
92 return count;
95 int
96 ThreadManager::queueJobs( const JobList &jobs )
98 SHOULD_BE_GUI
100 if ( jobs.isEmpty() )
101 return -1;
103 m_jobs += jobs;
105 const QByteArray name = jobs.front()->name();
106 const int count = jobCount( name );
108 if ( count == jobs.count() )
109 gimmeThread()->runJob( jobs.front() );
111 return count;
114 void
115 ThreadManager::onlyOneJob( Job *job )
117 SHOULD_BE_GUI
119 const QByteArray name = job->name();
121 // first cause all current jobs with this name to be aborted
122 abortAllJobsNamed( name );
124 // now queue this job.
125 // if there is a running Job of its type this one will be
126 // started when that one returns to the GUI thread.
127 m_jobs += job;
129 // if there weren't any jobs of this type running, we must
130 // start this job.
131 if ( jobCount( name ) == 1 )
132 gimmeThread()->runJob( job );
136 ThreadManager::abortAllJobsNamed( const QByteArray &name )
138 SHOULD_BE_GUI
140 int count = 0;
142 for( JobList::Iterator it = m_jobs.begin(), end = m_jobs.end(); it != end; ++it )
143 if ( name == (*it)->name() ) {
144 count++;
145 (*it)->abort();
148 return count;
151 ThreadManager::Thread*
152 ThreadManager::gimmeThread()
154 for( ThreadList::ConstIterator it = m_threads.begin(), end = m_threads.end(); it != end; ++it )
155 if ( !(*it)->isRunning() && (*it)->job() == 0 )
156 return *it;
158 Thread *thread = new Thread;
159 m_threads += thread;
160 return thread;
163 bool
164 ThreadManager::event( QEvent *e )
166 switch( e->type() )
168 case JobCompletedEvent::JobCompletedEventType: {
169 Job *job = static_cast<JobCompletedEvent*>( e )->job;
170 DebugStream d = debug() << "Job ";
171 const QByteArray name = job->name();
172 Thread *thread = job->m_thread;
174 QApplication::postEvent(
175 ThreadManager::instance(),
176 new QEvent( QEvent::Type( ThreadManager::RestoreOverrideCursorEventType ) ) );
178 if ( !job->isAborted() ) {
179 d << "completed";
180 job->completeJob();
182 else d << "aborted";
184 m_jobs.remove( job );
185 delete job;
187 d << ": " << name;
188 d << ". Jobs pending: " << jobCount( name );
190 for( JobList::ConstIterator it = m_jobs.begin(), end = m_jobs.end(); it != end; ++it )
191 if ( name == (*it)->name() ) {
192 thread->runJob( (*it) );
193 return true;
196 // this thread is done
197 thread->m_job = 0;
199 break;
202 case QEvent::Timer:
203 debug() << "Threads in pool: " << m_threads.count();
205 // for( ThreadList::Iterator it = m_threads.begin(), end = m_threads.end(); it != end; ++it )
206 // if ( (*it)->readyForTrash() ) {
207 // m_threads.remove( it );
208 // delete *it;
209 // break; // only delete 1 thread every 5 minutes
210 // }
211 break;
213 case OverrideCursorEventType:
214 // we have to do this for the PlaylistLoader case, as Qt uses the same
215 // function for drag and drop operations.
216 QApplication::setOverrideCursor( Qt::BusyCursor );
217 break;
219 case RestoreOverrideCursorEventType:
220 // we have to do this for the PlaylistLoader case, as Qt uses the same
221 // function for drag and drop operations.
222 QApplication::restoreOverrideCursor();
223 break;
225 default:
226 return false;
229 return true;
233 /// @class ThreadManager::Thread
235 ThreadManager::Thread::Thread()
236 : QThread()
239 ThreadManager::Thread::~Thread()
241 Q_ASSERT( isFinished() );
244 void
245 ThreadManager::Thread::runJob( Job *job )
247 job->m_thread = this;
249 if ( job->isAborted() )
250 QApplication::postEvent( ThreadManager::instance(), new JobCompletedEvent( job ) );
252 else {
253 m_job = job;
254 if( isRunning() )
255 wait();
256 start( Thread::IdlePriority );
258 QApplication::postEvent(
259 ThreadManager::instance(),
260 new QEvent( QEvent::Type( ThreadManager::OverrideCursorEventType ) ) );
264 void
265 ThreadManager::Thread::run()
267 // BE THREAD-SAFE!
269 DEBUG_BLOCK
271 //keep this first, before anything that uses the database, or SQLite may error out
272 if ( AmarokConfig::databaseEngine().toInt() == DbConnection::sqlite )
273 CollectionDB::instance()->releasePreviousConnection( this );
275 if( m_job )
277 m_job->m_aborted |= !m_job->doJob();
278 QApplication::postEvent( ThreadManager::instance(), new JobCompletedEvent( m_job ) );
281 // almost always the thread doesn't finish until after the
282 // above event is already finished processing
287 /// @class ProgressEvent
288 /// @short Used by ThreadManager::Job internally
290 class ProgressEvent : public QEvent {
291 public:
292 static const int ProgressEventType = 30303;
293 ProgressEvent( int progress )
294 : QEvent( Type( ProgressEventType ) )
295 , progress( progress ) {}
297 const int progress;
302 /// @class ThreadManager::Job
304 ThreadManager::Job::Job( const char *name )
305 : QEvent( Type( ThreadManager::JobEventType ) )
306 , m_name( name )
307 , m_thread( 0 )
308 , m_percentDone( 0 )
309 , m_progressDone( 0 )
310 , m_totalSteps( 1 ) // no divide by zero
313 ThreadManager::Job::~Job()
315 /*if( m_thread->running() && m_thread->job() == this )
316 warning() << "Deleting a job before its thread has finished with it!\n";*/
319 void
320 ThreadManager::Job::setProgressTotalSteps( uint steps )
322 if ( steps == 0 ) {
323 warning() << "You can't set steps to 0!\n";
324 steps = 1;
327 m_totalSteps = steps;
329 QApplication::postEvent( this, new ProgressEvent( -1 ) );
332 void
333 ThreadManager::Job::setProgress( uint steps )
335 m_progressDone = steps;
337 uint newPercent = uint( (100 * steps) / m_totalSteps);
339 if ( newPercent != m_percentDone ) {
340 m_percentDone = newPercent;
341 QApplication::postEvent( this, new ProgressEvent( newPercent ) );
345 void
346 ThreadManager::Job::setStatus( const QString &status )
348 m_status = status;
350 QApplication::postEvent( this, new ProgressEvent( -2 ) );
353 void
354 ThreadManager::Job::incrementProgress()
356 setProgress( m_progressDone + 1 );
359 void
360 ThreadManager::Job::customEvent( QEvent *e )
362 int progress = static_cast<ProgressEvent*>(e)->progress;
364 switch( progress )
366 case -2:
367 ContextStatusBar::instance()->setProgressStatus( this, m_status );
368 break;
370 case -1:
371 ContextStatusBar::instance()->newProgressOperation( this )
372 .setDescription( m_description )
373 .setAbortSlot( this, SLOT(abort()) )
374 .setMaximum( 100 );
375 break;
377 default:
378 ContextStatusBar::instance()->setProgress( this, progress );
384 ThreadManager::DependentJob::DependentJob( QObject *dependent, const char *name )
385 : Job( name )
386 , m_dependent( dependent )
388 Q_ASSERT( dependent != this );
389 connect( dependent, SIGNAL(destroyed()), SLOT(abort()) );
391 QApplication::postEvent( dependent, new QEvent( Type( JobStartedEventType ) ) );
394 void
395 ThreadManager::DependentJob::completeJob()
397 //synchronous, so we don't get deleted twice
398 QApplication::sendEvent( m_dependent, this );
401 #include "threadmanager.moc"
402 #undef QT_FATAL_ASSERT //enable-final