kmail

jobscheduler.cpp

Go to the documentation of this file.
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( QObject* parent, const char* name )
00038   : QObject( parent, name ), mTimer( this, "mTimer" ),
00039     mPendingImmediateTasks( 0 ),
00040     mCurrentTask( 0 ), mCurrentJob( 0 )
00041 {
00042   connect( &mTimer, SIGNAL( timeout() ), 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 QValueList)
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, SIGNAL( finished() ), this, 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"