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

KDE's Doxygen guidelines are available online.

KIO

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

kdelibs-4.8.3 API Reference

Skip menu "kdelibs-4.8.3 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • 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
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal