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