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"
KDE 4.6 API Reference