KIO

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

KDE's Doxygen guidelines are available online.