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

KIO

scheduler.cpp

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

KIO

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

kdelibs

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