• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KIO

scheduler.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2000 Stephan Kulow <coolo@kde.org>
00003                       Waldo Bastian <bastian@kde.org>
00004    Copyright (C) 2009, 2010 Andreas Hartmetz <ahartmetz@gmail.com>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License version 2 as published by the Free Software Foundation.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018    Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "scheduler.h"
00022 #include "scheduler_p.h"
00023 
00024 #include "sessiondata.h"
00025 #include "slaveconfig.h"
00026 #include "authinfo.h"
00027 #include "slave.h"
00028 #include "connection.h"
00029 #include "job_p.h"
00030 
00031 #include <kdebug.h>
00032 #include <kglobal.h>
00033 #include <kprotocolmanager.h>
00034 #include <kprotocolinfo.h>
00035 #include <assert.h>
00036 #include <kdesu/client.h>
00037 
00038 #include <QtCore/QHash>
00039 #include <QtGui/QWidget>
00040 #include <QtDBus/QtDBus>
00041 
00042 // Slaves may be idle for a certain time (3 minutes) before they are killed.
00043 static const int s_idleSlaveLifetime = 3 * 60;
00044 
00045 
00046 using namespace KIO;
00047 
00048 #ifndef KDE_USE_FINAL // already defined in job.cpp
00049 static inline Slave *jobSlave(SimpleJob *job)
00050 {
00051     return SimpleJobPrivate::get(job)->m_slave;
00052 }
00053 #endif
00054 
00055 static inline int jobCommand(SimpleJob *job)
00056 {
00057     return SimpleJobPrivate::get(job)->m_command;
00058 }
00059 
00060 static inline void startJob(SimpleJob *job, Slave *slave)
00061 {
00062     SimpleJobPrivate::get(job)->start(slave);
00063 }
00064 
00065 // here be uglies
00066 // forward declaration to break cross-dependency of SlaveKeeper and SchedulerPrivate
00067 static void setupSlave(KIO::Slave *slave, const KUrl &url, const QString &protocol,
00068                        const QString &proxy , bool newSlave, const KIO::MetaData *config = 0);
00069 // same reason as above
00070 static Scheduler *scheduler();
00071 static Slave *heldSlaveForJob(SimpleJob *job);
00072 
00073 
00074 int SerialPicker::changedPrioritySerial(int oldSerial, int newPriority) const
00075 {
00076     Q_ASSERT(newPriority >= -10 && newPriority <= 10);
00077     newPriority = qBound(-10, newPriority, 10);
00078     int unbiasedSerial = oldSerial % m_jobsPerPriority;
00079     return unbiasedSerial + newPriority * m_jobsPerPriority;
00080 }
00081 
00082 
00083 SlaveKeeper::SlaveKeeper()
00084 {
00085     m_grimTimer.setSingleShot(true);
00086     connect (&m_grimTimer, SIGNAL(timeout()), SLOT(grimReaper()));
00087 }
00088 
00089 void SlaveKeeper::returnSlave(Slave *slave)
00090 {
00091     Q_ASSERT(slave);
00092     slave->setIdle();
00093     m_idleSlaves.insert(slave->host(), slave);
00094     scheduleGrimReaper();
00095 }
00096 
00097 Slave *SlaveKeeper::takeSlaveForJob(SimpleJob *job)
00098 {
00099     Slave *slave = heldSlaveForJob(job);
00100     if (slave) {
00101         return slave;
00102     }
00103 
00104     KUrl url = SimpleJobPrivate::get(job)->m_url;
00105     // TODO take port, username and password into account
00106     QMultiHash<QString, Slave *>::Iterator it = m_idleSlaves.find(url.host());
00107     if (it == m_idleSlaves.end()) {
00108         it = m_idleSlaves.begin();
00109     }
00110     if (it == m_idleSlaves.end()) {
00111         return 0;
00112     }
00113     slave = it.value();
00114     m_idleSlaves.erase(it);
00115     return slave;
00116 }
00117 
00118 bool SlaveKeeper::removeSlave(Slave *slave)
00119 {
00120     // ### performance not so great
00121     QMultiHash<QString, Slave *>::Iterator it = m_idleSlaves.begin();
00122     for (; it != m_idleSlaves.end(); ++it) {
00123         if (it.value() == slave) {
00124             m_idleSlaves.erase(it);
00125             return true;
00126         }
00127     }
00128     return false;
00129 }
00130 
00131 QList<Slave *> SlaveKeeper::allSlaves() const
00132 {
00133     return m_idleSlaves.values();
00134 }
00135 
00136 void SlaveKeeper::scheduleGrimReaper()
00137 {
00138     if (!m_grimTimer.isActive()) {
00139         m_grimTimer.start((s_idleSlaveLifetime / 2) * 1000);
00140     }
00141 }
00142 
00143 //private slot
00144 void SlaveKeeper::grimReaper()
00145 {
00146     QMultiHash<QString, Slave *>::Iterator it = m_idleSlaves.begin();
00147     while (it != m_idleSlaves.end()) {
00148         Slave *slave = it.value();
00149         if (slave->idleTime() >= s_idleSlaveLifetime) {
00150             it = m_idleSlaves.erase(it);
00151             if (slave->job()) {
00152                 kDebug (7006) << "Idle slave" << slave << "still has job" << slave->job();
00153             }
00154             slave->kill();
00155             // avoid invoking slotSlaveDied() because its cleanup services are not needed
00156             slave->deref();
00157         } else {
00158             ++it;
00159         }
00160     }
00161     if (!m_idleSlaves.isEmpty()) {
00162         scheduleGrimReaper();
00163     }
00164 }
00165 
00166 
00167 int HostQueue::lowestSerial() const
00168 {
00169     QMap<int, SimpleJob*>::ConstIterator first = m_queuedJobs.constBegin();
00170     if (first != m_queuedJobs.constEnd()) {
00171         return first.key();
00172     }
00173     return SerialPicker::maxSerial;
00174 }
00175 
00176 void HostQueue::queueJob(SimpleJob *job)
00177 {
00178     const int serial = SimpleJobPrivate::get(job)->m_schedSerial;
00179     Q_ASSERT(serial != 0);
00180     Q_ASSERT(!m_queuedJobs.contains(serial));
00181     Q_ASSERT(!m_runningJobs.contains(job));
00182     m_queuedJobs.insert(serial, job);
00183 }
00184 
00185 SimpleJob *HostQueue::nextStartingJob()
00186 {
00187     Q_ASSERT(!m_queuedJobs.isEmpty());
00188     QMap<int, SimpleJob *>::iterator first = m_queuedJobs.begin();
00189     SimpleJob *job = first.value();
00190     m_queuedJobs.erase(first);
00191     m_runningJobs.insert(job);
00192     return job;
00193 }
00194 
00195 bool HostQueue::removeJob(SimpleJob *job)
00196 {
00197     const int serial = SimpleJobPrivate::get(job)->m_schedSerial;
00198     if (m_runningJobs.remove(job)) {
00199         Q_ASSERT(!m_queuedJobs.contains(serial));
00200         return true;
00201     }
00202     if (m_queuedJobs.remove(serial)) {
00203         return true;
00204     }
00205     return false;
00206 }
00207 
00208 QList<Slave *> HostQueue::allSlaves() const
00209 {
00210     QList<Slave *> ret;
00211     Q_FOREACH (SimpleJob *job, m_runningJobs) {
00212         Slave *slave = jobSlave(job);
00213         Q_ASSERT(slave);
00214         ret.append(slave);
00215     }
00216     return ret;
00217 }
00218 
00219 
00220 
00221 ConnectedSlaveQueue::ConnectedSlaveQueue()
00222 {
00223     m_startJobsTimer.setSingleShot(true);
00224     connect (&m_startJobsTimer, SIGNAL(timeout()), SLOT(startRunnableJobs()));
00225 }
00226 
00227 bool ConnectedSlaveQueue::queueJob(SimpleJob *job, Slave *slave)
00228 {
00229     QHash<Slave *, PerSlaveQueue>::Iterator it = m_connectedSlaves.find(slave);
00230     if (it == m_connectedSlaves.end()) {
00231         return false;
00232     }
00233     SimpleJobPrivate::get(job)->m_slave = slave;
00234 
00235     PerSlaveQueue &jobs = it.value();
00236     jobs.waitingList.append(job);
00237     if (!jobs.runningJob) {
00238         // idle slave now has a job to run
00239         m_runnableSlaves.insert(slave);
00240         m_startJobsTimer.start();
00241     }
00242     return true;
00243 }
00244 
00245 bool ConnectedSlaveQueue::removeJob(SimpleJob *job)
00246 {
00247     Slave *slave = jobSlave(job);
00248     Q_ASSERT(slave);
00249     QHash<Slave *, PerSlaveQueue>::Iterator it = m_connectedSlaves.find(slave);
00250     if (it == m_connectedSlaves.end()) {
00251         return false;
00252     }
00253     PerSlaveQueue &jobs = it.value();
00254     if (jobs.runningJob || jobs.waitingList.isEmpty()) {
00255         // a slave that was busy running a job was not runnable.
00256         // a slave that has no waiting job(s) was not runnable either.
00257         Q_ASSERT(!m_runnableSlaves.contains(slave));
00258     }
00259 
00260     const bool removedRunning = jobs.runningJob == job;
00261     const bool removedWaiting = jobs.waitingList.removeAll(job) != 0;
00262     if (removedRunning) {
00263         jobs.runningJob = 0;
00264         Q_ASSERT(!removedWaiting);
00265     }
00266     const bool removedTheJob = removedRunning || removedWaiting;
00267 
00268     if (!slave->isAlive()) {
00269         removeSlave(slave);
00270         return removedTheJob;
00271     }
00272 
00273     if (removedRunning && jobs.waitingList.count()) {
00274         m_runnableSlaves.insert(slave);
00275         m_startJobsTimer.start();
00276     }
00277     if (removedWaiting && jobs.waitingList.isEmpty()) {
00278         m_runnableSlaves.remove(slave);
00279     }
00280     return removedTheJob;
00281 }
00282 
00283 void ConnectedSlaveQueue::addSlave(Slave *slave)
00284 {
00285     Q_ASSERT(slave);
00286     if (!m_connectedSlaves.contains(slave)) {
00287         m_connectedSlaves.insert(slave, PerSlaveQueue());
00288     }
00289 }
00290 
00291 bool ConnectedSlaveQueue::removeSlave(Slave *slave)
00292 {
00293     QHash<Slave *, PerSlaveQueue>::Iterator it = m_connectedSlaves.find(slave);
00294     if (it == m_connectedSlaves.end()) {
00295         return false;
00296     }
00297     PerSlaveQueue &jobs = it.value();
00298     Q_FOREACH (SimpleJob *job, jobs.waitingList) {
00299         // ### for compatibility with the old scheduler we don't touch the running job, if any.
00300         // make sure that the job doesn't call back into Scheduler::cancelJob(); this would
00301         // a) crash and b) be unnecessary because we clean up just fine.
00302         SimpleJobPrivate::get(job)->m_schedSerial = 0;
00303         job->kill();
00304     }
00305     m_connectedSlaves.erase(it);
00306     m_runnableSlaves.remove(slave);
00307 
00308     slave->kill();
00309     return true;
00310 }
00311 
00312 // KDE5: only one caller, for doubtful reasons. remove this if possible.
00313 bool ConnectedSlaveQueue::isIdle(Slave *slave)
00314 {
00315     QHash<Slave *, PerSlaveQueue>::Iterator it = m_connectedSlaves.find(slave);
00316     if (it == m_connectedSlaves.end()) {
00317         return false;
00318     }
00319     return it.value().runningJob == 0;
00320 }
00321 
00322 
00323 //private slot
00324 void ConnectedSlaveQueue::startRunnableJobs()
00325 {
00326     QSet<Slave *>::Iterator it = m_runnableSlaves.begin();
00327     while (it != m_runnableSlaves.end()) {
00328         Slave *slave = *it;
00329         if (!slave->isConnected()) {
00330             // this polling is somewhat inefficient...
00331             m_startJobsTimer.start();
00332             ++it;
00333             continue;
00334         }
00335         it = m_runnableSlaves.erase(it);
00336         PerSlaveQueue &jobs = m_connectedSlaves[slave];
00337         SimpleJob *job = jobs.waitingList.takeFirst();
00338         Q_ASSERT(!jobs.runningJob);
00339         jobs.runningJob = job;
00340 
00341         const KUrl url = job->url();
00342         // no port is -1 in QUrl, but in kde3 we used 0 and the kioslaves assume that.
00343         const int port = url.port() == -1 ? 0 : url.port();
00344 
00345         if (slave->host() == "<reset>") {
00346             MetaData configData = SlaveConfig::self()->configData(url.protocol(), url.host());
00347             slave->setConfig(configData);
00348             slave->setProtocol(url.protocol());
00349             slave->setHost(url.host(), port, url.user(), url.pass());
00350         }
00351 
00352         Q_ASSERT(slave->protocol() == url.protocol());
00353         Q_ASSERT(slave->host() == url.host());
00354         Q_ASSERT(slave->port() == port);
00355         startJob(job, slave);
00356     }
00357 }
00358 
00359 
00360 static void ensureNoDuplicates(QMap<int, HostQueue *> *queuesBySerial)
00361 {
00362     Q_UNUSED(queuesBySerial);
00363 #ifdef SCHEDULER_DEBUG
00364     // a host queue may *never* be in queuesBySerial twice.
00365     QSet<HostQueue *> seen;
00366     Q_FOREACH (HostQueue *hq, *queuesBySerial) {
00367         Q_ASSERT(!seen.contains(hq));
00368         seen.insert(hq);
00369     }
00370 #endif
00371 }
00372 
00373 static void verifyRunningJobsCount(QHash<QString, HostQueue> *queues, int runningJobsCount)
00374 {
00375     Q_UNUSED(queues);
00376     Q_UNUSED(runningJobsCount);
00377 #ifdef SCHEDULER_DEBUG
00378     int realRunningJobsCount = 0;
00379     Q_FOREACH (const HostQueue &hq, *queues) {
00380         realRunningJobsCount += hq.runningJobsCount();
00381     }
00382     Q_ASSERT(realRunningJobsCount == runningJobsCount);
00383 
00384     // ...and of course we may never run the same job twice!
00385     QSet<SimpleJob *> seenJobs;
00386     Q_FOREACH (const HostQueue &hq, *queues) {
00387         Q_FOREACH (SimpleJob *job, hq.runningJobs()) {
00388             Q_ASSERT(!seenJobs.contains(job));
00389             seenJobs.insert(job);
00390         }
00391     }
00392 #endif
00393 }
00394 
00395 
00396 ProtoQueue::ProtoQueue(SchedulerPrivate *sp, int maxSlaves, int maxSlavesPerHost)
00397  : m_schedPrivate(sp),
00398    m_maxConnectionsPerHost(maxSlavesPerHost ? maxSlavesPerHost : maxSlaves),
00399    m_maxConnectionsTotal(qMax(maxSlaves, maxSlavesPerHost)),
00400    m_runningJobsCount(0)
00401 
00402 {
00403     kDebug(7006) << "m_maxConnectionsTotal:" << m_maxConnectionsTotal
00404                  << "m_maxConnectionsPerHost:" << m_maxConnectionsPerHost;
00405     Q_ASSERT(m_maxConnectionsPerHost >= 1);
00406     Q_ASSERT(maxSlaves >= maxSlavesPerHost);
00407     m_startJobTimer.setSingleShot(true);
00408     connect (&m_startJobTimer, SIGNAL(timeout()), SLOT(startAJob()));
00409 }
00410 
00411 ProtoQueue::~ProtoQueue()
00412 {
00413     Q_FOREACH (Slave *slave, allSlaves()) {
00414         // kill the slave process, then remove the interface in our process
00415         slave->kill();
00416         slave->deref();
00417     }
00418 }
00419 
00420 void ProtoQueue::queueJob(SimpleJob *job)
00421 {
00422     QString hostname = SimpleJobPrivate::get(job)->m_url.host();
00423     HostQueue &hq = m_queuesByHostname[hostname];
00424     const int prevLowestSerial = hq.lowestSerial();
00425     Q_ASSERT(hq.runningJobsCount() <= m_maxConnectionsPerHost);
00426 
00427     // nevert insert a job twice
00428     Q_ASSERT(SimpleJobPrivate::get(job)->m_schedSerial == 0);
00429     SimpleJobPrivate::get(job)->m_schedSerial = m_serialPicker.next();
00430 
00431     const bool wasQueueEmpty = hq.isQueueEmpty();
00432     hq.queueJob(job);
00433     // note that HostQueue::queueJob() into an empty queue changes its lowestSerial() too...
00434     // the queue's lowest serial job may have changed, so update the ordered list of queues.
00435     // however, we ignore all jobs that would cause more connections to a host than allowed.
00436     if (prevLowestSerial != hq.lowestSerial()) {
00437         if (hq.runningJobsCount() < m_maxConnectionsPerHost) {
00438             // if the connection limit didn't keep the HQ unscheduled it must have been lack of jobs
00439             if (m_queuesBySerial.remove(prevLowestSerial) == 0) {
00440                 Q_UNUSED(wasQueueEmpty);
00441                 Q_ASSERT(wasQueueEmpty);
00442             }
00443             m_queuesBySerial.insert(hq.lowestSerial(), &hq);
00444         } else {
00445 #ifdef SCHEDULER_DEBUG
00446             // ### this assertion may fail if the limits were modified at runtime!
00447             // if the per-host connection limit is already reached the host queue's lowest serial
00448             // should not be queued.
00449             Q_ASSERT(!m_queuesBySerial.contains(prevLowestSerial));
00450 #endif
00451         }
00452     }
00453     // just in case; startAJob() will refuse to start a job if it shouldn't.
00454     m_startJobTimer.start();
00455 
00456     ensureNoDuplicates(&m_queuesBySerial);
00457 }
00458 
00459 void ProtoQueue::changeJobPriority(SimpleJob *job, int newPrio)
00460 {
00461     SimpleJobPrivate *jobPriv = SimpleJobPrivate::get(job);
00462     QHash<QString, HostQueue>::Iterator it = m_queuesByHostname.find(jobPriv->m_url.host());
00463     if (it == m_queuesByHostname.end()) {
00464         return;
00465     }
00466     HostQueue &hq = it.value();
00467     const int prevLowestSerial = hq.lowestSerial();
00468     if (hq.isJobRunning(job) || !hq.removeJob(job)) {
00469         return;
00470     }
00471     jobPriv->m_schedSerial = m_serialPicker.changedPrioritySerial(jobPriv->m_schedSerial, newPrio);
00472     hq.queueJob(job);
00473     const bool needReinsert = hq.lowestSerial() != prevLowestSerial;
00474     // the host queue might be absent from m_queuesBySerial because the connections per host limit
00475     // for that host has been reached.
00476     if (needReinsert && m_queuesBySerial.remove(prevLowestSerial)) {
00477         m_queuesBySerial.insert(hq.lowestSerial(), &hq);
00478     }
00479     ensureNoDuplicates(&m_queuesBySerial);
00480 }
00481 
00482 void ProtoQueue::removeJob(SimpleJob *job)
00483 {
00484     SimpleJobPrivate *jobPriv = SimpleJobPrivate::get(job);
00485     HostQueue &hq = m_queuesByHostname[jobPriv->m_url.host()];
00486     const int prevLowestSerial = hq.lowestSerial();
00487     const int prevRunningJobs = hq.runningJobsCount();
00488 
00489     Q_ASSERT(hq.runningJobsCount() <= m_maxConnectionsPerHost);
00490 
00491     if (hq.removeJob(job)) {
00492         if (hq.lowestSerial() != prevLowestSerial) {
00493             // we have dequeued the not yet running job with the lowest serial
00494             Q_ASSERT(!jobPriv->m_slave);
00495             Q_ASSERT(prevRunningJobs == hq.runningJobsCount());
00496             if (m_queuesBySerial.remove(prevLowestSerial) == 0) {
00497                 // make sure that the queue was not scheduled for a good reason
00498                 Q_ASSERT(hq.runningJobsCount() == m_maxConnectionsPerHost);
00499             }
00500         } else {
00501             if (prevRunningJobs != hq.runningJobsCount()) {
00502                 // we have dequeued a previously running job
00503                 Q_ASSERT(prevRunningJobs - 1 == hq.runningJobsCount());
00504                 m_runningJobsCount--;
00505                 Q_ASSERT(m_runningJobsCount >= 0);
00506             }
00507         }
00508         if (!hq.isQueueEmpty() && hq.runningJobsCount() < m_maxConnectionsPerHost) {
00509             // this may be a no-op, but it's faster than first checking if it's already in.
00510             m_queuesBySerial.insert(hq.lowestSerial(), &hq);
00511         }
00512 
00513         if (hq.isEmpty()) {
00514             // no queued jobs, no running jobs. this destroys hq from above.
00515             m_queuesByHostname.remove(jobPriv->m_url.host());
00516         }
00517 
00518         if (jobPriv->m_slave && jobPriv->m_slave->isAlive()) {
00519             m_slaveKeeper.returnSlave(jobPriv->m_slave);
00520         }
00521         // just in case; startAJob() will refuse to start a job if it shouldn't.
00522         m_startJobTimer.start();
00523     } else {
00524         // should be a connected slave
00525         // if the assertion fails the job has probably changed the host part of its URL while
00526         // running, so we can't find it by hostname. don't do this.
00527         const bool removed = m_connectedSlaveQueue.removeJob(job);
00528         Q_UNUSED(removed);
00529         Q_ASSERT(removed);
00530     }
00531 
00532     ensureNoDuplicates(&m_queuesBySerial);
00533 }
00534 
00535 Slave *ProtoQueue::createSlave(const QString &protocol, SimpleJob *job, const KUrl &url)
00536 {
00537     int error;
00538     QString errortext;
00539     Slave *slave = Slave::createSlave(protocol, url, error, errortext);
00540     if (slave) {
00541         scheduler()->connect(slave, SIGNAL(slaveDied(KIO::Slave *)),
00542                              SLOT(slotSlaveDied(KIO::Slave *)));
00543         scheduler()->connect(slave, SIGNAL(slaveStatus(pid_t,const QByteArray&,const QString &, bool)),
00544                              SLOT(slotSlaveStatus(pid_t,const QByteArray&, const QString &, bool)));
00545     } else {
00546         kError() << "couldn't create slave:" << errortext;
00547         if (job) {
00548             job->slotError(error, errortext);
00549         }
00550     }
00551     return slave;
00552 }
00553 
00554 bool ProtoQueue::removeSlave (KIO::Slave *slave)
00555 {
00556     const bool removedConnected = m_connectedSlaveQueue.removeSlave(slave);
00557     const bool removedUnconnected = m_slaveKeeper.removeSlave(slave);
00558     Q_ASSERT(!(removedConnected && removedUnconnected));
00559     return removedConnected || removedUnconnected;
00560 }
00561 
00562 QList<Slave *> ProtoQueue::allSlaves() const
00563 {
00564     QList<Slave *> ret(m_slaveKeeper.allSlaves());
00565     Q_FOREACH (const HostQueue &hq, m_queuesByHostname) {
00566         ret.append(hq.allSlaves());
00567     }
00568     ret.append(m_connectedSlaveQueue.allSlaves());
00569     return ret;
00570 }
00571 
00572 //private slot
00573 void ProtoQueue::startAJob()
00574 {
00575     ensureNoDuplicates(&m_queuesBySerial);
00576     verifyRunningJobsCount(&m_queuesByHostname, m_runningJobsCount);
00577 
00578 #ifdef SCHEDULER_DEBUG
00579     kDebug(7006) << "m_runningJobsCount:" << m_runningJobsCount;
00580     Q_FOREACH (const HostQueue &hq, m_queuesByHostname) {
00581         Q_FOREACH (SimpleJob *job, hq.runningJobs()) {
00582             kDebug(7006) << SimpleJobPrivate::get(job)->m_url;
00583         }
00584     }
00585 #endif
00586     if (m_runningJobsCount >= m_maxConnectionsTotal) {
00587 #ifdef SCHEDULER_DEBUG
00588         kDebug(7006) << "not starting any jobs because maxConnectionsTotal has been reached.";
00589 #endif
00590         return;
00591     }
00592 
00593     QMap<int, HostQueue *>::iterator first = m_queuesBySerial.begin();
00594     if (first != m_queuesBySerial.end()) {
00595         // pick a job and maintain the queue invariant: lower serials first
00596         const int prevLowestSerial = first.key();
00597         HostQueue *hq = first.value();
00598         Q_ASSERT(prevLowestSerial == hq->lowestSerial());
00599         // the following assertions should hold due to queueJob(), nextStartingJob() and
00600         // removeJob() being correct
00601         Q_ASSERT(hq->runningJobsCount() < m_maxConnectionsPerHost);
00602         SimpleJob *startingJob = hq->nextStartingJob();
00603         Q_ASSERT(hq->runningJobsCount() <= m_maxConnectionsPerHost);
00604         Q_ASSERT(hq->lowestSerial() != prevLowestSerial);
00605 
00606         m_queuesBySerial.erase(first);
00607         // we've increased hq's runningJobsCount() by calling nexStartingJob()
00608         // so we need to check again.
00609         if (!hq->isQueueEmpty() && hq->runningJobsCount() < m_maxConnectionsPerHost) {
00610             m_queuesBySerial.insert(hq->lowestSerial(), hq);
00611         }
00612 
00613         // always increase m_runningJobsCount because it's correct if there is a slave and if there
00614         // is no slave, removeJob() will balance the number again. removeJob() would decrease the
00615         // number too much otherwise.
00616         // Note that createSlave() can call slotError() on a job which in turn calls removeJob(),
00617         // so increase the count here already.
00618         m_runningJobsCount++;
00619 
00620         bool isNewSlave = false;
00621         Slave *slave = m_slaveKeeper.takeSlaveForJob(startingJob);
00622         SimpleJobPrivate *jobPriv = SimpleJobPrivate::get(startingJob);
00623         if (!slave) {
00624             isNewSlave = true;
00625             slave = createSlave(jobPriv->m_protocol, startingJob, jobPriv->m_url);
00626         }
00627 
00628         if (slave) {
00629             jobPriv->m_slave = slave;
00630             setupSlave(slave, jobPriv->m_url, jobPriv->m_protocol, jobPriv->m_proxy, isNewSlave);
00631             startJob(startingJob, slave);
00632         } else {
00633             // dispose of our records about the job and mark the job as unknown
00634             // (to prevent crashes later)
00635             // note that the job's slotError() can have called removeJob() first, so check that
00636             // it's not a ghost job with null serial already.
00637             if (jobPriv->m_schedSerial) {
00638                 removeJob(startingJob);
00639                 jobPriv->m_schedSerial = 0;
00640             }
00641         }
00642     } else {
00643 #ifdef SCHEDULER_DEBUG
00644         kDebug(7006) << "not starting any jobs because there is no queued job.";
00645 #endif
00646     }
00647 
00648     if (!m_queuesBySerial.isEmpty()) {
00649         m_startJobTimer.start();
00650     }
00651 }
00652 
00653 
00654 
00655 class KIO::SchedulerPrivate
00656 {
00657 public:
00658     SchedulerPrivate()
00659      : q(new Scheduler()),
00660        m_slaveOnHold(0),
00661        m_checkOnHold(true) // !! Always check with KLauncher for the first request
00662     {
00663     }
00664 
00665     ~SchedulerPrivate()
00666     {
00667         delete q;
00668         q = 0;
00669         Q_FOREACH (ProtoQueue *p, m_protocols) {
00670             Q_FOREACH (Slave *slave, p->allSlaves()) {
00671                 slave->kill();
00672             }
00673             p->deleteLater();
00674         }
00675     }
00676     Scheduler *q;
00677 
00678     Slave *m_slaveOnHold;
00679     KUrl m_urlOnHold;
00680     bool m_checkOnHold;
00681 
00682     SessionData sessionData;
00683     QMap<QObject *,WId> m_windowList;
00684 
00685     void doJob(SimpleJob *job);
00686 #ifndef KDE_NO_DEPRECATED
00687     void scheduleJob(SimpleJob *job);
00688 #endif
00689     void setJobPriority(SimpleJob *job, int priority);
00690     void cancelJob(SimpleJob *job);
00691     void jobFinished(KIO::SimpleJob *job, KIO::Slave *slave);
00692     void putSlaveOnHold(KIO::SimpleJob *job, const KUrl &url);
00693     void removeSlaveOnHold();
00694     Slave *getConnectedSlave(const KUrl &url, const KIO::MetaData &metaData);
00695     bool assignJobToSlave(KIO::Slave *slave, KIO::SimpleJob *job);
00696     bool disconnectSlave(KIO::Slave *slave);
00697     void checkSlaveOnHold(bool b);
00698     void publishSlaveOnHold();
00699     Slave *heldSlaveForJob(KIO::SimpleJob *job);
00700     void registerWindow(QWidget *wid);
00701 
00702     MetaData metaDataFor(const QString &protocol, const QString &proxy, const KUrl &url);
00703     void setupSlave(KIO::Slave *slave, const KUrl &url, const QString &protocol,
00704                     const QString &proxy, bool newSlave, const KIO::MetaData *config = 0);
00705 
00706     void slotSlaveDied(KIO::Slave *slave);
00707     void slotSlaveStatus(pid_t pid, const QByteArray &protocol,
00708                          const QString &host, bool connected);
00709 
00710     void slotReparseSlaveConfiguration(const QString &, const QDBusMessage&);
00711 
00712     void slotSlaveConnected();
00713     void slotSlaveError(int error, const QString &errorMsg);
00714     void slotUnregisterWindow(QObject *);
00715 
00716     ProtoQueue *protoQ(const QString &p)
00717     {
00718         ProtoQueue *pq = m_protocols.value(p, 0);
00719         if (!pq) {
00720             kDebug(7006) << "creating ProtoQueue instance for" << p;
00721             pq = new ProtoQueue(this, KProtocolInfo::maxSlaves(p),
00722                                 KProtocolInfo::maxSlavesPerHost(p));
00723             m_protocols.insert(p, pq);
00724         }
00725         return pq;
00726     }
00727 private:
00728     QHash<QString, ProtoQueue *> m_protocols;
00729 };
00730 
00731 
00732 K_GLOBAL_STATIC(SchedulerPrivate, schedulerPrivate)
00733 
00734 Scheduler *Scheduler::self()
00735 {
00736     return schedulerPrivate->q;
00737 }
00738 
00739 //static
00740 Scheduler *scheduler()
00741 {
00742     return schedulerPrivate->q;
00743 }
00744 
00745 //static
00746 Slave *heldSlaveForJob(SimpleJob *job)
00747 {
00748     return schedulerPrivate->heldSlaveForJob(job);
00749 }
00750 
00751 
00752 Scheduler::Scheduler()
00753  : d(0)
00754 {
00755     setObjectName( "scheduler" );
00756 
00757     const QString dbusPath = "/KIO/Scheduler";
00758     const QString dbusInterface = "org.kde.KIO.Scheduler";
00759     QDBusConnection dbus = QDBusConnection::sessionBus();
00760     dbus.registerObject( "/KIO/Scheduler", this, QDBusConnection::ExportScriptableSlots |
00761                                                  QDBusConnection::ExportScriptableSignals );
00762     dbus.connect(QString(), dbusPath, dbusInterface, "reparseSlaveConfiguration",
00763                  this, SLOT(slotReparseSlaveConfiguration(QString,QDBusMessage)));
00764 }
00765 
00766 Scheduler::~Scheduler()
00767 {
00768 }
00769 
00770 void Scheduler::doJob(SimpleJob *job)
00771 {
00772     schedulerPrivate->doJob(job);
00773 }
00774 
00775 #ifndef KDE_NO_DEPRECATED
00776 void Scheduler::scheduleJob(SimpleJob *job)
00777 {
00778     schedulerPrivate->scheduleJob(job);
00779 }
00780 #endif
00781 
00782 void Scheduler::setJobPriority(SimpleJob *job, int priority)
00783 {
00784     schedulerPrivate->setJobPriority(job, priority);
00785 }
00786 
00787 void Scheduler::cancelJob(SimpleJob *job)
00788 {
00789     schedulerPrivate->cancelJob(job);
00790 }
00791 
00792 void Scheduler::jobFinished(KIO::SimpleJob *job, KIO::Slave *slave)
00793 {
00794     schedulerPrivate->jobFinished(job, slave);
00795 }
00796 
00797 void Scheduler::putSlaveOnHold(KIO::SimpleJob *job, const KUrl &url)
00798 {
00799     schedulerPrivate->putSlaveOnHold(job, url);
00800 }
00801 
00802 void Scheduler::removeSlaveOnHold()
00803 {
00804     schedulerPrivate->removeSlaveOnHold();
00805 }
00806 
00807 void Scheduler::publishSlaveOnHold()
00808 {
00809     schedulerPrivate->publishSlaveOnHold();
00810 }
00811 
00812 KIO::Slave *Scheduler::getConnectedSlave(const KUrl &url,
00813         const KIO::MetaData &config )
00814 {
00815     return schedulerPrivate->getConnectedSlave(url, config);
00816 }
00817 
00818 bool Scheduler::assignJobToSlave(KIO::Slave *slave, KIO::SimpleJob *job)
00819 {
00820     return schedulerPrivate->assignJobToSlave(slave, job);
00821 }
00822 
00823 bool Scheduler::disconnectSlave(KIO::Slave *slave)
00824 {
00825     return schedulerPrivate->disconnectSlave(slave);
00826 }
00827 
00828 void Scheduler::registerWindow(QWidget *wid)
00829 {
00830     schedulerPrivate->registerWindow(wid);
00831 }
00832 
00833 void Scheduler::unregisterWindow(QObject *wid)
00834 {
00835     schedulerPrivate->slotUnregisterWindow(wid);
00836 }
00837 
00838 bool Scheduler::connect( const char *signal, const QObject *receiver,
00839                          const char *member)
00840 {
00841     return QObject::connect(self(), signal, receiver, member);
00842 }
00843 
00844 bool Scheduler::connect( const QObject* sender, const char* signal,
00845                          const QObject* receiver, const char* member )
00846 {
00847     return QObject::connect(sender, signal, receiver, member);
00848 }
00849 
00850 bool Scheduler::disconnect( const QObject* sender, const char* signal,
00851                             const QObject* receiver, const char* member )
00852 {
00853     return QObject::disconnect(sender, signal, receiver, member);
00854 }
00855 
00856 bool Scheduler::connect( const QObject *sender, const char *signal,
00857                          const char *member )
00858 {
00859     return QObject::connect(sender, signal, member);
00860 }
00861 
00862 void Scheduler::checkSlaveOnHold(bool b)
00863 {
00864     schedulerPrivate->checkSlaveOnHold(b);
00865 }
00866 
00867 void Scheduler::emitReparseSlaveConfiguration()
00868 {
00869     emit self()->reparseSlaveConfiguration( QString() );
00870 
00871     // Do it immediately in this process, otherwise we might send a request before reparsing
00872     // (e.g. when changing useragent in the plugin)
00873     schedulerPrivate->slotReparseSlaveConfiguration(QString(), QDBusMessage());
00874 }
00875 
00876 
00877 void SchedulerPrivate::slotReparseSlaveConfiguration(const QString &proto, const QDBusMessage& msg)
00878 {
00879     if (QDBusConnection::sessionBus().baseService() == msg.service()) {
00880         kDebug(7006) << "Ignoring signal sent by myself";
00881         return;
00882     }
00883     kDebug(7006) << "proto=" << proto;
00884     KProtocolManager::reparseConfiguration();
00885     SlaveConfig::self()->reset();
00886     sessionData.reset();
00887     NetRC::self()->reload();
00888 
00889     QHash<QString, ProtoQueue *>::ConstIterator it = proto.isEmpty() ? m_protocols.constBegin() :
00890                                                                        m_protocols.constFind(proto);
00891     // not found?
00892     if (it == m_protocols.constEnd()) {
00893         return;
00894     }
00895     QHash<QString, ProtoQueue *>::ConstIterator endIt = proto.isEmpty() ? m_protocols.constEnd() :
00896                                                                           it + 1;
00897     for (; it != endIt; ++it) {
00898         Q_FOREACH(Slave *slave, (*it)->allSlaves()) {
00899             slave->send(CMD_REPARSECONFIGURATION);
00900             slave->resetHost();
00901         }
00902     }
00903 }
00904 
00905 static bool mayReturnContent(int cmd, const QString& protocol)
00906 {
00907     if (cmd == CMD_GET)
00908         return true;
00909 
00910     if (cmd == CMD_SPECIAL && protocol.startsWith(QLatin1String("http"), Qt::CaseInsensitive))
00911         return true;
00912 
00913     return false;
00914 }
00915 
00916 void SchedulerPrivate::doJob(SimpleJob *job)
00917 {
00918     kDebug(7006) << job;
00919     if (QThread::currentThread() != QCoreApplication::instance()->thread()) {
00920         kWarning(7006) << "KIO is not thread-safe.";
00921     }
00922 
00923     KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job);
00924     jobPriv->m_protocol = KProtocolManager::slaveProtocol(job->url(), jobPriv->m_proxy);
00925 
00926     if (mayReturnContent(jobCommand(job), jobPriv->m_protocol)) {
00927        jobPriv->m_checkOnHold = m_checkOnHold;
00928        m_checkOnHold = false;
00929     }
00930 
00931     ProtoQueue *proto = protoQ(jobPriv->m_protocol);
00932     proto->queueJob(job);
00933 }
00934 
00935 #ifndef KDE_NO_DEPRECATED
00936 void SchedulerPrivate::scheduleJob(SimpleJob *job)
00937 {
00938     kDebug(7006) << job;
00939     setJobPriority(job, 1);
00940 }
00941 #endif
00942 
00943 void SchedulerPrivate::setJobPriority(SimpleJob *job, int priority)
00944 {
00945     kDebug(7006) << job << priority;
00946     ProtoQueue *proto = protoQ(SimpleJobPrivate::get(job)->m_protocol);
00947     proto->changeJobPriority(job, priority);
00948 }
00949 
00950 void SchedulerPrivate::cancelJob(SimpleJob *job)
00951 {
00952     // this method is called all over the place in job.cpp, so just do this check here to avoid
00953     // much boilerplate in job code.
00954     if (SimpleJobPrivate::get(job)->m_schedSerial == 0) {
00955         //kDebug(7006) << "Doing nothing because I don't know job" << job;
00956         return;
00957     }
00958     Slave *slave = jobSlave(job);
00959     kDebug(7006) << job << slave;
00960     if (slave) {
00961         kDebug(7006) << "Scheduler: killing slave " << slave->slave_pid();
00962         slave->kill();
00963     }
00964     jobFinished(job, slave);
00965 }
00966 
00967 void SchedulerPrivate::jobFinished(SimpleJob *job, Slave *slave)
00968 {
00969     kDebug(7006) << job << slave;
00970     if (QThread::currentThread() != QCoreApplication::instance()->thread()) {
00971         kWarning(7006) << "KIO is not thread-safe.";
00972     }
00973 
00974     KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job);
00975 
00976     // Preserve all internal meta-data so they can be sent back to the
00977     // ioslaves as needed...
00978     const KUrl jobUrl = job->url();
00979     QMapIterator<QString, QString> it (jobPriv->m_internalMetaData);
00980     while (it.hasNext()) {
00981         it.next();
00982         if (it.key().startsWith(QLatin1String("{internal~currenthost}"), Qt::CaseInsensitive)) {
00983             SlaveConfig::self()->setConfigData(jobUrl.protocol(), jobUrl.host(), it.key().mid(22), it.value());
00984         } else if (it.key().startsWith(QLatin1String("{internal~allhosts}"), Qt::CaseInsensitive)) {
00985             SlaveConfig::self()->setConfigData(jobUrl.protocol(), QString(), it.key().mid(19), it.value());
00986         }
00987     }
00988 
00989     // make sure that we knew about the job!
00990     Q_ASSERT(jobPriv->m_schedSerial);
00991 
00992     ProtoQueue *pq = m_protocols.value(jobPriv->m_protocol);
00993     pq->removeJob(job);
00994     if (slave) {
00995         // If we have internal meta-data, tell existing ioslaves to reload
00996         // their configuration.
00997         if (jobPriv->m_internalMetaData.count()) {
00998             kDebug(7006) << "Updating ioslaves with new internal metadata information";
00999             ProtoQueue * queue = m_protocols.value(slave->protocol());
01000             if (queue) {
01001                 QListIterator<Slave*> it (queue->allSlaves());
01002                 while (it.hasNext()) {
01003                     Slave* runningSlave = it.next();
01004                     if (slave->host() == runningSlave->host()) {
01005                         slave->setConfig(metaDataFor(slave->protocol(), jobPriv->m_proxy, jobUrl));
01006                         kDebug(7006) << "Updated configuration of" << slave->protocol()
01007                                      << "ioslave, pid=" << slave->slave_pid();
01008                     }
01009                 }
01010             }
01011         }
01012         slave->setJob(0);
01013         slave->disconnect(job);
01014     }
01015     jobPriv->m_schedSerial = 0; // this marks the job as unscheduled again
01016     jobPriv->m_slave = 0;
01017     // Clear the values in the internal metadata container since they have
01018     // already been taken care of above...
01019     jobPriv->m_internalMetaData.clear();
01020 }
01021 
01022 // static
01023 void setupSlave(KIO::Slave *slave, const KUrl &url, const QString &protocol,
01024                 const QString &proxy , bool newSlave, const KIO::MetaData *config)
01025 {
01026     schedulerPrivate->setupSlave(slave, url, protocol, proxy, newSlave, config);
01027 }
01028 
01029 MetaData SchedulerPrivate::metaDataFor(const QString &protocol, const QString &proxy, const KUrl &url)
01030 {
01031     const QString host = url.host();
01032     MetaData configData = SlaveConfig::self()->configData(protocol, host);
01033     sessionData.configDataFor( configData, protocol, host );
01034     configData["UseProxy"] = proxy;
01035 
01036     if ( configData.contains("EnableAutoLogin") &&
01037          configData.value("EnableAutoLogin").compare("true", Qt::CaseInsensitive) == 0 )
01038     {
01039         NetRC::AutoLogin l;
01040         l.login = url.user();
01041         bool usern = (protocol == "ftp");
01042         if ( NetRC::self()->lookup( url, l, usern) )
01043         {
01044             configData["autoLoginUser"] = l.login;
01045             configData["autoLoginPass"] = l.password;
01046             if ( usern )
01047             {
01048                 QString macdef;
01049                 QMap<QString, QStringList>::ConstIterator it = l.macdef.constBegin();
01050                 for ( ; it != l.macdef.constEnd(); ++it )
01051                     macdef += it.key() + '\\' + it.value().join( "\\" ) + '\n';
01052                 configData["autoLoginMacro"] = macdef;
01053             }
01054         }
01055     }
01056 
01057     return configData;
01058 }
01059 
01060 void SchedulerPrivate::setupSlave(KIO::Slave *slave, const KUrl &url, const QString &protocol,
01061                                   const QString &proxy , bool newSlave, const KIO::MetaData *config)
01062 {
01063     int port = url.port();
01064     if ( port == -1 ) // no port is -1 in QUrl, but in kde3 we used 0 and the kioslaves assume that.
01065         port = 0;
01066     const QString host = url.host();
01067     const QString user = url.user();
01068     const QString passwd = url.pass();
01069 
01070     if (newSlave || slave->host() != host || slave->port() != port ||
01071         slave->user() != user || slave->passwd() != passwd) {
01072 
01073         MetaData configData = metaDataFor(protocol, proxy, url);
01074         if (config)
01075            configData += *config;
01076 
01077         slave->setConfig(configData);
01078         slave->setProtocol(url.protocol());
01079         slave->setHost(host, port, user, passwd);
01080     }
01081 }
01082 
01083 
01084 void SchedulerPrivate::slotSlaveStatus(pid_t, const QByteArray&, const QString &, bool)
01085 {
01086 }
01087 
01088 
01089 void SchedulerPrivate::slotSlaveDied(KIO::Slave *slave)
01090 {
01091     kDebug(7006) << slave;
01092     Q_ASSERT(slave);
01093     Q_ASSERT(!slave->isAlive());
01094     ProtoQueue *pq = m_protocols.value(slave->protocol());
01095     if (slave->job()) {
01096         pq->removeJob(slave->job());
01097     }
01098     // in case this was a connected slave...
01099     pq->removeSlave(slave);
01100     if (slave == m_slaveOnHold) {
01101        m_slaveOnHold = 0;
01102        m_urlOnHold.clear();
01103     }
01104     slave->deref(); // Delete slave
01105 }
01106 
01107 void SchedulerPrivate::putSlaveOnHold(KIO::SimpleJob *job, const KUrl &url)
01108 {
01109     Slave *slave = jobSlave(job);
01110     kDebug(7006) << job << url << slave;
01111     slave->disconnect(job);
01112     // prevent the fake death of the slave from trying to kill the job again;
01113     // cf. Slave::hold(const KUrl &url) called in SchedulerPrivate::publishSlaveOnHold().
01114     slave->setJob(0);
01115     SimpleJobPrivate::get(job)->m_slave = 0;
01116 
01117     if (m_slaveOnHold) {
01118         m_slaveOnHold->kill();
01119     }
01120     m_slaveOnHold = slave;
01121     m_urlOnHold = url;
01122     m_slaveOnHold->suspend();
01123 }
01124 
01125 void SchedulerPrivate::publishSlaveOnHold()
01126 {
01127     kDebug(7006) << m_slaveOnHold;
01128     if (!m_slaveOnHold)
01129        return;
01130 
01131     m_slaveOnHold->hold(m_urlOnHold);
01132 }
01133 
01134 Slave *SchedulerPrivate::heldSlaveForJob(SimpleJob *job)
01135 {
01136     Slave *slave = 0;
01137     KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job);
01138 
01139     if (jobPriv->m_checkOnHold) {
01140         slave = Slave::holdSlave(jobPriv->m_protocol, job->url());
01141     }
01142 
01143     if (!slave && m_slaveOnHold) {
01144         // Make sure that the job wants to do a GET or a POST, and with no offset
01145         const int cmd = jobPriv->m_command;
01146         bool canJobReuse = cmd == CMD_GET;
01147 
01148         if (KIO::TransferJob *tJob = qobject_cast<KIO::TransferJob *>(job)) {
01149             canJobReuse = cmd == CMD_GET || cmd == CMD_SPECIAL;
01150             if (canJobReuse) {
01151                 KIO::MetaData outgoing = tJob->outgoingMetaData();
01152                 const QString resume = outgoing.value("resume");
01153                 kDebug(7006) << "Resume metadata is" << resume;
01154                 canJobReuse = resume.isEmpty() || resume == "0";
01155             }
01156         }
01157 
01158         if (job->url() == m_urlOnHold) {
01159             if (canJobReuse) {
01160                 kDebug(7006) << "HOLD: Reusing held slave for" << m_urlOnHold;
01161                 slave = m_slaveOnHold;
01162             } else {
01163                  kDebug(7006) << "HOLD: Discarding held slave (" << m_urlOnHold << ")";
01164                  m_slaveOnHold->kill();
01165             }
01166             m_slaveOnHold = 0;
01167             m_urlOnHold.clear();
01168         }
01169     }
01170     return slave;
01171 }
01172 
01173 void SchedulerPrivate::removeSlaveOnHold()
01174 {
01175     kDebug(7006) << m_slaveOnHold;
01176     if (m_slaveOnHold) {
01177         m_slaveOnHold->kill();
01178     }
01179     m_slaveOnHold = 0;
01180     m_urlOnHold.clear();
01181 }
01182 
01183 Slave *SchedulerPrivate::getConnectedSlave(const KUrl &url, const KIO::MetaData &config)
01184 {
01185     QString proxy;
01186     QString protocol = KProtocolManager::slaveProtocol(url, proxy);
01187     ProtoQueue *pq = protoQ(protocol);
01188 
01189     Slave *slave = pq->createSlave(protocol, /* job */0, url);
01190     if (slave) {
01191         setupSlave(slave, url, protocol, proxy, true, &config);
01192         pq->m_connectedSlaveQueue.addSlave(slave);
01193 
01194         slave->send( CMD_CONNECT );
01195         q->connect(slave, SIGNAL(connected()),
01196                    SLOT(slotSlaveConnected()));
01197         q->connect(slave, SIGNAL(error(int, const QString &)),
01198                    SLOT(slotSlaveError(int, const QString &)));
01199     }
01200     kDebug(7006) << url << slave;
01201     return slave;
01202 }
01203 
01204 
01205 void SchedulerPrivate::slotSlaveConnected()
01206 {
01207     kDebug(7006);
01208     Slave *slave = static_cast<Slave *>(q->sender());
01209     slave->setConnected(true);
01210     q->disconnect(slave, SIGNAL(connected()), q, SLOT(slotSlaveConnected()));
01211     emit q->slaveConnected(slave);
01212 }
01213 
01214 void SchedulerPrivate::slotSlaveError(int errorNr, const QString &errorMsg)
01215 {
01216     Slave *slave = static_cast<Slave *>(q->sender());
01217     kDebug(7006) << slave << errorNr << errorMsg;
01218     ProtoQueue *pq = protoQ(slave->protocol());
01219     if (!slave->isConnected() || pq->m_connectedSlaveQueue.isIdle(slave)) {
01220         // Only forward to application if slave is idle or still connecting.
01221         // ### KDE5: can we remove this apparently arbitrary behavior and just always emit SlaveError?
01222         emit q->slaveError(slave, errorNr, errorMsg);
01223     }
01224 }
01225 
01226 bool SchedulerPrivate::assignJobToSlave(KIO::Slave *slave, SimpleJob *job)
01227 {
01228     kDebug(7006) << slave << job;
01229     // KDE5: queueing of jobs can probably be removed, it provides very little benefit
01230     ProtoQueue *pq = m_protocols.value(slave->protocol());
01231     pq->removeJob(job);
01232     return pq->m_connectedSlaveQueue.queueJob(job, slave);
01233 }
01234 
01235 bool SchedulerPrivate::disconnectSlave(KIO::Slave *slave)
01236 {
01237     kDebug(7006) << slave;
01238     ProtoQueue *pq = m_protocols.value(slave->protocol());
01239     return pq->m_connectedSlaveQueue.removeSlave(slave);
01240 }
01241 
01242 void SchedulerPrivate::checkSlaveOnHold(bool b)
01243 {
01244     kDebug(7006) << b;
01245     m_checkOnHold = b;
01246 }
01247 
01248 void SchedulerPrivate::registerWindow(QWidget *wid)
01249 {
01250    if (!wid)
01251       return;
01252 
01253    QWidget* window = wid->window();
01254 
01255    QObject *obj = static_cast<QObject *>(window);
01256    if (!m_windowList.contains(obj))
01257    {
01258       // We must store the window Id because by the time
01259       // the destroyed signal is emitted we can no longer
01260       // access QWidget::winId() (already destructed)
01261       WId windowId = window->winId();
01262       m_windowList.insert(obj, windowId);
01263       q->connect(window, SIGNAL(destroyed(QObject *)),
01264                  SLOT(slotUnregisterWindow(QObject*)));
01265       QDBusInterface("org.kde.kded", "/kded", "org.kde.kded").
01266           call(QDBus::NoBlock, "registerWindowId", qlonglong(windowId));
01267    }
01268 }
01269 
01270 void SchedulerPrivate::slotUnregisterWindow(QObject *obj)
01271 {
01272    if (!obj)
01273       return;
01274 
01275    QMap<QObject *, WId>::Iterator it = m_windowList.find(obj);
01276    if (it == m_windowList.end())
01277       return;
01278    WId windowId = it.value();
01279    q->disconnect(it.key(), SIGNAL(destroyed(QObject *)),
01280                  q, SLOT(slotUnregisterWindow(QObject*)));
01281    m_windowList.erase( it );
01282    QDBusInterface("org.kde.kded", "/kded", "org.kde.kded").
01283        call(QDBus::NoBlock, "unregisterWindowId", qlonglong(windowId));
01284 }
01285 
01286 #include "scheduler.moc"
01287 #include "scheduler_p.moc"

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal