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 "sessiondata_p.h"
14 #include "slaveconfig.h"
15 #include "slave.h"
16 #include "connection_p.h"
17 #include "job_p.h"
18 
19 #include <kprotocolmanager.h>
20 #include <kprotocolinfo.h>
21 //#include <KJobWidgets>
22 
23 #include <QHash>
24 #include <QThread>
25 #include <QThreadStorage>
26 #include <QDBusConnection>
27 #include <QDBusMessage>
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 setupSlave(KIO::Slave *slave, const QUrl &url, const QString &protocol,
52  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*)),
537  SLOT(slotSlaveDied(KIO::Slave*)));
538  scheduler()->connect(slave, SIGNAL(slaveStatus(qint64,QByteArray,QString,bool)),
539  SLOT(slotSlaveStatus(qint64,QByteArray,QString,bool)));
540  } else {
541  qCWarning(KIO_CORE) << "couldn't create slave:" << errortext;
542  if (job) {
543  job->slotError(error, errortext);
544  }
545  }
546  return slave;
547 }
548 
549 bool ProtoQueue::removeSlave(KIO::Slave *slave)
550 {
551  const bool removedConnected = m_connectedSlaveQueue.removeSlave(slave);
552  const bool removedUnconnected = m_slaveKeeper.removeSlave(slave);
553  Q_ASSERT(!(removedConnected && removedUnconnected));
554  return removedConnected || removedUnconnected;
555 }
556 
557 QList<Slave *> ProtoQueue::allSlaves() const
558 {
559  QList<Slave *> ret(m_slaveKeeper.allSlaves());
560  auto it = m_queuesByHostname.cbegin();
561  for (; it != m_queuesByHostname.cend(); ++it) {
562  ret.append(it.value().allSlaves());
563  }
564  ret.append(m_connectedSlaveQueue.allSlaves());
565  return ret;
566 }
567 
568 //private slot
569 void ProtoQueue::startAJob()
570 {
571  ensureNoDuplicates(&m_queuesBySerial);
572  verifyRunningJobsCount(&m_queuesByHostname, m_runningJobsCount);
573 
574 #ifdef SCHEDULER_DEBUG
575  //qDebug() << "m_runningJobsCount:" << m_runningJobsCount;
576  auto it = m_queuesByHostname.cbegin();
577  for (; it != m_queuesByHostname.cend(); ++it) {
578  const QList<KIO::SimpleJob *> list = it.value().runningJobs();
579  for (SimpleJob *job : list) {
580  //qDebug() << SimpleJobPrivate::get(job)->m_url;
581  }
582  }
583 #endif
584  if (m_runningJobsCount >= m_maxConnectionsTotal) {
585 #ifdef SCHEDULER_DEBUG
586  //qDebug() << "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  //qDebug() << "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 class KIO::SchedulerPrivate
653 {
654 public:
655  SchedulerPrivate()
656  : q(new Scheduler()),
657  m_slaveOnHold(nullptr),
658  m_checkOnHold(true), // !! Always check with KLauncher for the first request
659  m_ignoreConfigReparse(false)
660  {
661  }
662 
663  ~SchedulerPrivate()
664  {
665  delete q;
666  q = nullptr;
667  for (ProtoQueue *p : qAsConst(m_protocols)) {
668  const QList<KIO::Slave *> list = p->allSlaves();
669  for (Slave *slave : list) {
670  slave->kill();
671  }
672  }
673  qDeleteAll(m_protocols);
674  }
675 
676  SchedulerPrivate(const SchedulerPrivate &) = delete;
677  SchedulerPrivate &operator=(const SchedulerPrivate &) = delete;
678 
679  Scheduler *q;
680 
681  Slave *m_slaveOnHold;
682  QUrl m_urlOnHold;
683  bool m_checkOnHold;
684  bool m_ignoreConfigReparse;
685 
686  SessionData sessionData;
687 
688  void doJob(SimpleJob *job);
689 #if KIOCORE_BUILD_DEPRECATED_SINCE(4, 5)
690  void scheduleJob(SimpleJob *job);
691 #endif
692  void setJobPriority(SimpleJob *job, int priority);
693  void cancelJob(SimpleJob *job);
694  void jobFinished(KIO::SimpleJob *job, KIO::Slave *slave);
695  void putSlaveOnHold(KIO::SimpleJob *job, const QUrl &url);
696  void removeSlaveOnHold();
697  Slave *getConnectedSlave(const QUrl &url, const KIO::MetaData &metaData);
698  bool assignJobToSlave(KIO::Slave *slave, KIO::SimpleJob *job);
699  bool disconnectSlave(KIO::Slave *slave);
700  void checkSlaveOnHold(bool b);
701  void publishSlaveOnHold();
702  Slave *heldSlaveForJob(KIO::SimpleJob *job);
703  bool isSlaveOnHoldFor(const QUrl &url);
704  void updateInternalMetaData(SimpleJob *job);
705 
706  MetaData metaDataFor(const QString &protocol, const QStringList &proxyList, const QUrl &url);
707  void setupSlave(KIO::Slave *slave, const QUrl &url, const QString &protocol,
708  const QStringList &proxyList, bool newSlave, const KIO::MetaData *config = nullptr);
709 
710  void slotSlaveDied(KIO::Slave *slave);
711  void slotSlaveStatus(qint64 pid, const QByteArray &protocol,
712  const QString &host, bool connected);
713 
714  void slotReparseSlaveConfiguration(const QString &, const QDBusMessage &);
715  void slotSlaveOnHoldListChanged();
716 
717  void slotSlaveConnected();
718  void slotSlaveError(int error, const QString &errorMsg);
719 
720  ProtoQueue *protoQ(const QString &protocol, const QString &host)
721  {
722  ProtoQueue *pq = m_protocols.value(protocol, nullptr);
723  if (!pq) {
724  //qDebug() << "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, QStringLiteral("MaxConnections")).toInt(&ok);
731  if (ok) {
732  maxSlavesPerHost = value;
733  }
734  }
735  if (maxSlavesPerHost == -1) {
736  maxSlavesPerHost = KProtocolInfo::maxSlavesPerHost(protocol);
737  }
738  // Never allow maxSlavesPerHost to exceed maxSlaves.
739  pq = new ProtoQueue(maxSlaves, qMin(maxSlaves, maxSlavesPerHost));
740  m_protocols.insert(protocol, pq);
741  }
742  return pq;
743  }
744 private:
745  QHash<QString, ProtoQueue *> m_protocols;
746 };
747 
748 static QThreadStorage<SchedulerPrivate *> s_storage;
749 static SchedulerPrivate *schedulerPrivate()
750 {
751  if (!s_storage.hasLocalData()) {
752  s_storage.setLocalData(new SchedulerPrivate);
753  }
754  return s_storage.localData();
755 }
756 
757 Scheduler *Scheduler::self()
758 {
759  return schedulerPrivate()->q;
760 }
761 
762 SchedulerPrivate *Scheduler::d_func()
763 {
764  return schedulerPrivate();
765 }
766 
767 //static
768 Scheduler *scheduler()
769 {
770  return schedulerPrivate()->q;
771 }
772 
773 //static
774 Slave *heldSlaveForJob(SimpleJob *job)
775 {
776  return schedulerPrivate()->heldSlaveForJob(job);
777 }
778 
779 Scheduler::Scheduler()
780 {
781  setObjectName(QStringLiteral("scheduler"));
782 
783  const QString dbusPath = QStringLiteral("/KIO/Scheduler");
784  const QString dbusInterface = QStringLiteral("org.kde.KIO.Scheduler");
786  // Not needed, right? We just want to emit two signals.
787  //dbus.registerObject(dbusPath, this, QDBusConnection::ExportScriptableSlots |
788  // QDBusConnection::ExportScriptableSignals);
789  dbus.connect(QString(), dbusPath, dbusInterface, QStringLiteral("reparseSlaveConfiguration"),
790  this, SLOT(slotReparseSlaveConfiguration(QString,QDBusMessage)));
791  dbus.connect(QString(), dbusPath, dbusInterface, QStringLiteral("slaveOnHoldListChanged"),
792  this, SLOT(slotSlaveOnHoldListChanged()));
793 }
794 
795 Scheduler::~Scheduler()
796 {
797 }
798 
800 {
801  schedulerPrivate()->doJob(job);
802 }
803 
804 #if KIOCORE_BUILD_DEPRECATED_SINCE(4, 5)
806 {
807  schedulerPrivate()->scheduleJob(job);
808 }
809 #endif
810 
811 void Scheduler::setJobPriority(SimpleJob *job, int priority)
812 {
813  schedulerPrivate()->setJobPriority(job, priority);
814 }
815 
817 {
818  schedulerPrivate()->cancelJob(job);
819 }
820 
821 void Scheduler::jobFinished(KIO::SimpleJob *job, KIO::Slave *slave)
822 {
823  schedulerPrivate()->jobFinished(job, slave);
824 }
825 
827 {
828  schedulerPrivate()->putSlaveOnHold(job, url);
829 }
830 
832 {
833  schedulerPrivate()->removeSlaveOnHold();
834 }
835 
837 {
838  schedulerPrivate()->publishSlaveOnHold();
839 }
840 
842 {
843  return schedulerPrivate()->isSlaveOnHoldFor(url);
844 }
845 
847 {
848  schedulerPrivate()->updateInternalMetaData(job);
849 }
850 
851 KIO::Slave *Scheduler::getConnectedSlave(const QUrl &url,
852  const KIO::MetaData &config)
853 {
854  return schedulerPrivate()->getConnectedSlave(url, config);
855 }
856 
857 bool Scheduler::assignJobToSlave(KIO::Slave *slave, KIO::SimpleJob *job)
858 {
859  return schedulerPrivate()->assignJobToSlave(slave, job);
860 }
861 
862 bool Scheduler::disconnectSlave(KIO::Slave *slave)
863 {
864  return schedulerPrivate()->disconnectSlave(slave);
865 }
866 
867 bool Scheduler::connect(const char *signal, const QObject *receiver,
868  const char *member)
869 {
870  return QObject::connect(self(), signal, receiver, member);
871 }
872 
873 bool Scheduler::connect(const QObject *sender, const char *signal,
874  const QObject *receiver, const char *member)
875 {
876  return QObject::connect(sender, signal, receiver, member);
877 }
878 
879 bool Scheduler::disconnect(const QObject *sender, const char *signal,
880  const QObject *receiver, const char *member)
881 {
882  return QObject::disconnect(sender, signal, receiver, member);
883 }
884 
885 bool Scheduler::connect(const QObject *sender, const char *signal,
886  const char *member)
887 {
888  return QObject::connect(sender, signal, member);
889 }
890 
892 {
893  schedulerPrivate()->checkSlaveOnHold(b);
894 }
895 
896 void Scheduler::emitReparseSlaveConfiguration()
897 {
898  // Do it immediately in this process, otherwise we might send a request before reparsing
899  // (e.g. when changing useragent in the plugin)
900  schedulerPrivate()->slotReparseSlaveConfiguration(QString(), QDBusMessage());
901 
902  schedulerPrivate()->m_ignoreConfigReparse = true;
903  emit self()->reparseSlaveConfiguration(QString());
904 }
905 
906 void SchedulerPrivate::slotReparseSlaveConfiguration(const QString &proto, const QDBusMessage &)
907 {
908  if (m_ignoreConfigReparse) {
909  //qDebug() << "Ignoring signal sent by myself";
910  m_ignoreConfigReparse = false;
911  return;
912  }
913 
914  //qDebug() << "proto=" << proto;
916  SlaveConfig::self()->reset();
917  sessionData.reset();
918  NetRC::self()->reload();
919 
920  QHash<QString, ProtoQueue *>::ConstIterator it = proto.isEmpty() ? m_protocols.constBegin() :
921  m_protocols.constFind(proto);
923 
924  // not found?
925  if (it == endIt) {
926  return;
927  }
928 
929  if (!proto.isEmpty()) {
930  endIt = it;
931  ++endIt;
932  }
933 
934  for (; it != endIt; ++it) {
935  const QList<KIO::Slave *> list = it.value()->allSlaves();
936  for (Slave *slave : list) {
937  slave->send(CMD_REPARSECONFIGURATION);
938  slave->resetHost();
939  }
940  }
941 }
942 
943 void SchedulerPrivate::slotSlaveOnHoldListChanged()
944 {
945  m_checkOnHold = true;
946 }
947 
948 static bool mayReturnContent(int cmd, const QString &protocol)
949 {
950  if (cmd == CMD_GET) {
951  return true;
952  }
953 
954  if (cmd == CMD_MULTI_GET) {
955  return true;
956  }
957 
958  if (cmd == CMD_SPECIAL && protocol.startsWith(QLatin1String("http"), Qt::CaseInsensitive)) {
959  return true;
960  }
961 
962  return false;
963 }
964 
965 void SchedulerPrivate::doJob(SimpleJob *job)
966 {
967  //qDebug() << job;
968  KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job);
969  jobPriv->m_proxyList.clear();
970  jobPriv->m_protocol = KProtocolManager::slaveProtocol(job->url(), jobPriv->m_proxyList);
971 
972  if (mayReturnContent(jobCommand(job), jobPriv->m_protocol)) {
973  jobPriv->m_checkOnHold = m_checkOnHold;
974  m_checkOnHold = false;
975  }
976 
977  ProtoQueue *proto = protoQ(jobPriv->m_protocol, job->url().host());
978  proto->queueJob(job);
979 }
980 
981 #if KIOCORE_BUILD_DEPRECATED_SINCE(4, 5)
982 void SchedulerPrivate::scheduleJob(SimpleJob *job)
983 {
984  //qDebug() << job;
985  setJobPriority(job, 1);
986 }
987 #endif
988 
989 void SchedulerPrivate::setJobPriority(SimpleJob *job, int priority)
990 {
991  //qDebug() << job << priority;
992  const QString protocol = SimpleJobPrivate::get(job)->m_protocol;
993  if (!protocol.isEmpty()) {
994  ProtoQueue *proto = protoQ(SimpleJobPrivate::get(job)->m_protocol, job->url().host());
995  proto->changeJobPriority(job, priority);
996  }
997 }
998 
999 void SchedulerPrivate::cancelJob(SimpleJob *job)
1000 {
1001  // this method is called all over the place in job.cpp, so just do this check here to avoid
1002  // much boilerplate in job code.
1003  if (SimpleJobPrivate::get(job)->m_schedSerial == 0) {
1004  //qDebug() << "Doing nothing because I don't know job" << job;
1005  return;
1006  }
1007  Slave *slave = jobSlave(job);
1008  //qDebug() << job << slave;
1009  if (slave) {
1010  //qDebug() << "Scheduler: killing slave " << slave->slave_pid();
1011  slave->kill();
1012  }
1013  jobFinished(job, slave);
1014 }
1015 
1016 void SchedulerPrivate::jobFinished(SimpleJob *job, Slave *slave)
1017 {
1018  //qDebug() << job << slave;
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  //qDebug() << "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  /*qDebug() << "Updated configuration of" << slave->protocol()
1042  << "ioslave, pid=" << slave->slave_pid();*/
1043  }
1044  }
1045  }
1046  }
1047  slave->setJob(nullptr);
1048  slave->disconnect(job);
1049  }
1050  jobPriv->m_schedSerial = 0; // this marks the job as unscheduled again
1051  jobPriv->m_slave = nullptr;
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
1058 void setupSlave(KIO::Slave *slave, const QUrl &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 
1064 MetaData SchedulerPrivate::metaDataFor(const QString &protocol, const QStringList &proxyList, const QUrl &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(QStringLiteral("UseProxy"));
1071  configData.remove(QStringLiteral("ProxyUrls"));
1072  } else {
1073  configData[QStringLiteral("UseProxy")] = proxyList.first();
1074  configData[QStringLiteral("ProxyUrls")] = proxyList.join(QLatin1Char(','));
1075  }
1076 
1077  if (configData.contains(QLatin1String("EnableAutoLogin")) &&
1078  configData.value(QStringLiteral("EnableAutoLogin")).compare(QLatin1String("true"), Qt::CaseInsensitive) == 0) {
1079  NetRC::AutoLogin l;
1080  l.login = url.userName();
1081  bool usern = (protocol == QLatin1String("ftp"));
1082  if (NetRC::self()->lookup(url, l, usern)) {
1083  configData[QStringLiteral("autoLoginUser")] = l.login;
1084  configData[QStringLiteral("autoLoginPass")] = l.password;
1085  if (usern) {
1086  QString macdef;
1088  for (; it != l.macdef.constEnd(); ++it) {
1089  macdef += it.key() + QLatin1Char('\\') + it.value().join(QLatin1Char('\\')) + QLatin1Char('\n');
1090  }
1091  configData[QStringLiteral("autoLoginMacro")] = macdef;
1092  }
1093  }
1094  }
1095 
1096  return configData;
1097 }
1098 
1099 void SchedulerPrivate::setupSlave(KIO::Slave *slave, const QUrl &url, const QString &protocol,
1100  const QStringList &proxyList, bool newSlave, const KIO::MetaData *config)
1101 {
1102  int port = url.port();
1103  if (port == -1) { // no port is -1 in QUrl, but in kde3 we used 0 and the kioslaves assume that.
1104  port = 0;
1105  }
1106  const QString host = url.host();
1107  const QString user = url.userName();
1108  const QString passwd = url.password();
1109 
1110  if (newSlave || slave->host() != host || slave->port() != port ||
1111  slave->user() != user || slave->passwd() != passwd) {
1112 
1113  MetaData configData = metaDataFor(protocol, proxyList, url);
1114  if (config) {
1115  configData += *config;
1116  }
1117 
1118  slave->setConfig(configData);
1119  slave->setProtocol(url.scheme());
1120  slave->setHost(host, port, user, passwd);
1121  }
1122 }
1123 
1124 void SchedulerPrivate::slotSlaveStatus(qint64, const QByteArray &, const QString &, bool)
1125 {
1126 }
1127 
1128 void SchedulerPrivate::slotSlaveDied(KIO::Slave *slave)
1129 {
1130  //qDebug() << slave;
1131  Q_ASSERT(slave);
1132  Q_ASSERT(!slave->isAlive());
1133  ProtoQueue *pq = m_protocols.value(slave->protocol());
1134  if (pq) {
1135  if (slave->job()) {
1136  pq->removeJob(slave->job());
1137  }
1138  // in case this was a connected slave...
1139  pq->removeSlave(slave);
1140  }
1141  if (slave == m_slaveOnHold) {
1142  m_slaveOnHold = nullptr;
1143  m_urlOnHold.clear();
1144  }
1145  // can't use slave->deref() here because we need to use deleteLater
1146  slave->aboutToDelete();
1147  slave->deleteLater();
1148 }
1149 
1150 void SchedulerPrivate::putSlaveOnHold(KIO::SimpleJob *job, const QUrl &url)
1151 {
1152  Slave *slave = jobSlave(job);
1153  //qDebug() << 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 QUrl &url) called in SchedulerPrivate::publishSlaveOnHold().
1157  slave->setJob(nullptr);
1158  SimpleJobPrivate::get(job)->m_slave = nullptr;
1159 
1160  if (m_slaveOnHold) {
1161  m_slaveOnHold->kill();
1162  }
1163  m_slaveOnHold = slave;
1164  m_urlOnHold = url;
1165  m_slaveOnHold->suspend();
1166 }
1167 
1168 void SchedulerPrivate::publishSlaveOnHold()
1169 {
1170  //qDebug() << m_slaveOnHold;
1171  if (!m_slaveOnHold) {
1172  return;
1173  }
1174 
1175  m_slaveOnHold->hold(m_urlOnHold);
1176  emit q->slaveOnHoldListChanged();
1177 }
1178 
1179 bool SchedulerPrivate::isSlaveOnHoldFor(const QUrl &url)
1180 {
1181  if (url.isValid() && m_urlOnHold.isValid() && url == m_urlOnHold) {
1182  return true;
1183  }
1184 
1185  return Slave::checkForHeldSlave(url);
1186 }
1187 
1188 Slave *SchedulerPrivate::heldSlaveForJob(SimpleJob *job)
1189 {
1190  Slave *slave = nullptr;
1191  KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job);
1192 
1193  if (jobPriv->m_checkOnHold) {
1194  slave = Slave::holdSlave(jobPriv->m_protocol, job->url());
1195  }
1196 
1197  if (!slave && m_slaveOnHold) {
1198  // Make sure that the job wants to do a GET or a POST, and with no offset
1199  const int cmd = jobPriv->m_command;
1200  bool canJobReuse = (cmd == CMD_GET || cmd == CMD_MULTI_GET);
1201 
1202  if (KIO::TransferJob *tJob = qobject_cast<KIO::TransferJob *>(job)) {
1203  canJobReuse = (canJobReuse || cmd == CMD_SPECIAL);
1204  if (canJobReuse) {
1205  KIO::MetaData outgoing = tJob->outgoingMetaData();
1206  const QString resume = outgoing.value(QStringLiteral("resume"));
1207  const QString rangeStart = outgoing.value(QStringLiteral("range-start"));
1208  //qDebug() << "Resume metadata is" << resume;
1209  canJobReuse = (resume.isEmpty() || resume == QLatin1Char('0')) && (rangeStart.isEmpty() || rangeStart == QLatin1Char('0'));
1210  }
1211  }
1212 
1213  if (job->url() == m_urlOnHold) {
1214  if (canJobReuse) {
1215  //qDebug() << "HOLD: Reusing held slave (" << m_slaveOnHold << ")";
1216  slave = m_slaveOnHold;
1217  } else {
1218  //qDebug() << "HOLD: Discarding held slave (" << m_slaveOnHold << ")";
1219  m_slaveOnHold->kill();
1220  }
1221  m_slaveOnHold = nullptr;
1222  m_urlOnHold.clear();
1223  }
1224  } else if (slave) {
1225  //qDebug() << "HOLD: Reusing klauncher held slave (" << slave << ")";
1226  }
1227 
1228  return slave;
1229 }
1230 
1231 void SchedulerPrivate::removeSlaveOnHold()
1232 {
1233  //qDebug() << m_slaveOnHold;
1234  if (m_slaveOnHold) {
1235  m_slaveOnHold->kill();
1236  }
1237  m_slaveOnHold = nullptr;
1238  m_urlOnHold.clear();
1239 }
1240 
1241 Slave *SchedulerPrivate::getConnectedSlave(const QUrl &url, const KIO::MetaData &config)
1242 {
1243  QStringList proxyList;
1244  const QString protocol = KProtocolManager::slaveProtocol(url, proxyList);
1245  ProtoQueue *pq = protoQ(protocol, url.host());
1246 
1247  Slave *slave = pq->createSlave(protocol, /* job */nullptr, url);
1248  if (slave) {
1249  setupSlave(slave, url, protocol, proxyList, true, &config);
1250  pq->m_connectedSlaveQueue.addSlave(slave);
1251 
1252  slave->send(CMD_CONNECT);
1253  q->connect(slave, SIGNAL(connected()),
1254  SLOT(slotSlaveConnected()));
1255  q->connect(slave, SIGNAL(error(int,QString)),
1256  SLOT(slotSlaveError(int,QString)));
1257  }
1258  //qDebug() << url << slave;
1259  return slave;
1260 }
1261 
1262 void SchedulerPrivate::slotSlaveConnected()
1263 {
1264  //qDebug();
1265  Slave *slave = static_cast<Slave *>(q->sender());
1266  slave->setConnected(true);
1267  q->disconnect(slave, SIGNAL(connected()), q, SLOT(slotSlaveConnected()));
1268  emit q->slaveConnected(slave);
1269 }
1270 
1271 void SchedulerPrivate::slotSlaveError(int errorNr, const QString &errorMsg)
1272 {
1273  Slave *slave = static_cast<Slave *>(q->sender());
1274  //qDebug() << slave << errorNr << errorMsg;
1275  ProtoQueue *pq = protoQ(slave->protocol(), slave->host());
1276  if (!slave->isConnected() || pq->m_connectedSlaveQueue.isIdle(slave)) {
1277  // Only forward to application if slave is idle or still connecting.
1278  // ### KDE5: can we remove this apparently arbitrary behavior and just always emit SlaveError?
1279  emit q->slaveError(slave, errorNr, errorMsg);
1280  }
1281 }
1282 
1283 bool SchedulerPrivate::assignJobToSlave(KIO::Slave *slave, SimpleJob *job)
1284 {
1285  //qDebug() << slave << job;
1286  // KDE5: queueing of jobs can probably be removed, it provides very little benefit
1287  ProtoQueue *pq = m_protocols.value(slave->protocol());
1288  if (pq) {
1289  pq->removeJob(job);
1290  return pq->m_connectedSlaveQueue.queueJob(job, slave);
1291  }
1292  return false;
1293 }
1294 
1295 bool SchedulerPrivate::disconnectSlave(KIO::Slave *slave)
1296 {
1297  //qDebug() << slave;
1298  ProtoQueue *pq = m_protocols.value(slave->protocol());
1299  return (pq ? pq->m_connectedSlaveQueue.removeSlave(slave) : false);
1300 }
1301 
1302 void SchedulerPrivate::checkSlaveOnHold(bool b)
1303 {
1304  //qDebug() << b;
1305  m_checkOnHold = b;
1306 }
1307 
1308 void SchedulerPrivate::updateInternalMetaData(SimpleJob *job)
1309 {
1310  KIO::SimpleJobPrivate *const jobPriv = SimpleJobPrivate::get(job);
1311  // Preserve all internal meta-data so they can be sent back to the
1312  // ioslaves as needed...
1313  const QUrl jobUrl = job->url();
1314  //qDebug() << job << jobPriv->m_internalMetaData;
1315  QMapIterator<QString, QString> it(jobPriv->m_internalMetaData);
1316  while (it.hasNext()) {
1317  it.next();
1318  if (it.key().startsWith(QLatin1String("{internal~currenthost}"), Qt::CaseInsensitive)) {
1319  SlaveConfig::self()->setConfigData(jobUrl.scheme(), jobUrl.host(), it.key().mid(22), it.value());
1320  } else if (it.key().startsWith(QLatin1String("{internal~allhosts}"), Qt::CaseInsensitive)) {
1321  SlaveConfig::self()->setConfigData(jobUrl.scheme(), QString(), it.key().mid(19), it.value());
1322  }
1323  }
1324 }
1325 
1326 #include "moc_scheduler.cpp"
1327 #include "moc_scheduler_p.cpp"
static void removeSlaveOnHold()
Removes any slave that might have been put on hold.
Definition: scheduler.cpp:831
bool kill(KillVerbosity verbosity=Quietly)
static void scheduleJob(SimpleJob *job)
Schedules job scheduled for later execution.
Definition: scheduler.cpp:805
void clear()
void clear()
static void doJob(SimpleJob *job)
Register job with the scheduler.
Definition: scheduler.cpp:799
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:811
A namespace for KIO globals.
Definition: authinfo.h:21
static NetRC * self()
A reference to the instance of the class.
Definition: authinfo.cpp:275
QObject * sender() const const
static bool disconnectSlave(KIO::Slave *slave)
Disconnects slave.
Definition: scheduler.cpp:862
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:224
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:23
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:846
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:891
void timeout()
static bool assignJobToSlave(KIO::Slave *slave, KIO::SimpleJob *job)
Uses slave to do job.
Definition: scheduler.cpp:857
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:821
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:826
static void cancelJob(SimpleJob *job)
Stop the execution of a job.
Definition: scheduler.cpp:816
MetaData metaData() const
Get meta data received from the slave.
Definition: job.cpp:215
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:841
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:97
QList::const_iterator cbegin() const const
NETWORKMANAGERQT_EXPORT QString hostname()
QSet::iterator end()
void reload()
Reloads the auto login information.
Definition: authinfo.cpp:358
QHash::iterator end()
The transfer job pumps data into and/or out of a Slave.
Definition: transferjob.h:26
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:836
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:851
static bool connect(const char *signal, const QObject *receiver, const char *member)
Function to connect signals emitted by the scheduler.
Definition: scheduler.cpp:867
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:283
QSet::iterator find(const T &value)
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:325
A simple job (one url and one command).
Definition: simplejob.h:27
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Nov 30 2020 23:01:47 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.