• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.9.5 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         // Set the parent widget the slave should use to display message boxes.
00540         if (job && job->ui()) {
00541             slave->setWindow(job->ui()->window());
00542         }
00543         scheduler()->connect(slave, SIGNAL(slaveDied(KIO::Slave*)),
00544                              SLOT(slotSlaveDied(KIO::Slave*)));
00545         scheduler()->connect(slave, SIGNAL(slaveStatus(pid_t,QByteArray,QString,bool)),
00546                              SLOT(slotSlaveStatus(pid_t,QByteArray,QString,bool)));
00547     } else {
00548         kError() << "couldn't create slave:" << errortext;
00549         if (job) {
00550             job->slotError(error, errortext);
00551         }
00552     }
00553     return slave;
00554 }
00555 
00556 bool ProtoQueue::removeSlave (KIO::Slave *slave)
00557 {
00558     const bool removedConnected = m_connectedSlaveQueue.removeSlave(slave);
00559     const bool removedUnconnected = m_slaveKeeper.removeSlave(slave);
00560     Q_ASSERT(!(removedConnected && removedUnconnected));
00561     return removedConnected || removedUnconnected;
00562 }
00563 
00564 QList<Slave *> ProtoQueue::allSlaves() const
00565 {
00566     QList<Slave *> ret(m_slaveKeeper.allSlaves());
00567     Q_FOREACH (const HostQueue &hq, m_queuesByHostname) {
00568         ret.append(hq.allSlaves());
00569     }
00570     ret.append(m_connectedSlaveQueue.allSlaves());
00571     return ret;
00572 }
00573 
00574 //private slot
00575 void ProtoQueue::startAJob()
00576 {
00577     ensureNoDuplicates(&m_queuesBySerial);
00578     verifyRunningJobsCount(&m_queuesByHostname, m_runningJobsCount);
00579 
00580 #ifdef SCHEDULER_DEBUG
00581     kDebug(7006) << "m_runningJobsCount:" << m_runningJobsCount;
00582     Q_FOREACH (const HostQueue &hq, m_queuesByHostname) {
00583         Q_FOREACH (SimpleJob *job, hq.runningJobs()) {
00584             kDebug(7006) << SimpleJobPrivate::get(job)->m_url;
00585         }
00586     }
00587 #endif
00588     if (m_runningJobsCount >= m_maxConnectionsTotal) {
00589 #ifdef SCHEDULER_DEBUG
00590         kDebug(7006) << "not starting any jobs because maxConnectionsTotal has been reached.";
00591 #endif
00592         return;
00593     }
00594 
00595     QMap<int, HostQueue *>::iterator first = m_queuesBySerial.begin();
00596     if (first != m_queuesBySerial.end()) {
00597         // pick a job and maintain the queue invariant: lower serials first
00598         HostQueue *hq = first.value();
00599         const int prevLowestSerial = first.key();
00600         Q_UNUSED(prevLowestSerial);
00601         Q_ASSERT(hq->lowestSerial() == prevLowestSerial);
00602         // the following assertions should hold due to queueJob(), takeFirstInQueue() and
00603         // removeJob() being correct
00604         Q_ASSERT(hq->runningJobsCount() < m_maxConnectionsPerHost);
00605         SimpleJob *startingJob = hq->takeFirstInQueue();
00606         Q_ASSERT(hq->runningJobsCount() <= m_maxConnectionsPerHost);
00607         Q_ASSERT(hq->lowestSerial() != prevLowestSerial);
00608 
00609         m_queuesBySerial.erase(first);
00610         // we've increased hq's runningJobsCount() by calling nexStartingJob()
00611         // so we need to check again.
00612         if (!hq->isQueueEmpty() && hq->runningJobsCount() < m_maxConnectionsPerHost) {
00613             m_queuesBySerial.insert(hq->lowestSerial(), hq);
00614         }
00615 
00616         // always increase m_runningJobsCount because it's correct if there is a slave and if there
00617         // is no slave, removeJob() will balance the number again. removeJob() would decrease the
00618         // number too much otherwise.
00619         // Note that createSlave() can call slotError() on a job which in turn calls removeJob(),
00620         // so increase the count here already.
00621         m_runningJobsCount++;
00622 
00623         bool isNewSlave = false;
00624         Slave *slave = m_slaveKeeper.takeSlaveForJob(startingJob);
00625         SimpleJobPrivate *jobPriv = SimpleJobPrivate::get(startingJob);
00626         if (!slave) {
00627             isNewSlave = true;
00628             slave = createSlave(jobPriv->m_protocol, startingJob, jobPriv->m_url);
00629         }
00630 
00631         if (slave) {
00632             jobPriv->m_slave = slave;
00633             setupSlave(slave, jobPriv->m_url, jobPriv->m_protocol, jobPriv->m_proxyList, isNewSlave);
00634             startJob(startingJob, slave);
00635         } else {
00636             // dispose of our records about the job and mark the job as unknown
00637             // (to prevent crashes later)
00638             // note that the job's slotError() can have called removeJob() first, so check that
00639             // it's not a ghost job with null serial already.
00640             if (jobPriv->m_schedSerial) {
00641                 removeJob(startingJob);
00642                 jobPriv->m_schedSerial = 0;
00643             }
00644         }
00645     } else {
00646 #ifdef SCHEDULER_DEBUG
00647         kDebug(7006) << "not starting any jobs because there is no queued job.";
00648 #endif
00649     }
00650 
00651     if (!m_queuesBySerial.isEmpty()) {
00652         m_startJobTimer.start();
00653     }
00654 }
00655 
00656 
00657 
00658 class KIO::SchedulerPrivate
00659 {
00660 public:
00661     SchedulerPrivate()
00662      : q(new Scheduler()),
00663        m_slaveOnHold(0),
00664        m_checkOnHold(true), // !! Always check with KLauncher for the first request
00665        m_ignoreConfigReparse(false)
00666     {
00667     }
00668 
00669     ~SchedulerPrivate()
00670     {
00671         delete q;
00672         q = 0;
00673         Q_FOREACH (ProtoQueue *p, m_protocols) {
00674             Q_FOREACH (Slave *slave, p->allSlaves()) {
00675                 slave->kill();
00676             }
00677             p->deleteLater();
00678         }
00679     }
00680     Scheduler *q;
00681 
00682     Slave *m_slaveOnHold;
00683     KUrl m_urlOnHold;
00684     bool m_checkOnHold;
00685     bool m_ignoreConfigReparse;
00686 
00687     SessionData sessionData;
00688     QMap<QObject *,WId> m_windowList;
00689 
00690     void doJob(SimpleJob *job);
00691 #ifndef KDE_NO_DEPRECATED
00692     void scheduleJob(SimpleJob *job);
00693 #endif
00694     void setJobPriority(SimpleJob *job, int priority);
00695     void cancelJob(SimpleJob *job);
00696     void jobFinished(KIO::SimpleJob *job, KIO::Slave *slave);
00697     void putSlaveOnHold(KIO::SimpleJob *job, const KUrl &url);
00698     void removeSlaveOnHold();
00699     Slave *getConnectedSlave(const KUrl &url, const KIO::MetaData &metaData);
00700     bool assignJobToSlave(KIO::Slave *slave, KIO::SimpleJob *job);
00701     bool disconnectSlave(KIO::Slave *slave);
00702     void checkSlaveOnHold(bool b);
00703     void publishSlaveOnHold();
00704     Slave *heldSlaveForJob(KIO::SimpleJob *job);
00705     bool isSlaveOnHoldFor(const KUrl& url);
00706     void registerWindow(QWidget *wid);
00707     void updateInternalMetaData(SimpleJob* job);
00708 
00709     MetaData metaDataFor(const QString &protocol, const QStringList &proxyList, const KUrl &url);
00710     void setupSlave(KIO::Slave *slave, const KUrl &url, const QString &protocol,
00711                     const QStringList &proxyList, bool newSlave, const KIO::MetaData *config = 0);
00712 
00713     void slotSlaveDied(KIO::Slave *slave);
00714     void slotSlaveStatus(pid_t pid, const QByteArray &protocol,
00715                          const QString &host, bool connected);
00716 
00717     void slotReparseSlaveConfiguration(const QString &, const QDBusMessage&);
00718     void slotSlaveOnHoldListChanged();
00719 
00720     void slotSlaveConnected();
00721     void slotSlaveError(int error, const QString &errorMsg);
00722     void slotUnregisterWindow(QObject *);
00723 
00724     ProtoQueue *protoQ(const QString& protocol, const QString& host)
00725     {
00726         ProtoQueue *pq = m_protocols.value(protocol, 0);
00727         if (!pq) {
00728             kDebug(7006) << "creating ProtoQueue instance for" << protocol;
00729 
00730             const int maxSlaves = KProtocolInfo::maxSlaves(protocol);
00731             int maxSlavesPerHost = -1;
00732             if (!host.isEmpty()) {
00733                 bool ok = false;
00734                 const int value = SlaveConfig::self()->configData(protocol, host, QLatin1String("MaxConnections")).toInt(&ok);
00735                 if (ok)
00736                     maxSlavesPerHost = value;
00737             }
00738             if (maxSlavesPerHost == -1) {
00739                 maxSlavesPerHost = KProtocolInfo::maxSlavesPerHost(protocol);
00740             }
00741             // Never allow maxSlavesPerHost to exceed maxSlaves.
00742             pq = new ProtoQueue(this, maxSlaves, qMin(maxSlaves, maxSlavesPerHost));
00743             m_protocols.insert(protocol, pq);
00744         }
00745         return pq;
00746     }
00747 private:
00748     QHash<QString, ProtoQueue *> m_protocols;
00749 };
00750 
00751 
00752 K_GLOBAL_STATIC(SchedulerPrivate, schedulerPrivate)
00753 
00754 Scheduler *Scheduler::self()
00755 {
00756     return schedulerPrivate->q;
00757 }
00758 
00759 SchedulerPrivate *Scheduler::d_func()
00760 {
00761     return schedulerPrivate;
00762 }
00763 
00764 //static
00765 Scheduler *scheduler()
00766 {
00767     return schedulerPrivate->q;
00768 }
00769 
00770 //static
00771 Slave *heldSlaveForJob(SimpleJob *job)
00772 {
00773     return schedulerPrivate->heldSlaveForJob(job);
00774 }
00775 
00776 
00777 Scheduler::Scheduler()
00778  : removeMe(0)
00779 {
00780     setObjectName( "scheduler" );
00781 
00782     const QString dbusPath = "/KIO/Scheduler";
00783     const QString dbusInterface = "org.kde.KIO.Scheduler";
00784     QDBusConnection dbus = QDBusConnection::sessionBus();
00785     dbus.registerObject( "/KIO/Scheduler", this, QDBusConnection::ExportScriptableSlots |
00786                                                  QDBusConnection::ExportScriptableSignals );
00787     dbus.connect(QString(), dbusPath, dbusInterface, "reparseSlaveConfiguration",
00788                  this, SLOT(slotReparseSlaveConfiguration(QString,QDBusMessage)));
00789     dbus.connect(QString(), dbusPath, dbusInterface, "slaveOnHoldListChanged",
00790                  this, SLOT(slotSlaveOnHoldListChanged()));
00791 }
00792 
00793 Scheduler::~Scheduler()
00794 {
00795 }
00796 
00797 void Scheduler::doJob(SimpleJob *job)
00798 {
00799     schedulerPrivate->doJob(job);
00800 }
00801 
00802 #ifndef KDE_NO_DEPRECATED
00803 void Scheduler::scheduleJob(SimpleJob *job)
00804 {
00805     schedulerPrivate->scheduleJob(job);
00806 }
00807 #endif
00808 
00809 void Scheduler::setJobPriority(SimpleJob *job, int priority)
00810 {
00811     schedulerPrivate->setJobPriority(job, priority);
00812 }
00813 
00814 void Scheduler::cancelJob(SimpleJob *job)
00815 {
00816     schedulerPrivate->cancelJob(job);
00817 }
00818 
00819 void Scheduler::jobFinished(KIO::SimpleJob *job, KIO::Slave *slave)
00820 {
00821     schedulerPrivate->jobFinished(job, slave);
00822 }
00823 
00824 void Scheduler::putSlaveOnHold(KIO::SimpleJob *job, const KUrl &url)
00825 {
00826     schedulerPrivate->putSlaveOnHold(job, url);
00827 }
00828 
00829 void Scheduler::removeSlaveOnHold()
00830 {
00831     schedulerPrivate->removeSlaveOnHold();
00832 }
00833 
00834 void Scheduler::publishSlaveOnHold()
00835 {
00836     schedulerPrivate->publishSlaveOnHold();
00837 }
00838 
00839 bool Scheduler::isSlaveOnHoldFor(const KUrl& url)
00840 {
00841     return schedulerPrivate->isSlaveOnHoldFor(url);
00842 }
00843 
00844 void Scheduler::updateInternalMetaData(SimpleJob* job)
00845 {
00846     schedulerPrivate->updateInternalMetaData(job);
00847 }
00848 
00849 KIO::Slave *Scheduler::getConnectedSlave(const KUrl &url,
00850         const KIO::MetaData &config )
00851 {
00852     return schedulerPrivate->getConnectedSlave(url, config);
00853 }
00854 
00855 bool Scheduler::assignJobToSlave(KIO::Slave *slave, KIO::SimpleJob *job)
00856 {
00857     return schedulerPrivate->assignJobToSlave(slave, job);
00858 }
00859 
00860 bool Scheduler::disconnectSlave(KIO::Slave *slave)
00861 {
00862     return schedulerPrivate->disconnectSlave(slave);
00863 }
00864 
00865 void Scheduler::registerWindow(QWidget *wid)
00866 {
00867     schedulerPrivate->registerWindow(wid);
00868 }
00869 
00870 void Scheduler::unregisterWindow(QObject *wid)
00871 {
00872     schedulerPrivate->slotUnregisterWindow(wid);
00873 }
00874 
00875 bool Scheduler::connect( const char *signal, const QObject *receiver,
00876                          const char *member)
00877 {
00878     return QObject::connect(self(), signal, receiver, member);
00879 }
00880 
00881 bool Scheduler::connect( const QObject* sender, const char* signal,
00882                          const QObject* receiver, const char* member )
00883 {
00884     return QObject::connect(sender, signal, receiver, member);
00885 }
00886 
00887 bool Scheduler::disconnect( const QObject* sender, const char* signal,
00888                             const QObject* receiver, const char* member )
00889 {
00890     return QObject::disconnect(sender, signal, receiver, member);
00891 }
00892 
00893 bool Scheduler::connect( const QObject *sender, const char *signal,
00894                          const char *member )
00895 {
00896     return QObject::connect(sender, signal, member);
00897 }
00898 
00899 void Scheduler::checkSlaveOnHold(bool b)
00900 {
00901     schedulerPrivate->checkSlaveOnHold(b);
00902 }
00903 
00904 void Scheduler::emitReparseSlaveConfiguration()
00905 {
00906     // Do it immediately in this process, otherwise we might send a request before reparsing
00907     // (e.g. when changing useragent in the plugin)
00908     schedulerPrivate->slotReparseSlaveConfiguration(QString(), QDBusMessage());
00909 
00910     schedulerPrivate->m_ignoreConfigReparse = true;
00911     emit self()->reparseSlaveConfiguration( QString() );
00912 }
00913 
00914 
00915 void SchedulerPrivate::slotReparseSlaveConfiguration(const QString &proto, const QDBusMessage&)
00916 {
00917     if (m_ignoreConfigReparse) {
00918         kDebug(7006) << "Ignoring signal sent by myself";
00919         m_ignoreConfigReparse = false;
00920         return;
00921     }
00922 
00923     kDebug(7006) << "proto=" << proto;
00924     KProtocolManager::reparseConfiguration();
00925     SlaveConfig::self()->reset();
00926     sessionData.reset();
00927     NetRC::self()->reload();
00928 
00929     QHash<QString, ProtoQueue *>::ConstIterator it = proto.isEmpty() ? m_protocols.constBegin() :
00930                                                                        m_protocols.constFind(proto);
00931     // not found?
00932     if (it == m_protocols.constEnd()) {
00933         return;
00934     }
00935     QHash<QString, ProtoQueue *>::ConstIterator endIt = proto.isEmpty() ? m_protocols.constEnd() :
00936                                                                           it + 1;
00937     for (; it != endIt; ++it) {
00938         Q_FOREACH(Slave *slave, (*it)->allSlaves()) {
00939             slave->send(CMD_REPARSECONFIGURATION);
00940             slave->resetHost();
00941         }
00942     }
00943 }
00944 
00945 void SchedulerPrivate::slotSlaveOnHoldListChanged()
00946 {
00947     m_checkOnHold = true;
00948 }
00949 
00950 static bool mayReturnContent(int cmd, const QString& protocol)
00951 {
00952     if (cmd == CMD_GET)
00953         return true;
00954 
00955     if (cmd == CMD_MULTI_GET)
00956         return true;
00957 
00958     if (cmd == CMD_SPECIAL && protocol.startsWith(QLatin1String("http"), Qt::CaseInsensitive))
00959         return true;
00960 
00961     return false;
00962 }
00963 
00964 void SchedulerPrivate::doJob(SimpleJob *job)
00965 {
00966     kDebug(7006) << job;
00967     if (QThread::currentThread() != QCoreApplication::instance()->thread()) {
00968         kWarning(7006) << "KIO is not thread-safe.";
00969     }
00970 
00971     KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job);
00972     jobPriv->m_proxyList.clear();
00973     jobPriv->m_protocol = KProtocolManager::slaveProtocol(job->url(), jobPriv->m_proxyList);
00974 
00975     if (mayReturnContent(jobCommand(job), jobPriv->m_protocol)) {
00976        jobPriv->m_checkOnHold = m_checkOnHold;
00977        m_checkOnHold = false;
00978     }
00979 
00980     ProtoQueue *proto = protoQ(jobPriv->m_protocol, job->url().host());
00981     proto->queueJob(job);
00982 }
00983 
00984 #ifndef KDE_NO_DEPRECATED
00985 void SchedulerPrivate::scheduleJob(SimpleJob *job)
00986 {
00987     kDebug(7006) << job;
00988     setJobPriority(job, 1);
00989 }
00990 #endif
00991 
00992 void SchedulerPrivate::setJobPriority(SimpleJob *job, int priority)
00993 {
00994     kDebug(7006) << job << priority;
00995     ProtoQueue *proto = protoQ(SimpleJobPrivate::get(job)->m_protocol, job->url().host());
00996     proto->changeJobPriority(job, priority);
00997 }
00998 
00999 void SchedulerPrivate::cancelJob(SimpleJob *job)
01000 {
01001     // this method is called all over the place in job.cpp, so just do this check here to avoid
01002     // much boilerplate in job code.
01003     if (SimpleJobPrivate::get(job)->m_schedSerial == 0) {
01004         //kDebug(7006) << "Doing nothing because I don't know job" << job;
01005         return;
01006     }
01007     Slave *slave = jobSlave(job);
01008     kDebug(7006) << job << slave;
01009     if (slave) {
01010         kDebug(7006) << "Scheduler: killing slave " << slave->slave_pid();
01011         slave->kill();
01012     }
01013     jobFinished(job, slave);
01014 }
01015 
01016 void SchedulerPrivate::jobFinished(SimpleJob *job, Slave *slave)
01017 {
01018     kDebug(7006) << job << slave;
01019     if (QThread::currentThread() != QCoreApplication::instance()->thread()) {
01020         kWarning(7006) << "KIO is not thread-safe.";
01021     }
01022 
01023     KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job);
01024 
01025     // make sure that we knew about the job!
01026     Q_ASSERT(jobPriv->m_schedSerial);
01027 
01028     ProtoQueue *pq = m_protocols.value(jobPriv->m_protocol);
01029     if (pq) {
01030        pq->removeJob(job);
01031     }
01032 
01033     if (slave) {
01034         // If we have internal meta-data, tell existing ioslaves to reload
01035         // their configuration.
01036         if (jobPriv->m_internalMetaData.count()) {
01037             kDebug(7006) << "Updating ioslaves with new internal metadata information";
01038             ProtoQueue * queue = m_protocols.value(slave->protocol());
01039             if (queue) {
01040                 QListIterator<Slave*> it (queue->allSlaves());
01041                 while (it.hasNext()) {
01042                     Slave* runningSlave = it.next();
01043                     if (slave->host() == runningSlave->host()) {
01044                         slave->setConfig(metaDataFor(slave->protocol(), jobPriv->m_proxyList, job->url()));
01045                         kDebug(7006) << "Updated configuration of" << slave->protocol()
01046                                      << "ioslave, pid=" << slave->slave_pid();
01047                     }
01048                 }
01049             }
01050         }
01051         slave->setJob(0);
01052         slave->disconnect(job);
01053     }
01054     jobPriv->m_schedSerial = 0; // this marks the job as unscheduled again
01055     jobPriv->m_slave = 0;
01056     // Clear the values in the internal metadata container since they have
01057     // already been taken care of above...
01058     jobPriv->m_internalMetaData.clear();
01059 }
01060 
01061 // static
01062 void setupSlave(KIO::Slave *slave, const KUrl &url, const QString &protocol,
01063                 const QStringList &proxyList , bool newSlave, const KIO::MetaData *config)
01064 {
01065     schedulerPrivate->setupSlave(slave, url, protocol, proxyList, newSlave, config);
01066 }
01067 
01068 MetaData SchedulerPrivate::metaDataFor(const QString &protocol, const QStringList &proxyList, const KUrl &url)
01069 {
01070     const QString host = url.host();
01071     MetaData configData = SlaveConfig::self()->configData(protocol, host);
01072     sessionData.configDataFor( configData, protocol, host );
01073     if (proxyList.isEmpty()) {
01074         configData.remove(QLatin1String("UseProxy"));
01075         configData.remove(QLatin1String("ProxyUrls"));
01076     } else {
01077         configData[QLatin1String("UseProxy")] = proxyList.first();
01078         configData[QLatin1String("ProxyUrls")] = proxyList.join(QLatin1String(","));
01079     }
01080 
01081     if ( configData.contains("EnableAutoLogin") &&
01082          configData.value("EnableAutoLogin").compare("true", Qt::CaseInsensitive) == 0 )
01083     {
01084         NetRC::AutoLogin l;
01085         l.login = url.user();
01086         bool usern = (protocol == "ftp");
01087         if ( NetRC::self()->lookup( url, l, usern) )
01088         {
01089             configData["autoLoginUser"] = l.login;
01090             configData["autoLoginPass"] = l.password;
01091             if ( usern )
01092             {
01093                 QString macdef;
01094                 QMap<QString, QStringList>::ConstIterator it = l.macdef.constBegin();
01095                 for ( ; it != l.macdef.constEnd(); ++it )
01096                     macdef += it.key() + '\\' + it.value().join( "\\" ) + '\n';
01097                 configData["autoLoginMacro"] = macdef;
01098             }
01099         }
01100     }
01101 
01102     return configData;
01103 }
01104 
01105 void SchedulerPrivate::setupSlave(KIO::Slave *slave, const KUrl &url, const QString &protocol,
01106                                   const QStringList &proxyList, bool newSlave, const KIO::MetaData *config)
01107 {
01108     int port = url.port();
01109     if ( port == -1 ) // no port is -1 in QUrl, but in kde3 we used 0 and the kioslaves assume that.
01110         port = 0;
01111     const QString host = url.host();
01112     const QString user = url.user();
01113     const QString passwd = url.pass();
01114 
01115     if (newSlave || slave->host() != host || slave->port() != port ||
01116         slave->user() != user || slave->passwd() != passwd) {
01117 
01118         MetaData configData = metaDataFor(protocol, proxyList, url);
01119         if (config)
01120            configData += *config;
01121 
01122         slave->setConfig(configData);
01123         slave->setProtocol(url.protocol());
01124         slave->setHost(host, port, user, passwd);
01125     }
01126 }
01127 
01128 
01129 void SchedulerPrivate::slotSlaveStatus(pid_t, const QByteArray&, const QString &, bool)
01130 {
01131 }
01132 
01133 
01134 void SchedulerPrivate::slotSlaveDied(KIO::Slave *slave)
01135 {
01136     kDebug(7006) << slave;
01137     Q_ASSERT(slave);
01138     Q_ASSERT(!slave->isAlive());
01139     ProtoQueue *pq = m_protocols.value(slave->protocol());
01140     if (pq) {
01141        if (slave->job()) {
01142            pq->removeJob(slave->job());
01143        }
01144        // in case this was a connected slave...
01145        pq->removeSlave(slave);
01146     }
01147     if (slave == m_slaveOnHold) {
01148        m_slaveOnHold = 0;
01149        m_urlOnHold.clear();
01150     }
01151     slave->deref(); // Delete slave
01152 }
01153 
01154 void SchedulerPrivate::putSlaveOnHold(KIO::SimpleJob *job, const KUrl &url)
01155 {
01156     Slave *slave = jobSlave(job);
01157     kDebug(7006) << job << url << slave;
01158     slave->disconnect(job);
01159     // prevent the fake death of the slave from trying to kill the job again;
01160     // cf. Slave::hold(const KUrl &url) called in SchedulerPrivate::publishSlaveOnHold().
01161     slave->setJob(0);
01162     SimpleJobPrivate::get(job)->m_slave = 0;
01163 
01164     if (m_slaveOnHold) {
01165         m_slaveOnHold->kill();
01166     }
01167     m_slaveOnHold = slave;
01168     m_urlOnHold = url;
01169     m_slaveOnHold->suspend();
01170 }
01171 
01172 void SchedulerPrivate::publishSlaveOnHold()
01173 {
01174     kDebug(7006) << m_slaveOnHold;
01175     if (!m_slaveOnHold)
01176        return;
01177 
01178     m_slaveOnHold->hold(m_urlOnHold);
01179     emit q->slaveOnHoldListChanged();
01180 }
01181 
01182 bool SchedulerPrivate::isSlaveOnHoldFor(const KUrl& url)
01183 {
01184     if (url.isValid() && m_urlOnHold.isValid() && url == m_urlOnHold)
01185         return true;
01186 
01187     return Slave::checkForHeldSlave(url);
01188 }
01189 
01190 Slave *SchedulerPrivate::heldSlaveForJob(SimpleJob *job)
01191 {
01192     Slave *slave = 0;
01193     KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job);
01194 
01195     if (jobPriv->m_checkOnHold) {
01196         slave = Slave::holdSlave(jobPriv->m_protocol, job->url());
01197     }
01198 
01199     if (!slave && m_slaveOnHold) {
01200         // Make sure that the job wants to do a GET or a POST, and with no offset
01201         const int cmd = jobPriv->m_command;
01202         bool canJobReuse = (cmd == CMD_GET || cmd == CMD_MULTI_GET);
01203 
01204         if (KIO::TransferJob *tJob = qobject_cast<KIO::TransferJob *>(job)) {
01205             canJobReuse = ( canJobReuse || cmd == CMD_SPECIAL );
01206             if (canJobReuse) {
01207                 KIO::MetaData outgoing = tJob->outgoingMetaData();
01208                 const QString resume = outgoing.value("resume");
01209                 kDebug(7006) << "Resume metadata is" << resume;
01210                 canJobReuse = (resume.isEmpty() || resume == "0");
01211             }
01212         }
01213 
01214         if (job->url() == m_urlOnHold) {
01215             if (canJobReuse) {
01216                 kDebug(7006) << "HOLD: Reusing held slave (" << m_slaveOnHold << ")";
01217                 slave = m_slaveOnHold;
01218             } else {
01219                 kDebug(7006) << "HOLD: Discarding held slave (" << m_slaveOnHold << ")";
01220                 m_slaveOnHold->kill();
01221             }
01222             m_slaveOnHold = 0;
01223             m_urlOnHold.clear();
01224         }
01225     } else if (slave) {
01226         kDebug(7006) << "HOLD: Reusing klauncher held slave (" << slave << ")";
01227     }
01228 
01229     // Reset the parent widget the ioslave should use when displaying message
01230     // boxes after being put on hold.
01231     if (slave && job->ui()) {
01232         slave->setWindow(job->ui()->window());
01233     }
01234 
01235     return slave;
01236 }
01237 
01238 void SchedulerPrivate::removeSlaveOnHold()
01239 {
01240     kDebug(7006) << m_slaveOnHold;
01241     if (m_slaveOnHold) {
01242         m_slaveOnHold->kill();
01243     }
01244     m_slaveOnHold = 0;
01245     m_urlOnHold.clear();
01246 }
01247 
01248 Slave *SchedulerPrivate::getConnectedSlave(const KUrl &url, const KIO::MetaData &config)
01249 {
01250     QStringList proxyList;
01251     const QString protocol = KProtocolManager::slaveProtocol(url, proxyList);
01252     ProtoQueue *pq = protoQ(protocol, url.host());
01253 
01254     Slave *slave = pq->createSlave(protocol, /* job */0, url);
01255     if (slave) {
01256         setupSlave(slave, url, protocol, proxyList, true, &config);
01257         pq->m_connectedSlaveQueue.addSlave(slave);
01258 
01259         slave->send( CMD_CONNECT );
01260         q->connect(slave, SIGNAL(connected()),
01261                    SLOT(slotSlaveConnected()));
01262         q->connect(slave, SIGNAL(error(int,QString)),
01263                    SLOT(slotSlaveError(int,QString)));
01264     }
01265     kDebug(7006) << url << slave;
01266     return slave;
01267 }
01268 
01269 
01270 void SchedulerPrivate::slotSlaveConnected()
01271 {
01272     kDebug(7006);
01273     Slave *slave = static_cast<Slave *>(q->sender());
01274     slave->setConnected(true);
01275     q->disconnect(slave, SIGNAL(connected()), q, SLOT(slotSlaveConnected()));
01276     emit q->slaveConnected(slave);
01277 }
01278 
01279 void SchedulerPrivate::slotSlaveError(int errorNr, const QString &errorMsg)
01280 {
01281     Slave *slave = static_cast<Slave *>(q->sender());
01282     kDebug(7006) << slave << errorNr << errorMsg;
01283     ProtoQueue *pq = protoQ(slave->protocol(), slave->host());
01284     if (!slave->isConnected() || pq->m_connectedSlaveQueue.isIdle(slave)) {
01285         // Only forward to application if slave is idle or still connecting.
01286         // ### KDE5: can we remove this apparently arbitrary behavior and just always emit SlaveError?
01287         emit q->slaveError(slave, errorNr, errorMsg);
01288     }
01289 }
01290 
01291 bool SchedulerPrivate::assignJobToSlave(KIO::Slave *slave, SimpleJob *job)
01292 {
01293     kDebug(7006) << slave << job;
01294     // KDE5: queueing of jobs can probably be removed, it provides very little benefit
01295     ProtoQueue *pq = m_protocols.value(slave->protocol());
01296     if (pq) {
01297         pq->removeJob(job);
01298         return pq->m_connectedSlaveQueue.queueJob(job, slave);
01299     }
01300     return false;
01301 }
01302 
01303 bool SchedulerPrivate::disconnectSlave(KIO::Slave *slave)
01304 {
01305     kDebug(7006) << slave;
01306     ProtoQueue *pq = m_protocols.value(slave->protocol());
01307     return (pq ? pq->m_connectedSlaveQueue.removeSlave(slave) : false);
01308 }
01309 
01310 void SchedulerPrivate::checkSlaveOnHold(bool b)
01311 {
01312     kDebug(7006) << b;
01313     m_checkOnHold = b;
01314 }
01315 
01316 /*
01317   Returns the top most window associated with widget.
01318 
01319   Unlike QWidget::window(), this function does its best to find and return the
01320   main application window associated with the given widget.
01321 
01322   If widget itself is a dialog or its parent is a dialog, and that dialog has a
01323   parent widget then this function will iterate through all those widgets to
01324   find the top most window, which most of the time is the main window of the
01325   application. By contrast, QWidget::window() would simply return the first
01326   file dialog it encountered since it is the "next ancestor widget that has (or
01327   could have) a window-system frame".
01328 */
01329 static QWidget* topLevelWindow(QWidget* widget)
01330 {
01331     QWidget* w = widget;
01332     while (w && w->parentWidget()) {
01333         w = w->parentWidget();
01334     }
01335     return (w ? w->window() : 0);
01336 }
01337 
01338 void SchedulerPrivate::registerWindow(QWidget *wid)
01339 {
01340    if (!wid)
01341       return;
01342 
01343    QWidget* window = topLevelWindow(wid);
01344    QObject *obj = static_cast<QObject *>(window);
01345 
01346    if (!m_windowList.contains(obj))
01347    {
01348       // We must store the window Id because by the time
01349       // the destroyed signal is emitted we can no longer
01350       // access QWidget::winId() (already destructed)
01351       WId windowId = window->winId();
01352       m_windowList.insert(obj, windowId);
01353       q->connect(window, SIGNAL(destroyed(QObject*)),
01354                  SLOT(slotUnregisterWindow(QObject*)));
01355       QDBusInterface("org.kde.kded", "/kded", "org.kde.kded").
01356           call(QDBus::NoBlock, "registerWindowId", qlonglong(windowId));
01357    }
01358 }
01359 
01360 void SchedulerPrivate::slotUnregisterWindow(QObject *obj)
01361 {
01362    if (!obj)
01363       return;
01364 
01365    QMap<QObject *, WId>::Iterator it = m_windowList.find(obj);
01366    if (it == m_windowList.end())
01367       return;
01368    WId windowId = it.value();
01369    q->disconnect(it.key(), SIGNAL(destroyed(QObject*)),
01370                  q, SLOT(slotUnregisterWindow(QObject*)));
01371    m_windowList.erase( it );
01372    QDBusInterface("org.kde.kded", "/kded", "org.kde.kded").
01373        call(QDBus::NoBlock, "unregisterWindowId", qlonglong(windowId));
01374 }
01375 
01376 void SchedulerPrivate::updateInternalMetaData(SimpleJob* job)
01377 {
01378     KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job);
01379     // Preserve all internal meta-data so they can be sent back to the
01380     // ioslaves as needed...
01381     const KUrl jobUrl = job->url();
01382     kDebug(7006) << job << jobPriv->m_internalMetaData;
01383     QMapIterator<QString, QString> it (jobPriv->m_internalMetaData);
01384     while (it.hasNext()) {
01385         it.next();
01386         if (it.key().startsWith(QLatin1String("{internal~currenthost}"), Qt::CaseInsensitive)) {
01387             SlaveConfig::self()->setConfigData(jobUrl.protocol(), jobUrl.host(), it.key().mid(22), it.value());
01388         } else if (it.key().startsWith(QLatin1String("{internal~allhosts}"), Qt::CaseInsensitive)) {
01389             SlaveConfig::self()->setConfigData(jobUrl.protocol(), QString(), it.key().mid(19), it.value());
01390         }
01391     }
01392 }
01393 
01394 
01395 #include "scheduler.moc"
01396 #include "scheduler_p.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:35:02 by doxygen 1.7.5.1 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.9.5 API Reference

Skip menu "kdelibs-4.9.5 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