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