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

KDE's Doxygen guidelines are available online.