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

KIO

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

KDE's Doxygen guidelines are available online.

KIO

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

kdelibs-4.10.4 API Reference

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

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