Make playlist items use the full width available to them and adjust correctly when...
[amarok.git] / src / threadmanager.cpp
blob5feca94285aa8c93e0a3e9bad72ab65697c17bdb
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 "statusbar.h"
17 #include <KCursor>
19 #include <QApplication>
20 #include <QByteArray>
21 #include <QEvent>
23 using Amarok::StatusBar;
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 );
189 d << endl;
191 for( JobList::ConstIterator it = m_jobs.begin(), end = m_jobs.end(); it != end; ++it )
192 if ( name == (*it)->name() ) {
193 thread->runJob( (*it) );
194 return true;
197 // this thread is done
198 thread->m_job = 0;
200 break;
203 case QEvent::Timer:
204 debug() << "Threads in pool: " << m_threads.count();
206 // for( ThreadList::Iterator it = m_threads.begin(), end = m_threads.end(); it != end; ++it )
207 // if ( (*it)->readyForTrash() ) {
208 // m_threads.remove( it );
209 // delete *it;
210 // break; // only delete 1 thread every 5 minutes
211 // }
212 break;
214 case OverrideCursorEventType:
215 // we have to do this for the PlaylistLoader case, as Qt uses the same
216 // function for drag and drop operations.
217 QApplication::setOverrideCursor( Qt::BusyCursor );
218 break;
220 case RestoreOverrideCursorEventType:
221 // we have to do this for the PlaylistLoader case, as Qt uses the same
222 // function for drag and drop operations.
223 QApplication::restoreOverrideCursor();
224 break;
226 default:
227 return false;
230 return true;
234 /// @class ThreadManager::Thread
236 ThreadManager::Thread::Thread()
237 : QThread()
240 ThreadManager::Thread::~Thread()
242 Q_ASSERT( isFinished() );
245 void
246 ThreadManager::Thread::runJob( Job *job )
248 job->m_thread = this;
250 if ( job->isAborted() )
251 QApplication::postEvent( ThreadManager::instance(), new JobCompletedEvent( job ) );
253 else {
254 m_job = job;
255 if( isRunning() )
256 wait();
257 start( Thread::IdlePriority );
259 QApplication::postEvent(
260 ThreadManager::instance(),
261 new QEvent( QEvent::Type( ThreadManager::OverrideCursorEventType ) ) );
265 void
266 ThreadManager::Thread::run()
268 // BE THREAD-SAFE!
270 DEBUG_BLOCK
272 //keep this first, before anything that uses the database, or SQLite may error out
273 if ( AmarokConfig::databaseEngine().toInt() == DbConnection::sqlite )
274 CollectionDB::instance()->releasePreviousConnection( this );
276 if( m_job )
278 m_job->m_aborted |= !m_job->doJob();
279 QApplication::postEvent( ThreadManager::instance(), new JobCompletedEvent( m_job ) );
282 // almost always the thread doesn't finish until after the
283 // above event is already finished processing
288 /// @class ProgressEvent
289 /// @short Used by ThreadManager::Job internally
291 class ProgressEvent : public QEvent {
292 public:
293 static const int ProgressEventType = 30303;
294 ProgressEvent( int progress )
295 : QEvent( Type( ProgressEventType ) )
296 , progress( progress ) {}
298 const int progress;
303 /// @class ThreadManager::Job
305 ThreadManager::Job::Job( const char *name )
306 : QEvent( Type( ThreadManager::JobEventType ) )
307 , m_name( name )
308 , m_thread( 0 )
309 , m_percentDone( 0 )
310 , m_progressDone( 0 )
311 , m_totalSteps( 1 ) // no divide by zero
314 ThreadManager::Job::~Job()
316 /*if( m_thread->running() && m_thread->job() == this )
317 warning() << "Deleting a job before its thread has finished with it!\n";*/
320 void
321 ThreadManager::Job::setProgressTotalSteps( uint steps )
323 if ( steps == 0 ) {
324 warning() << "You can't set steps to 0!\n";
325 steps = 1;
328 m_totalSteps = steps;
330 QApplication::postEvent( this, new ProgressEvent( -1 ) );
333 void
334 ThreadManager::Job::setProgress( uint steps )
336 m_progressDone = steps;
338 uint newPercent = uint( (100 * steps) / m_totalSteps);
340 if ( newPercent != m_percentDone ) {
341 m_percentDone = newPercent;
342 QApplication::postEvent( this, new ProgressEvent( newPercent ) );
346 void
347 ThreadManager::Job::setStatus( const QString &status )
349 m_status = status;
351 QApplication::postEvent( this, new ProgressEvent( -2 ) );
354 void
355 ThreadManager::Job::incrementProgress()
357 setProgress( m_progressDone + 1 );
360 void
361 ThreadManager::Job::customEvent( QEvent *e )
363 int progress = static_cast<ProgressEvent*>(e)->progress;
365 switch( progress )
367 case -2:
368 StatusBar::instance()->setProgressStatus( this, m_status );
369 break;
371 case -1:
372 StatusBar::instance()->newProgressOperation( this )
373 .setDescription( m_description )
374 .setAbortSlot( this, SLOT(abort()) )
375 .setMaximum( 100 );
376 break;
378 default:
379 StatusBar::instance()->setProgress( this, progress );
385 ThreadManager::DependentJob::DependentJob( QObject *dependent, const char *name )
386 : Job( name )
387 , m_dependent( dependent )
389 Q_ASSERT( dependent != this );
390 connect( dependent, SIGNAL(destroyed()), SLOT(abort()) );
392 QApplication::postEvent( dependent, new QEvent( Type( JobStartedEventType ) ) );
395 void
396 ThreadManager::DependentJob::completeJob()
398 //synchronous, so we don't get deleted twice
399 QApplication::sendEvent( m_dependent, this );
402 #include "threadmanager.moc"
403 #undef QT_FATAL_ASSERT //enable-final