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