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

KIO

  • kio
  • kio
scheduler.cpp
Go to the documentation of this file.
1/* This file is part of the KDE libraries
2 Copyright (C) 2000 Stephan Kulow <coolo@kde.org>
3 Waldo Bastian <bastian@kde.org>
4 Copyright (C) 2009, 2010 Andreas Hartmetz <ahartmetz@gmail.com>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License version 2 as published by the Free Software Foundation.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*/
20
21#include "scheduler.h"
22#include "scheduler_p.h"
23
24#include "sessiondata.h"
25#include "slaveconfig.h"
26#include "authinfo.h"
27#include "slave.h"
28#include "connection.h"
29#include "job_p.h"
30
31#include <kdebug.h>
32#include <kprotocolmanager.h>
33#include <kprotocolinfo.h>
34#include <assert.h>
35
36#include <QtCore/QHash>
37#include <QtGui/QWidget>
38#include <QtDBus/QtDBus>
39
40// Slaves may be idle for a certain time (3 minutes) before they are killed.
41static const int s_idleSlaveLifetime = 3 * 60;
42
43
44using namespace KIO;
45
46#ifndef KDE_USE_FINAL // already defined in job.cpp
47static inline Slave *jobSlave(SimpleJob *job)
48{
49 return SimpleJobPrivate::get(job)->m_slave;
50}
51#endif
52
53static inline int jobCommand(SimpleJob *job)
54{
55 return SimpleJobPrivate::get(job)->m_command;
56}
57
58static inline void startJob(SimpleJob *job, Slave *slave)
59{
60 SimpleJobPrivate::get(job)->start(slave);
61}
62
63// here be uglies
64// forward declaration to break cross-dependency of SlaveKeeper and SchedulerPrivate
65static void setupSlave(KIO::Slave *slave, const KUrl &url, const QString &protocol,
66 const QStringList &proxyList, bool newSlave, const KIO::MetaData *config = 0);
67// same reason as above
68static Scheduler *scheduler();
69static Slave *heldSlaveForJob(SimpleJob *job);
70
71
72int SerialPicker::changedPrioritySerial(int oldSerial, int newPriority) const
73{
74 Q_ASSERT(newPriority >= -10 && newPriority <= 10);
75 newPriority = qBound(-10, newPriority, 10);
76 int unbiasedSerial = oldSerial % m_jobsPerPriority;
77 return unbiasedSerial + newPriority * m_jobsPerPriority;
78}
79
80
81SlaveKeeper::SlaveKeeper()
82{
83 m_grimTimer.setSingleShot(true);
84 connect (&m_grimTimer, SIGNAL(timeout()), SLOT(grimReaper()));
85}
86
87void SlaveKeeper::returnSlave(Slave *slave)
88{
89 Q_ASSERT(slave);
90 slave->setIdle();
91 m_idleSlaves.insert(slave->host(), slave);
92 scheduleGrimReaper();
93}
94
95Slave *SlaveKeeper::takeSlaveForJob(SimpleJob *job)
96{
97 Slave *slave = heldSlaveForJob(job);
98 if (slave) {
99 return slave;
100 }
101
102 KUrl url = SimpleJobPrivate::get(job)->m_url;
103 // TODO take port, username and password into account
104 QMultiHash<QString, Slave *>::Iterator it = m_idleSlaves.find(url.host());
105 if (it == m_idleSlaves.end()) {
106 it = m_idleSlaves.begin();
107 }
108 if (it == m_idleSlaves.end()) {
109 return 0;
110 }
111 slave = it.value();
112 m_idleSlaves.erase(it);
113 return slave;
114}
115
116bool SlaveKeeper::removeSlave(Slave *slave)
117{
118 // ### performance not so great
119 QMultiHash<QString, Slave *>::Iterator it = m_idleSlaves.begin();
120 for (; it != m_idleSlaves.end(); ++it) {
121 if (it.value() == slave) {
122 m_idleSlaves.erase(it);
123 return true;
124 }
125 }
126 return false;
127}
128
129QList<Slave *> SlaveKeeper::allSlaves() const
130{
131 return m_idleSlaves.values();
132}
133
134void SlaveKeeper::scheduleGrimReaper()
135{
136 if (!m_grimTimer.isActive()) {
137 m_grimTimer.start((s_idleSlaveLifetime / 2) * 1000);
138 }
139}
140
141//private slot
142void SlaveKeeper::grimReaper()
143{
144 QMultiHash<QString, Slave *>::Iterator it = m_idleSlaves.begin();
145 while (it != m_idleSlaves.end()) {
146 Slave *slave = it.value();
147 if (slave->idleTime() >= s_idleSlaveLifetime) {
148 it = m_idleSlaves.erase(it);
149 if (slave->job()) {
150 kDebug (7006) << "Idle slave" << slave << "still has job" << slave->job();
151 }
152 slave->kill();
153 // avoid invoking slotSlaveDied() because its cleanup services are not needed
154 slave->deref();
155 } else {
156 ++it;
157 }
158 }
159 if (!m_idleSlaves.isEmpty()) {
160 scheduleGrimReaper();
161 }
162}
163
164
165int HostQueue::lowestSerial() const
166{
167 QMap<int, SimpleJob*>::ConstIterator first = m_queuedJobs.constBegin();
168 if (first != m_queuedJobs.constEnd()) {
169 return first.key();
170 }
171 return SerialPicker::maxSerial;
172}
173
174void HostQueue::queueJob(SimpleJob *job)
175{
176 const int serial = SimpleJobPrivate::get(job)->m_schedSerial;
177 Q_ASSERT(serial != 0);
178 Q_ASSERT(!m_queuedJobs.contains(serial));
179 Q_ASSERT(!m_runningJobs.contains(job));
180 m_queuedJobs.insert(serial, job);
181}
182
183SimpleJob *HostQueue::takeFirstInQueue()
184{
185 Q_ASSERT(!m_queuedJobs.isEmpty());
186 QMap<int, SimpleJob *>::iterator first = m_queuedJobs.begin();
187 SimpleJob *job = first.value();
188 m_queuedJobs.erase(first);
189 m_runningJobs.insert(job);
190 return job;
191}
192
193bool HostQueue::removeJob(SimpleJob *job)
194{
195 const int serial = SimpleJobPrivate::get(job)->m_schedSerial;
196 if (m_runningJobs.remove(job)) {
197 Q_ASSERT(!m_queuedJobs.contains(serial));
198 return true;
199 }
200 if (m_queuedJobs.remove(serial)) {
201 return true;
202 }
203 return false;
204}
205
206QList<Slave *> HostQueue::allSlaves() const
207{
208 QList<Slave *> ret;
209 Q_FOREACH (SimpleJob *job, m_runningJobs) {
210 Slave *slave = jobSlave(job);
211 Q_ASSERT(slave);
212 ret.append(slave);
213 }
214 return ret;
215}
216
217
218
219ConnectedSlaveQueue::ConnectedSlaveQueue()
220{
221 m_startJobsTimer.setSingleShot(true);
222 connect (&m_startJobsTimer, SIGNAL(timeout()), SLOT(startRunnableJobs()));
223}
224
225bool ConnectedSlaveQueue::queueJob(SimpleJob *job, Slave *slave)
226{
227 QHash<Slave *, PerSlaveQueue>::Iterator it = m_connectedSlaves.find(slave);
228 if (it == m_connectedSlaves.end()) {
229 return false;
230 }
231 SimpleJobPrivate::get(job)->m_slave = slave;
232
233 PerSlaveQueue &jobs = it.value();
234 jobs.waitingList.append(job);
235 if (!jobs.runningJob) {
236 // idle slave now has a job to run
237 m_runnableSlaves.insert(slave);
238 m_startJobsTimer.start();
239 }
240 return true;
241}
242
243bool ConnectedSlaveQueue::removeJob(SimpleJob *job)
244{
245 Slave *slave = jobSlave(job);
246 Q_ASSERT(slave);
247 QHash<Slave *, PerSlaveQueue>::Iterator it = m_connectedSlaves.find(slave);
248 if (it == m_connectedSlaves.end()) {
249 return false;
250 }
251 PerSlaveQueue &jobs = it.value();
252 if (jobs.runningJob || jobs.waitingList.isEmpty()) {
253 // a slave that was busy running a job was not runnable.
254 // a slave that has no waiting job(s) was not runnable either.
255 Q_ASSERT(!m_runnableSlaves.contains(slave));
256 }
257
258 const bool removedRunning = jobs.runningJob == job;
259 const bool removedWaiting = jobs.waitingList.removeAll(job) != 0;
260 if (removedRunning) {
261 jobs.runningJob = 0;
262 Q_ASSERT(!removedWaiting);
263 }
264 const bool removedTheJob = removedRunning || removedWaiting;
265
266 if (!slave->isAlive()) {
267 removeSlave(slave);
268 return removedTheJob;
269 }
270
271 if (removedRunning && jobs.waitingList.count()) {
272 m_runnableSlaves.insert(slave);
273 m_startJobsTimer.start();
274 }
275 if (removedWaiting && jobs.waitingList.isEmpty()) {
276 m_runnableSlaves.remove(slave);
277 }
278 return removedTheJob;
279}
280
281void ConnectedSlaveQueue::addSlave(Slave *slave)
282{
283 Q_ASSERT(slave);
284 if (!m_connectedSlaves.contains(slave)) {
285 m_connectedSlaves.insert(slave, PerSlaveQueue());
286 }
287}
288
289bool ConnectedSlaveQueue::removeSlave(Slave *slave)
290{
291 QHash<Slave *, PerSlaveQueue>::Iterator it = m_connectedSlaves.find(slave);
292 if (it == m_connectedSlaves.end()) {
293 return false;
294 }
295 PerSlaveQueue &jobs = it.value();
296 Q_FOREACH (SimpleJob *job, jobs.waitingList) {
297 // ### for compatibility with the old scheduler we don't touch the running job, if any.
298 // make sure that the job doesn't call back into Scheduler::cancelJob(); this would
299 // a) crash and b) be unnecessary because we clean up just fine.
300 SimpleJobPrivate::get(job)->m_schedSerial = 0;
301 job->kill();
302 }
303 m_connectedSlaves.erase(it);
304 m_runnableSlaves.remove(slave);
305
306 slave->kill();
307 return true;
308}
309
310// KDE5: only one caller, for doubtful reasons. remove this if possible.
311bool ConnectedSlaveQueue::isIdle(Slave *slave)
312{
313 QHash<Slave *, PerSlaveQueue>::Iterator it = m_connectedSlaves.find(slave);
314 if (it == m_connectedSlaves.end()) {
315 return false;
316 }
317 return it.value().runningJob == 0;
318}
319
320
321//private slot
322void ConnectedSlaveQueue::startRunnableJobs()
323{
324 QSet<Slave *>::Iterator it = m_runnableSlaves.begin();
325 while (it != m_runnableSlaves.end()) {
326 Slave *slave = *it;
327 if (!slave->isConnected()) {
328 // this polling is somewhat inefficient...
329 m_startJobsTimer.start();
330 ++it;
331 continue;
332 }
333 it = m_runnableSlaves.erase(it);
334 PerSlaveQueue &jobs = m_connectedSlaves[slave];
335 SimpleJob *job = jobs.waitingList.takeFirst();
336 Q_ASSERT(!jobs.runningJob);
337 jobs.runningJob = job;
338
339 const KUrl url = job->url();
340 // no port is -1 in QUrl, but in kde3 we used 0 and the kioslaves assume that.
341 const int port = url.port() == -1 ? 0 : url.port();
342
343 if (slave->host() == "<reset>") {
344 MetaData configData = SlaveConfig::self()->configData(url.protocol(), url.host());
345 slave->setConfig(configData);
346 slave->setProtocol(url.protocol());
347 slave->setHost(url.host(), port, url.user(), url.pass());
348 }
349
350 Q_ASSERT(slave->protocol() == url.protocol());
351 Q_ASSERT(slave->host() == url.host());
352 Q_ASSERT(slave->port() == port);
353 startJob(job, slave);
354 }
355}
356
357
358static void ensureNoDuplicates(QMap<int, HostQueue *> *queuesBySerial)
359{
360 Q_UNUSED(queuesBySerial);
361#ifdef SCHEDULER_DEBUG
362 // a host queue may *never* be in queuesBySerial twice.
363 QSet<HostQueue *> seen;
364 Q_FOREACH (HostQueue *hq, *queuesBySerial) {
365 Q_ASSERT(!seen.contains(hq));
366 seen.insert(hq);
367 }
368#endif
369}
370
371static void verifyRunningJobsCount(QHash<QString, HostQueue> *queues, int runningJobsCount)
372{
373 Q_UNUSED(queues);
374 Q_UNUSED(runningJobsCount);
375#ifdef SCHEDULER_DEBUG
376 int realRunningJobsCount = 0;
377 Q_FOREACH (const HostQueue &hq, *queues) {
378 realRunningJobsCount += hq.runningJobsCount();
379 }
380 Q_ASSERT(realRunningJobsCount == runningJobsCount);
381
382 // ...and of course we may never run the same job twice!
383 QSet<SimpleJob *> seenJobs;
384 Q_FOREACH (const HostQueue &hq, *queues) {
385 Q_FOREACH (SimpleJob *job, hq.runningJobs()) {
386 Q_ASSERT(!seenJobs.contains(job));
387 seenJobs.insert(job);
388 }
389 }
390#endif
391}
392
393
394ProtoQueue::ProtoQueue(SchedulerPrivate *sp, int maxSlaves, int maxSlavesPerHost)
395 : m_schedPrivate(sp),
396 m_maxConnectionsPerHost(maxSlavesPerHost ? maxSlavesPerHost : maxSlaves),
397 m_maxConnectionsTotal(qMax(maxSlaves, maxSlavesPerHost)),
398 m_runningJobsCount(0)
399
400{
401 kDebug(7006) << "m_maxConnectionsTotal:" << m_maxConnectionsTotal
402 << "m_maxConnectionsPerHost:" << m_maxConnectionsPerHost;
403 Q_ASSERT(m_maxConnectionsPerHost >= 1);
404 Q_ASSERT(maxSlaves >= maxSlavesPerHost);
405 m_startJobTimer.setSingleShot(true);
406 connect (&m_startJobTimer, SIGNAL(timeout()), SLOT(startAJob()));
407}
408
409ProtoQueue::~ProtoQueue()
410{
411 Q_FOREACH (Slave *slave, allSlaves()) {
412 // kill the slave process, then remove the interface in our process
413 slave->kill();
414 slave->deref();
415 }
416}
417
418void ProtoQueue::queueJob(SimpleJob *job)
419{
420 QString hostname = SimpleJobPrivate::get(job)->m_url.host();
421 HostQueue &hq = m_queuesByHostname[hostname];
422 const int prevLowestSerial = hq.lowestSerial();
423 Q_ASSERT(hq.runningJobsCount() <= m_maxConnectionsPerHost);
424
425 // nevert insert a job twice
426 Q_ASSERT(SimpleJobPrivate::get(job)->m_schedSerial == 0);
427 SimpleJobPrivate::get(job)->m_schedSerial = m_serialPicker.next();
428
429 const bool wasQueueEmpty = hq.isQueueEmpty();
430 hq.queueJob(job);
431 // note that HostQueue::queueJob() into an empty queue changes its lowestSerial() too...
432 // the queue's lowest serial job may have changed, so update the ordered list of queues.
433 // however, we ignore all jobs that would cause more connections to a host than allowed.
434 if (prevLowestSerial != hq.lowestSerial()) {
435 if (hq.runningJobsCount() < m_maxConnectionsPerHost) {
436 // if the connection limit didn't keep the HQ unscheduled it must have been lack of jobs
437 if (m_queuesBySerial.remove(prevLowestSerial) == 0) {
438 Q_UNUSED(wasQueueEmpty);
439 Q_ASSERT(wasQueueEmpty);
440 }
441 m_queuesBySerial.insert(hq.lowestSerial(), &hq);
442 } else {
443#ifdef SCHEDULER_DEBUG
444 // ### this assertion may fail if the limits were modified at runtime!
445 // if the per-host connection limit is already reached the host queue's lowest serial
446 // should not be queued.
447 Q_ASSERT(!m_queuesBySerial.contains(prevLowestSerial));
448#endif
449 }
450 }
451 // just in case; startAJob() will refuse to start a job if it shouldn't.
452 m_startJobTimer.start();
453
454 ensureNoDuplicates(&m_queuesBySerial);
455}
456
457void ProtoQueue::changeJobPriority(SimpleJob *job, int newPrio)
458{
459 SimpleJobPrivate *jobPriv = SimpleJobPrivate::get(job);
460 QHash<QString, HostQueue>::Iterator it = m_queuesByHostname.find(jobPriv->m_url.host());
461 if (it == m_queuesByHostname.end()) {
462 return;
463 }
464 HostQueue &hq = it.value();
465 const int prevLowestSerial = hq.lowestSerial();
466 if (hq.isJobRunning(job) || !hq.removeJob(job)) {
467 return;
468 }
469 jobPriv->m_schedSerial = m_serialPicker.changedPrioritySerial(jobPriv->m_schedSerial, newPrio);
470 hq.queueJob(job);
471 const bool needReinsert = hq.lowestSerial() != prevLowestSerial;
472 // the host queue might be absent from m_queuesBySerial because the connections per host limit
473 // for that host has been reached.
474 if (needReinsert && m_queuesBySerial.remove(prevLowestSerial)) {
475 m_queuesBySerial.insert(hq.lowestSerial(), &hq);
476 }
477 ensureNoDuplicates(&m_queuesBySerial);
478}
479
480void ProtoQueue::removeJob(SimpleJob *job)
481{
482 SimpleJobPrivate *jobPriv = SimpleJobPrivate::get(job);
483 HostQueue &hq = m_queuesByHostname[jobPriv->m_url.host()];
484 const int prevLowestSerial = hq.lowestSerial();
485 const int prevRunningJobs = hq.runningJobsCount();
486
487 Q_ASSERT(hq.runningJobsCount() <= m_maxConnectionsPerHost);
488
489 if (hq.removeJob(job)) {
490 if (hq.lowestSerial() != prevLowestSerial) {
491 // we have dequeued the not yet running job with the lowest serial
492 Q_ASSERT(!jobPriv->m_slave);
493 Q_ASSERT(prevRunningJobs == hq.runningJobsCount());
494 if (m_queuesBySerial.remove(prevLowestSerial) == 0) {
495 // make sure that the queue was not scheduled for a good reason
496 Q_ASSERT(hq.runningJobsCount() == m_maxConnectionsPerHost);
497 }
498 } else {
499 if (prevRunningJobs != hq.runningJobsCount()) {
500 // we have dequeued a previously running job
501 Q_ASSERT(prevRunningJobs - 1 == hq.runningJobsCount());
502 m_runningJobsCount--;
503 Q_ASSERT(m_runningJobsCount >= 0);
504 }
505 }
506 if (!hq.isQueueEmpty() && hq.runningJobsCount() < m_maxConnectionsPerHost) {
507 // this may be a no-op, but it's faster than first checking if it's already in.
508 m_queuesBySerial.insert(hq.lowestSerial(), &hq);
509 }
510
511 if (hq.isEmpty()) {
512 // no queued jobs, no running jobs. this destroys hq from above.
513 m_queuesByHostname.remove(jobPriv->m_url.host());
514 }
515
516 if (jobPriv->m_slave && jobPriv->m_slave->isAlive()) {
517 m_slaveKeeper.returnSlave(jobPriv->m_slave);
518 }
519 // just in case; startAJob() will refuse to start a job if it shouldn't.
520 m_startJobTimer.start();
521 } else {
522 // should be a connected slave
523 // if the assertion fails the job has probably changed the host part of its URL while
524 // running, so we can't find it by hostname. don't do this.
525 const bool removed = m_connectedSlaveQueue.removeJob(job);
526 Q_UNUSED(removed);
527 Q_ASSERT(removed);
528 }
529
530 ensureNoDuplicates(&m_queuesBySerial);
531}
532
533Slave *ProtoQueue::createSlave(const QString &protocol, SimpleJob *job, const KUrl &url)
534{
535 int error;
536 QString errortext;
537 Slave *slave = Slave::createSlave(protocol, url, error, errortext);
538 if (slave) {
539 scheduler()->connect(slave, SIGNAL(slaveDied(KIO::Slave*)),
540 SLOT(slotSlaveDied(KIO::Slave*)));
541 scheduler()->connect(slave, SIGNAL(slaveStatus(pid_t,QByteArray,QString,bool)),
542 SLOT(slotSlaveStatus(pid_t,QByteArray,QString,bool)));
543 } else {
544 kError() << "couldn't create slave:" << errortext;
545 if (job) {
546 job->slotError(error, errortext);
547 }
548 }
549 return slave;
550}
551
552bool ProtoQueue::removeSlave (KIO::Slave *slave)
553{
554 const bool removedConnected = m_connectedSlaveQueue.removeSlave(slave);
555 const bool removedUnconnected = m_slaveKeeper.removeSlave(slave);
556 Q_ASSERT(!(removedConnected && removedUnconnected));
557 return removedConnected || removedUnconnected;
558}
559
560QList<Slave *> ProtoQueue::allSlaves() const
561{
562 QList<Slave *> ret(m_slaveKeeper.allSlaves());
563 Q_FOREACH (const HostQueue &hq, m_queuesByHostname) {
564 ret.append(hq.allSlaves());
565 }
566 ret.append(m_connectedSlaveQueue.allSlaves());
567 return ret;
568}
569
570//private slot
571void ProtoQueue::startAJob()
572{
573 ensureNoDuplicates(&m_queuesBySerial);
574 verifyRunningJobsCount(&m_queuesByHostname, m_runningJobsCount);
575
576#ifdef SCHEDULER_DEBUG
577 kDebug(7006) << "m_runningJobsCount:" << m_runningJobsCount;
578 Q_FOREACH (const HostQueue &hq, m_queuesByHostname) {
579 Q_FOREACH (SimpleJob *job, hq.runningJobs()) {
580 kDebug(7006) << SimpleJobPrivate::get(job)->m_url;
581 }
582 }
583#endif
584 if (m_runningJobsCount >= m_maxConnectionsTotal) {
585#ifdef SCHEDULER_DEBUG
586 kDebug(7006) << "not starting any jobs because maxConnectionsTotal has been reached.";
587#endif
588 return;
589 }
590
591 QMap<int, HostQueue *>::iterator first = m_queuesBySerial.begin();
592 if (first != m_queuesBySerial.end()) {
593 // pick a job and maintain the queue invariant: lower serials first
594 HostQueue *hq = first.value();
595 const int prevLowestSerial = first.key();
596 Q_UNUSED(prevLowestSerial);
597 Q_ASSERT(hq->lowestSerial() == prevLowestSerial);
598 // the following assertions should hold due to queueJob(), takeFirstInQueue() and
599 // removeJob() being correct
600 Q_ASSERT(hq->runningJobsCount() < m_maxConnectionsPerHost);
601 SimpleJob *startingJob = hq->takeFirstInQueue();
602 Q_ASSERT(hq->runningJobsCount() <= m_maxConnectionsPerHost);
603 Q_ASSERT(hq->lowestSerial() != prevLowestSerial);
604
605 m_queuesBySerial.erase(first);
606 // we've increased hq's runningJobsCount() by calling nexStartingJob()
607 // so we need to check again.
608 if (!hq->isQueueEmpty() && hq->runningJobsCount() < m_maxConnectionsPerHost) {
609 m_queuesBySerial.insert(hq->lowestSerial(), hq);
610 }
611
612 // always increase m_runningJobsCount because it's correct if there is a slave and if there
613 // is no slave, removeJob() will balance the number again. removeJob() would decrease the
614 // number too much otherwise.
615 // Note that createSlave() can call slotError() on a job which in turn calls removeJob(),
616 // so increase the count here already.
617 m_runningJobsCount++;
618
619 bool isNewSlave = false;
620 Slave *slave = m_slaveKeeper.takeSlaveForJob(startingJob);
621 SimpleJobPrivate *jobPriv = SimpleJobPrivate::get(startingJob);
622 if (!slave) {
623 isNewSlave = true;
624 slave = createSlave(jobPriv->m_protocol, startingJob, jobPriv->m_url);
625 }
626
627 if (slave) {
628 jobPriv->m_slave = slave;
629 setupSlave(slave, jobPriv->m_url, jobPriv->m_protocol, jobPriv->m_proxyList, isNewSlave);
630 startJob(startingJob, slave);
631 } else {
632 // dispose of our records about the job and mark the job as unknown
633 // (to prevent crashes later)
634 // note that the job's slotError() can have called removeJob() first, so check that
635 // it's not a ghost job with null serial already.
636 if (jobPriv->m_schedSerial) {
637 removeJob(startingJob);
638 jobPriv->m_schedSerial = 0;
639 }
640 }
641 } else {
642#ifdef SCHEDULER_DEBUG
643 kDebug(7006) << "not starting any jobs because there is no queued job.";
644#endif
645 }
646
647 if (!m_queuesBySerial.isEmpty()) {
648 m_startJobTimer.start();
649 }
650}
651
652
653
654class KIO::SchedulerPrivate
655{
656public:
657 SchedulerPrivate()
658 : q(new Scheduler()),
659 m_slaveOnHold(0),
660 m_checkOnHold(true), // !! Always check with KLauncher for the first request
661 m_ignoreConfigReparse(false)
662 {
663 }
664
665 ~SchedulerPrivate()
666 {
667 delete q;
668 q = 0;
669 Q_FOREACH (ProtoQueue *p, m_protocols) {
670 Q_FOREACH (Slave *slave, p->allSlaves()) {
671 slave->kill();
672 }
673 p->deleteLater();
674 }
675 }
676 Scheduler *q;
677
678 Slave *m_slaveOnHold;
679 KUrl m_urlOnHold;
680 bool m_checkOnHold;
681 bool m_ignoreConfigReparse;
682
683 SessionData sessionData;
684 QMap<QObject *,WId> m_windowList;
685
686 void doJob(SimpleJob *job);
687#ifndef KDE_NO_DEPRECATED
688 void scheduleJob(SimpleJob *job);
689#endif
690 void setJobPriority(SimpleJob *job, int priority);
691 void cancelJob(SimpleJob *job);
692 void jobFinished(KIO::SimpleJob *job, KIO::Slave *slave);
693 void putSlaveOnHold(KIO::SimpleJob *job, const KUrl &url);
694 void removeSlaveOnHold();
695 Slave *getConnectedSlave(const KUrl &url, const KIO::MetaData &metaData);
696 bool assignJobToSlave(KIO::Slave *slave, KIO::SimpleJob *job);
697 bool disconnectSlave(KIO::Slave *slave);
698 void checkSlaveOnHold(bool b);
699 void publishSlaveOnHold();
700 Slave *heldSlaveForJob(KIO::SimpleJob *job);
701 bool isSlaveOnHoldFor(const KUrl& url);
702 void registerWindow(QWidget *wid);
703 void updateInternalMetaData(SimpleJob* job);
704
705 MetaData metaDataFor(const QString &protocol, const QStringList &proxyList, const KUrl &url);
706 void setupSlave(KIO::Slave *slave, const KUrl &url, const QString &protocol,
707 const QStringList &proxyList, bool newSlave, const KIO::MetaData *config = 0);
708
709 void slotSlaveDied(KIO::Slave *slave);
710 void slotSlaveStatus(pid_t pid, const QByteArray &protocol,
711 const QString &host, bool connected);
712
713 void slotReparseSlaveConfiguration(const QString &, const QDBusMessage&);
714 void slotSlaveOnHoldListChanged();
715
716 void slotSlaveConnected();
717 void slotSlaveError(int error, const QString &errorMsg);
718 void slotUnregisterWindow(QObject *);
719
720 ProtoQueue *protoQ(const QString& protocol, const QString& host)
721 {
722 ProtoQueue *pq = m_protocols.value(protocol, 0);
723 if (!pq) {
724 kDebug(7006) << "creating ProtoQueue instance for" << protocol;
725
726 const int maxSlaves = KProtocolInfo::maxSlaves(protocol);
727 int maxSlavesPerHost = -1;
728 if (!host.isEmpty()) {
729 bool ok = false;
730 const int value = SlaveConfig::self()->configData(protocol, host, QLatin1String("MaxConnections")).toInt(&ok);
731 if (ok)
732 maxSlavesPerHost = value;
733 }
734 if (maxSlavesPerHost == -1) {
735 maxSlavesPerHost = KProtocolInfo::maxSlavesPerHost(protocol);
736 }
737 // Never allow maxSlavesPerHost to exceed maxSlaves.
738 pq = new ProtoQueue(this, maxSlaves, qMin(maxSlaves, maxSlavesPerHost));
739 m_protocols.insert(protocol, pq);
740 }
741 return pq;
742 }
743private:
744 QHash<QString, ProtoQueue *> m_protocols;
745};
746
747
748K_GLOBAL_STATIC(SchedulerPrivate, schedulerPrivate)
749
750Scheduler *Scheduler::self()
751{
752 return schedulerPrivate->q;
753}
754
755SchedulerPrivate *Scheduler::d_func()
756{
757 return schedulerPrivate;
758}
759
760//static
761Scheduler *scheduler()
762{
763 return schedulerPrivate->q;
764}
765
766//static
767Slave *heldSlaveForJob(SimpleJob *job)
768{
769 return schedulerPrivate->heldSlaveForJob(job);
770}
771
772
773Scheduler::Scheduler()
774 : removeMe(0)
775{
776 setObjectName( "scheduler" );
777
778 const QString dbusPath = "/KIO/Scheduler";
779 const QString dbusInterface = "org.kde.KIO.Scheduler";
780 QDBusConnection dbus = QDBusConnection::sessionBus();
781 dbus.registerObject( "/KIO/Scheduler", this, QDBusConnection::ExportScriptableSlots |
782 QDBusConnection::ExportScriptableSignals );
783 dbus.connect(QString(), dbusPath, dbusInterface, "reparseSlaveConfiguration",
784 this, SLOT(slotReparseSlaveConfiguration(QString,QDBusMessage)));
785 dbus.connect(QString(), dbusPath, dbusInterface, "slaveOnHoldListChanged",
786 this, SLOT(slotSlaveOnHoldListChanged()));
787}
788
789Scheduler::~Scheduler()
790{
791}
792
793void Scheduler::doJob(SimpleJob *job)
794{
795 schedulerPrivate->doJob(job);
796}
797
798#ifndef KDE_NO_DEPRECATED
799void Scheduler::scheduleJob(SimpleJob *job)
800{
801 schedulerPrivate->scheduleJob(job);
802}
803#endif
804
805void Scheduler::setJobPriority(SimpleJob *job, int priority)
806{
807 schedulerPrivate->setJobPriority(job, priority);
808}
809
810void Scheduler::cancelJob(SimpleJob *job)
811{
812 schedulerPrivate->cancelJob(job);
813}
814
815void Scheduler::jobFinished(KIO::SimpleJob *job, KIO::Slave *slave)
816{
817 schedulerPrivate->jobFinished(job, slave);
818}
819
820void Scheduler::putSlaveOnHold(KIO::SimpleJob *job, const KUrl &url)
821{
822 schedulerPrivate->putSlaveOnHold(job, url);
823}
824
825void Scheduler::removeSlaveOnHold()
826{
827 schedulerPrivate->removeSlaveOnHold();
828}
829
830void Scheduler::publishSlaveOnHold()
831{
832 schedulerPrivate->publishSlaveOnHold();
833}
834
835bool Scheduler::isSlaveOnHoldFor(const KUrl& url)
836{
837 return schedulerPrivate->isSlaveOnHoldFor(url);
838}
839
840void Scheduler::updateInternalMetaData(SimpleJob* job)
841{
842 schedulerPrivate->updateInternalMetaData(job);
843}
844
845KIO::Slave *Scheduler::getConnectedSlave(const KUrl &url,
846 const KIO::MetaData &config )
847{
848 return schedulerPrivate->getConnectedSlave(url, config);
849}
850
851bool Scheduler::assignJobToSlave(KIO::Slave *slave, KIO::SimpleJob *job)
852{
853 return schedulerPrivate->assignJobToSlave(slave, job);
854}
855
856bool Scheduler::disconnectSlave(KIO::Slave *slave)
857{
858 return schedulerPrivate->disconnectSlave(slave);
859}
860
861void Scheduler::registerWindow(QWidget *wid)
862{
863 schedulerPrivate->registerWindow(wid);
864}
865
866void Scheduler::unregisterWindow(QObject *wid)
867{
868 schedulerPrivate->slotUnregisterWindow(wid);
869}
870
871bool Scheduler::connect( const char *signal, const QObject *receiver,
872 const char *member)
873{
874 return QObject::connect(self(), signal, receiver, member);
875}
876
877bool Scheduler::connect( const QObject* sender, const char* signal,
878 const QObject* receiver, const char* member )
879{
880 return QObject::connect(sender, signal, receiver, member);
881}
882
883bool Scheduler::disconnect( const QObject* sender, const char* signal,
884 const QObject* receiver, const char* member )
885{
886 return QObject::disconnect(sender, signal, receiver, member);
887}
888
889bool Scheduler::connect( const QObject *sender, const char *signal,
890 const char *member )
891{
892 return QObject::connect(sender, signal, member);
893}
894
895void Scheduler::checkSlaveOnHold(bool b)
896{
897 schedulerPrivate->checkSlaveOnHold(b);
898}
899
900void Scheduler::emitReparseSlaveConfiguration()
901{
902 // Do it immediately in this process, otherwise we might send a request before reparsing
903 // (e.g. when changing useragent in the plugin)
904 schedulerPrivate->slotReparseSlaveConfiguration(QString(), QDBusMessage());
905
906 schedulerPrivate->m_ignoreConfigReparse = true;
907 emit self()->reparseSlaveConfiguration( QString() );
908}
909
910
911void SchedulerPrivate::slotReparseSlaveConfiguration(const QString &proto, const QDBusMessage&)
912{
913 if (m_ignoreConfigReparse) {
914 kDebug(7006) << "Ignoring signal sent by myself";
915 m_ignoreConfigReparse = false;
916 return;
917 }
918
919 kDebug(7006) << "proto=" << proto;
920 KProtocolManager::reparseConfiguration();
921 SlaveConfig::self()->reset();
922 sessionData.reset();
923 NetRC::self()->reload();
924
925 QHash<QString, ProtoQueue *>::ConstIterator it = proto.isEmpty() ? m_protocols.constBegin() :
926 m_protocols.constFind(proto);
927 // not found?
928 if (it == m_protocols.constEnd()) {
929 return;
930 }
931 QHash<QString, ProtoQueue *>::ConstIterator endIt = proto.isEmpty() ? m_protocols.constEnd() :
932 it + 1;
933 for (; it != endIt; ++it) {
934 Q_FOREACH(Slave *slave, (*it)->allSlaves()) {
935 slave->send(CMD_REPARSECONFIGURATION);
936 slave->resetHost();
937 }
938 }
939}
940
941void SchedulerPrivate::slotSlaveOnHoldListChanged()
942{
943 m_checkOnHold = true;
944}
945
946static bool mayReturnContent(int cmd, const QString& protocol)
947{
948 if (cmd == CMD_GET)
949 return true;
950
951 if (cmd == CMD_MULTI_GET)
952 return true;
953
954 if (cmd == CMD_SPECIAL && protocol.startsWith(QLatin1String("http"), Qt::CaseInsensitive))
955 return true;
956
957 return false;
958}
959
960void SchedulerPrivate::doJob(SimpleJob *job)
961{
962 kDebug(7006) << job;
963 if (QThread::currentThread() != QCoreApplication::instance()->thread()) {
964 kWarning(7006) << "KIO is not thread-safe.";
965 }
966
967 KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job);
968 jobPriv->m_proxyList.clear();
969 jobPriv->m_protocol = KProtocolManager::slaveProtocol(job->url(), jobPriv->m_proxyList);
970
971 if (mayReturnContent(jobCommand(job), jobPriv->m_protocol)) {
972 jobPriv->m_checkOnHold = m_checkOnHold;
973 m_checkOnHold = false;
974 }
975
976 ProtoQueue *proto = protoQ(jobPriv->m_protocol, job->url().host());
977 proto->queueJob(job);
978}
979
980#ifndef KDE_NO_DEPRECATED
981void SchedulerPrivate::scheduleJob(SimpleJob *job)
982{
983 kDebug(7006) << job;
984 setJobPriority(job, 1);
985}
986#endif
987
988void SchedulerPrivate::setJobPriority(SimpleJob *job, int priority)
989{
990 kDebug(7006) << job << priority;
991 ProtoQueue *proto = protoQ(SimpleJobPrivate::get(job)->m_protocol, job->url().host());
992 proto->changeJobPriority(job, priority);
993}
994
995void SchedulerPrivate::cancelJob(SimpleJob *job)
996{
997 // this method is called all over the place in job.cpp, so just do this check here to avoid
998 // much boilerplate in job code.
999 if (SimpleJobPrivate::get(job)->m_schedSerial == 0) {
1000 //kDebug(7006) << "Doing nothing because I don't know job" << job;
1001 return;
1002 }
1003 Slave *slave = jobSlave(job);
1004 kDebug(7006) << job << slave;
1005 if (slave) {
1006 kDebug(7006) << "Scheduler: killing slave " << slave->slave_pid();
1007 slave->kill();
1008 }
1009 jobFinished(job, slave);
1010}
1011
1012void SchedulerPrivate::jobFinished(SimpleJob *job, Slave *slave)
1013{
1014 kDebug(7006) << job << slave;
1015 if (QThread::currentThread() != QCoreApplication::instance()->thread()) {
1016 kWarning(7006) << "KIO is not thread-safe.";
1017 }
1018
1019 KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job);
1020
1021 // make sure that we knew about the job!
1022 Q_ASSERT(jobPriv->m_schedSerial);
1023
1024 ProtoQueue *pq = m_protocols.value(jobPriv->m_protocol);
1025 if (pq) {
1026 pq->removeJob(job);
1027 }
1028
1029 if (slave) {
1030 // If we have internal meta-data, tell existing ioslaves to reload
1031 // their configuration.
1032 if (jobPriv->m_internalMetaData.count()) {
1033 kDebug(7006) << "Updating ioslaves with new internal metadata information";
1034 ProtoQueue * queue = m_protocols.value(slave->protocol());
1035 if (queue) {
1036 QListIterator<Slave*> it (queue->allSlaves());
1037 while (it.hasNext()) {
1038 Slave* runningSlave = it.next();
1039 if (slave->host() == runningSlave->host()) {
1040 slave->setConfig(metaDataFor(slave->protocol(), jobPriv->m_proxyList, job->url()));
1041 kDebug(7006) << "Updated configuration of" << slave->protocol()
1042 << "ioslave, pid=" << slave->slave_pid();
1043 }
1044 }
1045 }
1046 }
1047 slave->setJob(0);
1048 slave->disconnect(job);
1049 }
1050 jobPriv->m_schedSerial = 0; // this marks the job as unscheduled again
1051 jobPriv->m_slave = 0;
1052 // Clear the values in the internal metadata container since they have
1053 // already been taken care of above...
1054 jobPriv->m_internalMetaData.clear();
1055}
1056
1057// static
1058void setupSlave(KIO::Slave *slave, const KUrl &url, const QString &protocol,
1059 const QStringList &proxyList , bool newSlave, const KIO::MetaData *config)
1060{
1061 schedulerPrivate->setupSlave(slave, url, protocol, proxyList, newSlave, config);
1062}
1063
1064MetaData SchedulerPrivate::metaDataFor(const QString &protocol, const QStringList &proxyList, const KUrl &url)
1065{
1066 const QString host = url.host();
1067 MetaData configData = SlaveConfig::self()->configData(protocol, host);
1068 sessionData.configDataFor( configData, protocol, host );
1069 if (proxyList.isEmpty()) {
1070 configData.remove(QLatin1String("UseProxy"));
1071 configData.remove(QLatin1String("ProxyUrls"));
1072 } else {
1073 configData[QLatin1String("UseProxy")] = proxyList.first();
1074 configData[QLatin1String("ProxyUrls")] = proxyList.join(QLatin1String(","));
1075 }
1076
1077 if ( configData.contains("EnableAutoLogin") &&
1078 configData.value("EnableAutoLogin").compare("true", Qt::CaseInsensitive) == 0 )
1079 {
1080 NetRC::AutoLogin l;
1081 l.login = url.user();
1082 bool usern = (protocol == "ftp");
1083 if ( NetRC::self()->lookup( url, l, usern) )
1084 {
1085 configData["autoLoginUser"] = l.login;
1086 configData["autoLoginPass"] = l.password;
1087 if ( usern )
1088 {
1089 QString macdef;
1090 QMap<QString, QStringList>::ConstIterator it = l.macdef.constBegin();
1091 for ( ; it != l.macdef.constEnd(); ++it )
1092 macdef += it.key() + '\\' + it.value().join( "\\" ) + '\n';
1093 configData["autoLoginMacro"] = macdef;
1094 }
1095 }
1096 }
1097
1098 return configData;
1099}
1100
1101void SchedulerPrivate::setupSlave(KIO::Slave *slave, const KUrl &url, const QString &protocol,
1102 const QStringList &proxyList, bool newSlave, const KIO::MetaData *config)
1103{
1104 int port = url.port();
1105 if ( port == -1 ) // no port is -1 in QUrl, but in kde3 we used 0 and the kioslaves assume that.
1106 port = 0;
1107 const QString host = url.host();
1108 const QString user = url.user();
1109 const QString passwd = url.pass();
1110
1111 if (newSlave || slave->host() != host || slave->port() != port ||
1112 slave->user() != user || slave->passwd() != passwd) {
1113
1114 MetaData configData = metaDataFor(protocol, proxyList, url);
1115 if (config)
1116 configData += *config;
1117
1118 slave->setConfig(configData);
1119 slave->setProtocol(url.protocol());
1120 slave->setHost(host, port, user, passwd);
1121 }
1122}
1123
1124
1125void SchedulerPrivate::slotSlaveStatus(pid_t, const QByteArray&, const QString &, bool)
1126{
1127}
1128
1129
1130void SchedulerPrivate::slotSlaveDied(KIO::Slave *slave)
1131{
1132 kDebug(7006) << slave;
1133 Q_ASSERT(slave);
1134 Q_ASSERT(!slave->isAlive());
1135 ProtoQueue *pq = m_protocols.value(slave->protocol());
1136 if (pq) {
1137 if (slave->job()) {
1138 pq->removeJob(slave->job());
1139 }
1140 // in case this was a connected slave...
1141 pq->removeSlave(slave);
1142 }
1143 if (slave == m_slaveOnHold) {
1144 m_slaveOnHold = 0;
1145 m_urlOnHold.clear();
1146 }
1147 slave->deref(); // Delete slave
1148}
1149
1150void SchedulerPrivate::putSlaveOnHold(KIO::SimpleJob *job, const KUrl &url)
1151{
1152 Slave *slave = jobSlave(job);
1153 kDebug(7006) << job << url << slave;
1154 slave->disconnect(job);
1155 // prevent the fake death of the slave from trying to kill the job again;
1156 // cf. Slave::hold(const KUrl &url) called in SchedulerPrivate::publishSlaveOnHold().
1157 slave->setJob(0);
1158 SimpleJobPrivate::get(job)->m_slave = 0;
1159
1160 if (m_slaveOnHold) {
1161 m_slaveOnHold->kill();
1162 }
1163 m_slaveOnHold = slave;
1164 m_urlOnHold = url;
1165 m_slaveOnHold->suspend();
1166}
1167
1168void SchedulerPrivate::publishSlaveOnHold()
1169{
1170 kDebug(7006) << m_slaveOnHold;
1171 if (!m_slaveOnHold)
1172 return;
1173
1174 m_slaveOnHold->hold(m_urlOnHold);
1175 emit q->slaveOnHoldListChanged();
1176}
1177
1178bool SchedulerPrivate::isSlaveOnHoldFor(const KUrl& url)
1179{
1180 if (url.isValid() && m_urlOnHold.isValid() && url == m_urlOnHold)
1181 return true;
1182
1183 return Slave::checkForHeldSlave(url);
1184}
1185
1186Slave *SchedulerPrivate::heldSlaveForJob(SimpleJob *job)
1187{
1188 Slave *slave = 0;
1189 KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job);
1190
1191 if (jobPriv->m_checkOnHold) {
1192 slave = Slave::holdSlave(jobPriv->m_protocol, job->url());
1193 }
1194
1195 if (!slave && m_slaveOnHold) {
1196 // Make sure that the job wants to do a GET or a POST, and with no offset
1197 const int cmd = jobPriv->m_command;
1198 bool canJobReuse = (cmd == CMD_GET || cmd == CMD_MULTI_GET);
1199
1200 if (KIO::TransferJob *tJob = qobject_cast<KIO::TransferJob *>(job)) {
1201 canJobReuse = ( canJobReuse || cmd == CMD_SPECIAL );
1202 if (canJobReuse) {
1203 KIO::MetaData outgoing = tJob->outgoingMetaData();
1204 const QString resume = outgoing.value("resume");
1205 kDebug(7006) << "Resume metadata is" << resume;
1206 canJobReuse = (resume.isEmpty() || resume == "0");
1207 }
1208 }
1209
1210 if (job->url() == m_urlOnHold) {
1211 if (canJobReuse) {
1212 kDebug(7006) << "HOLD: Reusing held slave (" << m_slaveOnHold << ")";
1213 slave = m_slaveOnHold;
1214 } else {
1215 kDebug(7006) << "HOLD: Discarding held slave (" << m_slaveOnHold << ")";
1216 m_slaveOnHold->kill();
1217 }
1218 m_slaveOnHold = 0;
1219 m_urlOnHold.clear();
1220 }
1221 } else if (slave) {
1222 kDebug(7006) << "HOLD: Reusing klauncher held slave (" << slave << ")";
1223 }
1224
1225 return slave;
1226}
1227
1228void SchedulerPrivate::removeSlaveOnHold()
1229{
1230 kDebug(7006) << m_slaveOnHold;
1231 if (m_slaveOnHold) {
1232 m_slaveOnHold->kill();
1233 }
1234 m_slaveOnHold = 0;
1235 m_urlOnHold.clear();
1236}
1237
1238Slave *SchedulerPrivate::getConnectedSlave(const KUrl &url, const KIO::MetaData &config)
1239{
1240 QStringList proxyList;
1241 const QString protocol = KProtocolManager::slaveProtocol(url, proxyList);
1242 ProtoQueue *pq = protoQ(protocol, url.host());
1243
1244 Slave *slave = pq->createSlave(protocol, /* job */0, url);
1245 if (slave) {
1246 setupSlave(slave, url, protocol, proxyList, true, &config);
1247 pq->m_connectedSlaveQueue.addSlave(slave);
1248
1249 slave->send( CMD_CONNECT );
1250 q->connect(slave, SIGNAL(connected()),
1251 SLOT(slotSlaveConnected()));
1252 q->connect(slave, SIGNAL(error(int,QString)),
1253 SLOT(slotSlaveError(int,QString)));
1254 }
1255 kDebug(7006) << url << slave;
1256 return slave;
1257}
1258
1259
1260void SchedulerPrivate::slotSlaveConnected()
1261{
1262 kDebug(7006);
1263 Slave *slave = static_cast<Slave *>(q->sender());
1264 slave->setConnected(true);
1265 q->disconnect(slave, SIGNAL(connected()), q, SLOT(slotSlaveConnected()));
1266 emit q->slaveConnected(slave);
1267}
1268
1269void SchedulerPrivate::slotSlaveError(int errorNr, const QString &errorMsg)
1270{
1271 Slave *slave = static_cast<Slave *>(q->sender());
1272 kDebug(7006) << slave << errorNr << errorMsg;
1273 ProtoQueue *pq = protoQ(slave->protocol(), slave->host());
1274 if (!slave->isConnected() || pq->m_connectedSlaveQueue.isIdle(slave)) {
1275 // Only forward to application if slave is idle or still connecting.
1276 // ### KDE5: can we remove this apparently arbitrary behavior and just always emit SlaveError?
1277 emit q->slaveError(slave, errorNr, errorMsg);
1278 }
1279}
1280
1281bool SchedulerPrivate::assignJobToSlave(KIO::Slave *slave, SimpleJob *job)
1282{
1283 kDebug(7006) << slave << job;
1284 // KDE5: queueing of jobs can probably be removed, it provides very little benefit
1285 ProtoQueue *pq = m_protocols.value(slave->protocol());
1286 if (pq) {
1287 pq->removeJob(job);
1288 return pq->m_connectedSlaveQueue.queueJob(job, slave);
1289 }
1290 return false;
1291}
1292
1293bool SchedulerPrivate::disconnectSlave(KIO::Slave *slave)
1294{
1295 kDebug(7006) << slave;
1296 ProtoQueue *pq = m_protocols.value(slave->protocol());
1297 return (pq ? pq->m_connectedSlaveQueue.removeSlave(slave) : false);
1298}
1299
1300void SchedulerPrivate::checkSlaveOnHold(bool b)
1301{
1302 kDebug(7006) << b;
1303 m_checkOnHold = b;
1304}
1305
1306/*
1307 Returns the top most window associated with widget.
1308
1309 Unlike QWidget::window(), this function does its best to find and return the
1310 main application window associated with the given widget.
1311
1312 If widget itself is a dialog or its parent is a dialog, and that dialog has a
1313 parent widget then this function will iterate through all those widgets to
1314 find the top most window, which most of the time is the main window of the
1315 application. By contrast, QWidget::window() would simply return the first
1316 file dialog it encountered since it is the "next ancestor widget that has (or
1317 could have) a window-system frame".
1318*/
1319static QWidget* topLevelWindow(QWidget* widget)
1320{
1321 QWidget* w = widget;
1322 while (w && w->parentWidget()) {
1323 w = w->parentWidget();
1324 }
1325 return (w ? w->window() : 0);
1326}
1327
1328void SchedulerPrivate::registerWindow(QWidget *wid)
1329{
1330 if (!wid)
1331 return;
1332
1333 QWidget* window = topLevelWindow(wid);
1334 QObject *obj = static_cast<QObject *>(window);
1335
1336 if (!m_windowList.contains(obj))
1337 {
1338 // We must store the window Id because by the time
1339 // the destroyed signal is emitted we can no longer
1340 // access QWidget::winId() (already destructed)
1341 WId windowId = window->winId();
1342 m_windowList.insert(obj, windowId);
1343 q->connect(window, SIGNAL(destroyed(QObject*)),
1344 SLOT(slotUnregisterWindow(QObject*)));
1345 QDBusInterface("org.kde.kded", "/kded", "org.kde.kded").
1346 call(QDBus::NoBlock, "registerWindowId", qlonglong(windowId));
1347 }
1348}
1349
1350void SchedulerPrivate::slotUnregisterWindow(QObject *obj)
1351{
1352 if (!obj)
1353 return;
1354
1355 QMap<QObject *, WId>::Iterator it = m_windowList.find(obj);
1356 if (it == m_windowList.end())
1357 return;
1358 WId windowId = it.value();
1359 q->disconnect(it.key(), SIGNAL(destroyed(QObject*)),
1360 q, SLOT(slotUnregisterWindow(QObject*)));
1361 m_windowList.erase( it );
1362 QDBusInterface("org.kde.kded", "/kded", "org.kde.kded").
1363 call(QDBus::NoBlock, "unregisterWindowId", qlonglong(windowId));
1364}
1365
1366void SchedulerPrivate::updateInternalMetaData(SimpleJob* job)
1367{
1368 KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job);
1369 // Preserve all internal meta-data so they can be sent back to the
1370 // ioslaves as needed...
1371 const KUrl jobUrl = job->url();
1372 kDebug(7006) << job << jobPriv->m_internalMetaData;
1373 QMapIterator<QString, QString> it (jobPriv->m_internalMetaData);
1374 while (it.hasNext()) {
1375 it.next();
1376 if (it.key().startsWith(QLatin1String("{internal~currenthost}"), Qt::CaseInsensitive)) {
1377 SlaveConfig::self()->setConfigData(jobUrl.protocol(), jobUrl.host(), it.key().mid(22), it.value());
1378 } else if (it.key().startsWith(QLatin1String("{internal~allhosts}"), Qt::CaseInsensitive)) {
1379 SlaveConfig::self()->setConfigData(jobUrl.protocol(), QString(), it.key().mid(19), it.value());
1380 }
1381 }
1382}
1383
1384
1385#include "scheduler.moc"
1386#include "scheduler_p.moc"
authinfo.h
KIO::ConnectedSlaveQueue::removeSlave
bool removeSlave(KIO::Slave *slave)
Definition: scheduler.cpp:289
KIO::ConnectedSlaveQueue::addSlave
void addSlave(KIO::Slave *slave)
Definition: scheduler.cpp:281
KIO::ConnectedSlaveQueue::ConnectedSlaveQueue
ConnectedSlaveQueue()
Definition: scheduler.cpp:219
KIO::ConnectedSlaveQueue::allSlaves
QList< KIO::Slave * > allSlaves() const
Definition: scheduler_p.h:97
KIO::ConnectedSlaveQueue::queueJob
bool queueJob(KIO::SimpleJob *job, KIO::Slave *slave)
Definition: scheduler.cpp:225
KIO::ConnectedSlaveQueue::removeJob
bool removeJob(KIO::SimpleJob *job)
Definition: scheduler.cpp:243
KIO::ConnectedSlaveQueue::isIdle
bool isIdle(KIO::Slave *slave)
Definition: scheduler.cpp:311
KIO::HostQueue
Definition: scheduler_p.h:53
KIO::HostQueue::lowestSerial
int lowestSerial() const
Definition: scheduler.cpp:165
KIO::HostQueue::runningJobsCount
int runningJobsCount() const
Definition: scheduler_p.h:59
KIO::HostQueue::removeJob
bool removeJob(KIO::SimpleJob *job)
Definition: scheduler.cpp:193
KIO::HostQueue::isJobRunning
bool isJobRunning(KIO::SimpleJob *job) const
Definition: scheduler_p.h:63
KIO::HostQueue::isEmpty
bool isEmpty() const
Definition: scheduler_p.h:58
KIO::HostQueue::isQueueEmpty
bool isQueueEmpty() const
Definition: scheduler_p.h:57
KIO::HostQueue::queueJob
void queueJob(KIO::SimpleJob *job)
Definition: scheduler.cpp:174
KIO::HostQueue::takeFirstInQueue
KIO::SimpleJob * takeFirstInQueue()
Definition: scheduler.cpp:183
KIO::HostQueue::allSlaves
QList< KIO::Slave * > allSlaves() const
Definition: scheduler.cpp:206
KIO::JobPrivate::m_internalMetaData
MetaData m_internalMetaData
Definition: job_p.h:61
KIO::MetaData
MetaData is a simple map of key/value strings.
Definition: global.h:397
KIO::NetRC::lookup
bool lookup(const KUrl &url, AutoLogin &login, bool userealnetrc=false, const QString &type=QString(), LookUpMode mode=LookUpMode(exactOnly)|defaultOnly)
Looks up the login information for the given url.
Definition: authinfo.cpp:293
KIO::NetRC::self
static NetRC * self()
A reference to the instance of the class.
Definition: authinfo.cpp:286
KIO::NetRC::reload
void reload()
Reloads the auto login information.
Definition: authinfo.cpp:370
KIO::ProtoQueue
Definition: scheduler_p.h:137
KIO::ProtoQueue::changeJobPriority
void changeJobPriority(KIO::SimpleJob *job, int newPriority)
Definition: scheduler.cpp:457
KIO::ProtoQueue::removeSlave
bool removeSlave(KIO::Slave *slave)
Definition: scheduler.cpp:552
KIO::ProtoQueue::~ProtoQueue
~ProtoQueue()
Definition: scheduler.cpp:409
KIO::ProtoQueue::ProtoQueue
ProtoQueue(KIO::SchedulerPrivate *sp, int maxSlaves, int maxSlavesPerHost)
Definition: scheduler.cpp:394
KIO::ProtoQueue::allSlaves
QList< KIO::Slave * > allSlaves() const
Definition: scheduler.cpp:560
KIO::ProtoQueue::queueJob
void queueJob(KIO::SimpleJob *job)
Definition: scheduler.cpp:418
KIO::ProtoQueue::m_connectedSlaveQueue
ConnectedSlaveQueue m_connectedSlaveQueue
Definition: scheduler_p.h:149
KIO::ProtoQueue::removeJob
void removeJob(KIO::SimpleJob *job)
Definition: scheduler.cpp:480
KIO::ProtoQueue::createSlave
KIO::Slave * createSlave(const QString &protocol, KIO::SimpleJob *job, const KUrl &url)
Definition: scheduler.cpp:533
KIO::Scheduler
The KIO::Scheduler manages io-slaves for the application.
Definition: scheduler.h:110
KIO::Scheduler::putSlaveOnHold
static void putSlaveOnHold(KIO::SimpleJob *job, const KUrl &url)
Puts a slave on notice.
Definition: scheduler.cpp:820
KIO::Scheduler::getConnectedSlave
static KIO::Slave * getConnectedSlave(const KUrl &url, const KIO::MetaData &config=MetaData())
Requests a slave for use in connection-oriented mode.
Definition: scheduler.cpp:845
KIO::Scheduler::setJobPriority
static void setJobPriority(SimpleJob *job, int priority)
Changes the priority of job; jobs of the same priority run in the order in which they were created.
Definition: scheduler.cpp:805
KIO::Scheduler::scheduleJob
static void scheduleJob(SimpleJob *job)
Schedules job scheduled for later execution.
Definition: scheduler.cpp:799
KIO::Scheduler::slaveOnHoldListChanged
Q_SCRIPTABLE void slaveOnHoldListChanged()
KIO::Scheduler::isSlaveOnHoldFor
static bool isSlaveOnHoldFor(const KUrl &url)
Returns true if there is a slave on hold for url.
Definition: scheduler.cpp:835
KIO::Scheduler::removeSlaveOnHold
static void removeSlaveOnHold()
Removes any slave that might have been put on hold.
Definition: scheduler.cpp:825
KIO::Scheduler::slaveConnected
void slaveConnected(KIO::Slave *slave)
KIO::Scheduler::assignJobToSlave
static bool assignJobToSlave(KIO::Slave *slave, KIO::SimpleJob *job)
Uses slave to do job.
Definition: scheduler.cpp:851
KIO::Scheduler::slaveError
void slaveError(KIO::Slave *slave, int error, const QString &errorMsg)
KIO::Scheduler::doJob
static void doJob(SimpleJob *job)
Register job with the scheduler.
Definition: scheduler.cpp:793
KIO::Scheduler::emitReparseSlaveConfiguration
static void emitReparseSlaveConfiguration()
Definition: scheduler.cpp:900
KIO::Scheduler::disconnect
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
Definition: scheduler.cpp:883
KIO::Scheduler::jobFinished
static void jobFinished(KIO::SimpleJob *job, KIO::Slave *slave)
Called when a job is done.
Definition: scheduler.cpp:815
KIO::Scheduler::connect
static bool connect(const char *signal, const QObject *receiver, const char *member)
Function to connect signals emitted by the scheduler.
Definition: scheduler.cpp:871
KIO::Scheduler::disconnectSlave
static bool disconnectSlave(KIO::Slave *slave)
Disconnects slave.
Definition: scheduler.cpp:856
KIO::Scheduler::registerWindow
static void registerWindow(QWidget *wid)
Register the mainwindow wid with the KIO subsystem Do not call this, it is called automatically from ...
Definition: scheduler.cpp:861
KIO::Scheduler::reparseSlaveConfiguration
Q_SCRIPTABLE void reparseSlaveConfiguration(const QString &)
KIO::Scheduler::cancelJob
static void cancelJob(SimpleJob *job)
Stop the execution of a job.
Definition: scheduler.cpp:810
KIO::Scheduler::publishSlaveOnHold
static void publishSlaveOnHold()
Send the slave that was put on hold back to KLauncher.
Definition: scheduler.cpp:830
KIO::Scheduler::checkSlaveOnHold
static void checkSlaveOnHold(bool b)
When true, the next job will check whether KLauncher has a slave on hold that is suitable for the job...
Definition: scheduler.cpp:895
KIO::Scheduler::updateInternalMetaData
static void updateInternalMetaData(SimpleJob *job)
Updates the internal metadata from job.
Definition: scheduler.cpp:840
KIO::Scheduler::unregisterWindow
static void unregisterWindow(QObject *wid)
Definition: scheduler.cpp:866
KIO::SerialPicker::changedPrioritySerial
int changedPrioritySerial(int oldSerial, int newPriority) const
Definition: scheduler.cpp:72
KIO::SerialPicker::maxSerial
static const int maxSerial
Definition: scheduler_p.h:132
KIO::SerialPicker::next
int next()
Definition: scheduler_p.h:118
KIO::SessionData
Definition: sessiondata.h:36
KIO::SessionData::configDataFor
virtual void configDataFor(KIO::MetaData &configData, const QString &proto, const QString &host)
Definition: sessiondata.cpp:95
KIO::SessionData::reset
virtual void reset()
Definition: sessiondata.cpp:120
KIO::SimpleJobPrivate
Definition: job_p.h:82
KIO::SimpleJobPrivate::m_slave
Slave * m_slave
Definition: job_p.h:104
KIO::SimpleJobPrivate::m_protocol
QString m_protocol
Definition: job_p.h:125
KIO::SimpleJobPrivate::get
static SimpleJobPrivate * get(KIO::SimpleJob *job)
Definition: job_p.h:199
KIO::SimpleJobPrivate::m_url
KUrl m_url
Definition: job_p.h:106
KIO::SimpleJobPrivate::m_command
int m_command
Definition: job_p.h:108
KIO::SimpleJobPrivate::m_proxyList
QStringList m_proxyList
Definition: job_p.h:126
KIO::SimpleJobPrivate::m_checkOnHold
bool m_checkOnHold
Definition: job_p.h:127
KIO::SimpleJobPrivate::start
virtual void start(KIO::Slave *slave)
Definition: job.cpp:385
KIO::SimpleJobPrivate::m_schedSerial
int m_schedSerial
Definition: job_p.h:128
KIO::SimpleJob
A simple job (one url and one command).
Definition: jobclasses.h:322
KIO::SimpleJob::slotError
void slotError(int, const QString &)
Definition: job.cpp:500
KIO::SimpleJob::url
const KUrl & url() const
Returns the SimpleJob's URL.
Definition: job.cpp:341
KIO::SlaveConfig::configData
MetaData configData(const QString &protocol, const QString &host)
Query slave configuration for slaves of type protocol when dealing with host.
Definition: slaveconfig.cpp:187
KIO::SlaveConfig::setConfigData
void setConfigData(const QString &protocol, const QString &host, const QString &key, const QString &value)
Configure slaves of type protocol by setting key to value.
Definition: slaveconfig.cpp:155
KIO::SlaveConfig::self
static SlaveConfig * self()
Definition: slaveconfig.cpp:137
KIO::SlaveConfig::reset
void reset()
Undo any changes made by calls to setConfigData.
Definition: slaveconfig.cpp:211
KIO::SlaveKeeper::returnSlave
void returnSlave(KIO::Slave *slave)
Definition: scheduler.cpp:87
KIO::SlaveKeeper::takeSlaveForJob
KIO::Slave * takeSlaveForJob(KIO::SimpleJob *job)
Definition: scheduler.cpp:95
KIO::SlaveKeeper::SlaveKeeper
SlaveKeeper()
Definition: scheduler.cpp:81
KIO::SlaveKeeper::removeSlave
bool removeSlave(KIO::Slave *slave)
Definition: scheduler.cpp:116
KIO::SlaveKeeper::allSlaves
QList< KIO::Slave * > allSlaves() const
Definition: scheduler.cpp:129
KIO::Slave
Definition: slave.h:49
KIO::Slave::host
QString host()
Definition: slave.cpp:193
KIO::Slave::slave_pid
int slave_pid()
Definition: slave.cpp:267
KIO::Slave::holdSlave
static Slave * holdSlave(const QString &protocol, const KUrl &url)
Requests a slave on hold for ths url, from klauncher, if there is such a job.
Definition: slave.cpp:484
KIO::Slave::job
KIO::SimpleJob * job() const
Definition: slave.cpp:282
KIO::Slave::send
virtual void send(int cmd, const QByteArray &arr=QByteArray())
Sends the given command to the kioslave.
Definition: slave.cpp:332
KIO::Slave::hold
virtual void hold(const KUrl &url)
Puts the kioslave associated with url at halt, and return it to klauncher, in order to let another ap...
Definition: slave.cpp:294
KIO::Slave::setHost
virtual void setHost(const QString &host, quint16 port, const QString &user, const QString &passwd)
Set host for url.
Definition: slave.cpp:378
KIO::Slave::setJob
void setJob(KIO::SimpleJob *job)
Definition: slave.cpp:273
KIO::Slave::suspend
virtual void suspend()
Suspends the operation of the attached kioslave.
Definition: slave.cpp:314
KIO::Slave::checkForHeldSlave
static bool checkForHeldSlave(const KUrl &url)
Returns true if klauncher is holding a slave for url.
Definition: slave.cpp:508
KIO::Slave::createSlave
static Slave * createSlave(const QString &protocol, const KUrl &url, int &error, QString &error_text)
Creates a new slave.
Definition: slave.cpp:410
KIO::Slave::kill
void kill()
Force termination.
Definition: slave.cpp:361
KIO::Slave::user
QString user()
Definition: slave.cpp:205
KIO::Slave::setConnected
void setConnected(bool c)
Definition: slave.cpp:229
KIO::Slave::protocol
QString protocol()
The protocol this slave handles.
Definition: slave.cpp:175
KIO::Slave::idleTime
time_t idleTime()
Definition: slave.cpp:252
KIO::Slave::setIdle
void setIdle()
Marks this slave as idle.
Definition: slave.cpp:217
KIO::Slave::isConnected
bool isConnected()
Definition: slave.cpp:223
KIO::Slave::passwd
QString passwd()
Definition: slave.cpp:211
KIO::Slave::port
quint16 port()
Definition: slave.cpp:199
KIO::Slave::deref
void deref()
Definition: slave.cpp:241
KIO::Slave::setProtocol
void setProtocol(const QString &protocol)
Definition: slave.cpp:181
KIO::Slave::resetHost
void resetHost()
Clear host info.
Definition: slave.cpp:394
KIO::Slave::isAlive
bool isAlive()
Definition: slave.cpp:288
KIO::Slave::setConfig
virtual void setConfig(const MetaData &config)
Configure slave.
Definition: slave.cpp:401
KIO::TransferJob
The transfer job pumps data into and/or out of a Slave.
Definition: jobclasses.h:555
KJob::kill
bool kill(KillVerbosity verbosity=Quietly)
KProtocolInfo::maxSlavesPerHost
static int maxSlavesPerHost(const QString &protocol)
KProtocolInfo::maxSlaves
static int maxSlaves(const QString &protocol)
KProtocolManager::slaveProtocol
static QString slaveProtocol(const KUrl &url, QString &proxy)
Return the protocol to use in order to handle the given url It's usually the same,...
Definition: kprotocolmanager.cpp:525
KProtocolManager::reparseConfiguration
static void reparseConfiguration()
Force a reload of the general config file of io-slaves ( kioslaverc).
Definition: kprotocolmanager.cpp:232
KUrl
KUrl::pass
QString pass() const
KUrl::user
QString user() const
KUrl::protocol
QString protocol() const
QHash
QList
QMap
QObject
QSet
QWidget
connection.h
K_GLOBAL_STATIC
#define K_GLOBAL_STATIC(TYPE, NAME)
kDebug
#define kDebug
kWarning
#define kWarning
jobSlave
static Slave * jobSlave(SimpleJob *job)
Definition: job.cpp:68
job_p.h
kdebug.h
timeout
int timeout
kprotocolinfo.h
kprotocolmanager.h
config
KSharedConfigPtr config()
KIO
A namespace for KIO globals.
Definition: kbookmarkmenu.h:55
KIO::CMD_MULTI_GET
@ CMD_MULTI_GET
Definition: global.h:178
KIO::CMD_REPARSECONFIGURATION
@ CMD_REPARSECONFIGURATION
Definition: global.h:171
KIO::CMD_GET
@ CMD_GET
Definition: global.h:159
KIO::CMD_SPECIAL
@ CMD_SPECIAL
Definition: global.h:169
KIO::CMD_CONNECT
@ CMD_CONNECT
Definition: global.h:152
ok
KGuiItem ok()
mayReturnContent
static bool mayReturnContent(int cmd, const QString &protocol)
Definition: scheduler.cpp:946
jobSlave
static Slave * jobSlave(SimpleJob *job)
Definition: scheduler.cpp:47
topLevelWindow
static QWidget * topLevelWindow(QWidget *widget)
Definition: scheduler.cpp:1319
verifyRunningJobsCount
static void verifyRunningJobsCount(QHash< QString, HostQueue > *queues, int runningJobsCount)
Definition: scheduler.cpp:371
scheduler
static Scheduler * scheduler()
Definition: scheduler.cpp:761
jobCommand
static int jobCommand(SimpleJob *job)
Definition: scheduler.cpp:53
ensureNoDuplicates
static void ensureNoDuplicates(QMap< int, HostQueue * > *queuesBySerial)
Definition: scheduler.cpp:358
startJob
static void startJob(SimpleJob *job, Slave *slave)
Definition: scheduler.cpp:58
heldSlaveForJob
static Slave * heldSlaveForJob(SimpleJob *job)
Definition: scheduler.cpp:767
s_idleSlaveLifetime
static const int s_idleSlaveLifetime
Definition: scheduler.cpp:41
setupSlave
static void setupSlave(KIO::Slave *slave, const KUrl &url, const QString &protocol, const QStringList &proxyList, bool newSlave, const KIO::MetaData *config=0)
Definition: scheduler.cpp:1058
scheduler.h
scheduler_p.h
sessiondata.h
slave.h
slaveconfig.h
KIO::NetRC::AutoLogin
Contains auto login information.
Definition: authinfo.h:333
KIO::NetRC::AutoLogin::login
QString login
Definition: authinfo.h:336
KIO::NetRC::AutoLogin::macdef
QMap< QString, QStringList > macdef
Definition: authinfo.h:338
KIO::NetRC::AutoLogin::password
QString password
Definition: authinfo.h:337
KIO::PerSlaveQueue
Definition: scheduler_p.h:76
KIO::PerSlaveQueue::waitingList
QList< SimpleJob * > waitingList
Definition: scheduler_p.h:78
KIO::PerSlaveQueue::runningJob
SimpleJob * runningJob
Definition: scheduler_p.h:79
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 20 2023 00:00:00 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KIO

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

kdelibs-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal