jobscheduler.cpp
00001 00029 #include "jobscheduler.h" 00030 #include "kmfolder.h" 00031 #include "folderstorage.h" 00032 #include "kmfoldermgr.h" 00033 #include <kdebug.h> 00034 00035 using namespace KMail; 00036 00037 JobScheduler::JobScheduler( TQObject* parent, const char* name ) 00038 : TQObject( parent, name ), mTimer( this, "mTimer" ), 00039 mPendingImmediateTasks( 0 ), 00040 mCurrentTask( 0 ), mCurrentJob( 0 ) 00041 { 00042 connect( &mTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( slotRunNextJob() ) ); 00043 // No need to start the internal timer yet, we wait for a task to be scheduled 00044 } 00045 00046 00047 JobScheduler::~JobScheduler() 00048 { 00049 // delete tasks in tasklist (no autodelete for TQValueList) 00050 for( TaskList::Iterator it = mTaskList.begin(); it != mTaskList.end(); ++it ) { 00051 delete (*it); 00052 } 00053 delete mCurrentTask; 00054 delete mCurrentJob; 00055 } 00056 00057 void JobScheduler::registerTask( ScheduledTask* task ) 00058 { 00059 bool immediate = task->isImmediate(); 00060 int typeId = task->taskTypeId(); 00061 if ( typeId ) { 00062 KMFolder* folder = task->folder(); 00063 // Search for an identical task already scheduled 00064 for( TaskList::Iterator it = mTaskList.begin(); it != mTaskList.end(); ++it ) { 00065 if ( (*it)->taskTypeId() == typeId && (*it)->folder() == folder ) { 00066 #ifdef DEBUG_SCHEDULER 00067 kdDebug(5006) << "JobScheduler: already having task type " << typeId << " for folder " << folder->label() << endl; 00068 #endif 00069 delete task; 00070 if ( !mCurrentTask && immediate ) { 00071 ScheduledTask* task = *it; 00072 removeTask( it ); 00073 runTaskNow( task ); 00074 } 00075 return; 00076 } 00077 } 00078 // Note that scheduling an identical task as the one currently running is allowed. 00079 } 00080 if ( !mCurrentTask && immediate ) 00081 runTaskNow( task ); 00082 else { 00083 #ifdef DEBUG_SCHEDULER 00084 kdDebug(5006) << "JobScheduler: adding task " << task << " (type " << task->taskTypeId() 00085 << ") for folder " << task->folder() << " " << task->folder()->label() << endl; 00086 #endif 00087 mTaskList.append( task ); 00088 if ( immediate ) 00089 ++mPendingImmediateTasks; 00090 if ( !mCurrentTask && !mTimer.isActive() ) 00091 restartTimer(); 00092 } 00093 } 00094 00095 void JobScheduler::removeTask( TaskList::Iterator& it ) 00096 { 00097 if ( (*it)->isImmediate() ) 00098 --mPendingImmediateTasks; 00099 mTaskList.remove( it ); 00100 } 00101 00102 void JobScheduler::notifyOpeningFolder( KMFolder* folder ) 00103 { 00104 if ( mCurrentTask && mCurrentTask->folder() == folder ) { 00105 if ( mCurrentJob->isOpeningFolder() ) { // set when starting a job for this folder 00106 #ifdef DEBUG_SCHEDULER 00107 kdDebug(5006) << "JobScheduler: got the opening-notification for " << folder->label() << " as expected." << endl; 00108 #endif 00109 } else { 00110 // Jobs scheduled from here should always be cancellable. 00111 // One exception though, is when ExpireJob does its final KMMoveCommand. 00112 // Then that command shouldn't kill its own parent job just because it opens a folder... 00113 if ( mCurrentJob->isCancellable() ) 00114 interruptCurrentTask(); 00115 } 00116 } 00117 } 00118 00119 void JobScheduler::interruptCurrentTask() 00120 { 00121 Q_ASSERT( mCurrentTask ); 00122 #ifdef DEBUG_SCHEDULER 00123 kdDebug(5006) << "JobScheduler: interrupting job " << mCurrentJob << " for folder " << mCurrentTask->folder()->label() << endl; 00124 #endif 00125 // File it again. This will either delete it or put it in mTaskList. 00126 registerTask( mCurrentTask ); 00127 mCurrentTask = 0; 00128 mCurrentJob->kill(); // This deletes the job and calls slotJobFinished! 00129 } 00130 00131 void JobScheduler::slotRunNextJob() 00132 { 00133 while ( !mCurrentJob ) { 00134 #ifdef DEBUG_SCHEDULER 00135 kdDebug(5006) << "JobScheduler: slotRunNextJob" << endl; 00136 #endif 00137 Q_ASSERT( mCurrentTask == 0 ); 00138 ScheduledTask* task = 0; 00139 // Find a task suitable for being run 00140 for( TaskList::Iterator it = mTaskList.begin(); it != mTaskList.end(); ++it ) { 00141 // Remove if folder died 00142 KMFolder* folder = (*it)->folder(); 00143 if ( folder == 0 ) { 00144 #ifdef DEBUG_SCHEDULER 00145 kdDebug(5006) << " folder for task " << (*it) << " was deleted" << endl; 00146 #endif 00147 removeTask( it ); 00148 if ( !mTaskList.isEmpty() ) 00149 slotRunNextJob(); // to avoid the mess with invalid iterators :) 00150 else 00151 mTimer.stop(); 00152 return; 00153 } 00154 // The condition is that the folder must be unused (not open) 00155 // But first we ask search folders to release their access to it 00156 kmkernel->searchFolderMgr()->tryReleasingFolder( folder ); 00157 #ifdef DEBUG_SCHEDULER 00158 kdDebug(5006) << " looking at folder " << folder->label() 00159 << " " << folder->location() 00160 << " isOpened=" << (*it)->folder()->isOpened() << endl; 00161 #endif 00162 if ( !folder->isOpened() ) { 00163 task = *it; 00164 removeTask( it ); 00165 break; 00166 } 00167 } 00168 00169 if ( !task ) // found nothing to run, i.e. folder was opened 00170 return; // Timer keeps running, i.e. try again in 1 minute 00171 00172 runTaskNow( task ); 00173 } // If nothing to do for that task, loop and find another one to run 00174 } 00175 00176 void JobScheduler::restartTimer() 00177 { 00178 if ( mPendingImmediateTasks > 0 ) 00179 slotRunNextJob(); 00180 else 00181 { 00182 #ifdef DEBUG_SCHEDULER 00183 mTimer.start( 10000 ); // 10 seconds 00184 #else 00185 mTimer.start( 1 * 60000 ); // 1 minute 00186 #endif 00187 } 00188 } 00189 00190 void JobScheduler::runTaskNow( ScheduledTask* task ) 00191 { 00192 Q_ASSERT( mCurrentTask == 0 ); 00193 if ( mCurrentTask ) { 00194 interruptCurrentTask(); 00195 } 00196 mCurrentTask = task; 00197 mTimer.stop(); 00198 mCurrentJob = mCurrentTask->run(); 00199 #ifdef DEBUG_SCHEDULER 00200 kdDebug(5006) << "JobScheduler: task " << mCurrentTask 00201 << " (type " << mCurrentTask->taskTypeId() << ")" 00202 << " for folder " << mCurrentTask->folder()->label() 00203 << " returned job " << mCurrentJob << " " 00204 << ( mCurrentJob?mCurrentJob->className():0 ) << endl; 00205 #endif 00206 if ( !mCurrentJob ) { // nothing to do, e.g. folder deleted 00207 delete mCurrentTask; 00208 mCurrentTask = 0; 00209 if ( !mTaskList.isEmpty() ) 00210 restartTimer(); 00211 return; 00212 } 00213 // Register the job in the folder. This makes it autodeleted if the folder is deleted. 00214 mCurrentTask->folder()->storage()->addJob( mCurrentJob ); 00215 connect( mCurrentJob, TQT_SIGNAL( finished() ), this, TQT_SLOT( slotJobFinished() ) ); 00216 mCurrentJob->start(); 00217 } 00218 00219 void JobScheduler::slotJobFinished() 00220 { 00221 // Do we need to test for mCurrentJob->error()? What do we do then? 00222 #ifdef DEBUG_SCHEDULER 00223 kdDebug(5006) << "JobScheduler: slotJobFinished" << endl; 00224 #endif 00225 delete mCurrentTask; 00226 mCurrentTask = 0; 00227 mCurrentJob = 0; 00228 if ( !mTaskList.isEmpty() ) 00229 restartTimer(); 00230 } 00231 00232 // DCOP call to pause any background jobs 00233 void JobScheduler::pause() 00234 { 00235 mPendingImmediateTasks = 0; 00236 if ( mCurrentJob && mCurrentJob->isCancellable() ) 00237 interruptCurrentTask(); 00238 mTimer.stop(); 00239 } 00240 00241 void JobScheduler::resume() 00242 { 00243 restartTimer(); 00244 } 00245 00247 00248 KMail::ScheduledJob::ScheduledJob( KMFolder* folder, bool immediate ) 00249 : FolderJob( 0, tOther, folder ), mImmediate( immediate ), 00250 mOpeningFolder( false ) 00251 { 00252 mCancellable = true; 00253 mSrcFolder = folder; 00254 } 00255 00256 #include "jobscheduler.moc"