24 #include "config-plasma.h"
28 #include <QCoreApplication>
31 #include <kplugininfo.h>
32 #include <kservicetypetrader.h>
33 #include <kstandarddirs.h>
35 #ifndef PLASMA_NO_SOLID
36 #include <solid/device.h>
37 #include <solid/deviceinterface.h>
40 #include <Weaver/DebuggingAids.h>
41 #include <Weaver/State.h>
42 #include <Weaver/Thread.h>
43 #include <Weaver/ThreadWeaver.h>
45 #include "private/runnerjobs_p.h"
49 using ThreadWeaver::Weaver;
50 using ThreadWeaver::Job;
61 class RunnerManagerPrivate
65 RunnerManagerPrivate(RunnerManager *parent)
68 currentSingleRunner(0),
70 allRunnersPrepped(false),
71 singleRunnerPrepped(false),
72 teardownRequested(false),
74 singleRunnerWasLoaded(false)
76 matchChangeTimer.setSingleShot(
true);
77 delayTimer.setSingleShot(
true);
79 QObject::connect(&matchChangeTimer, SIGNAL(timeout()), q, SLOT(matchesChanged()));
80 QObject::connect(&context, SIGNAL(matchesChanged()), q, SLOT(scheduleMatchesChanged()));
81 QObject::connect(&delayTimer, SIGNAL(timeout()), q, SLOT(unblockJobs()));
84 ~RunnerManagerPrivate()
86 KConfigGroup config = configGroup();
90 void scheduleMatchesChanged()
92 matchChangeTimer.start(100);
97 emit q->matchesChanged(context.matches());
100 void loadConfiguration()
102 KConfigGroup config = configGroup();
105 #ifndef PLASMA_NO_SOLID
107 qMax(Solid::Device::listFromType(Solid::DeviceInterface::Processor).count(), 1);
109 const int numProcs = 1;
112 const int maxThreads = config.readEntry(
"maxThreads", 16);
113 const int numThreads = qMin(maxThreads, 2 + ((numProcs - 1) * 2));
115 if (numThreads > Weaver::instance()->maximumNumberOfThreads()) {
116 Weaver::instance()->setMaximumNumberOfThreads(numThreads);
120 const int cap = qMax(2, numThreads/2);
121 DefaultRunnerPolicy::instance().setCap(cap);
123 context.restore(config);
126 KConfigGroup configGroup()
128 return conf.isValid() ? conf : KConfigGroup(KGlobal::config(),
"PlasmaRunnerManager");
131 void clearSingleRunner()
133 if (singleRunnerWasLoaded) {
134 delete currentSingleRunner;
137 currentSingleRunner = 0;
140 void loadSingleRunner()
142 if (!singleMode || singleModeRunnerId.isEmpty()) {
147 if (currentSingleRunner) {
148 if (currentSingleRunner->id() == singleModeRunnerId) {
155 AbstractRunner *loadedRunner = q->runner(singleModeRunnerId);
157 singleRunnerWasLoaded =
false;
158 currentSingleRunner = loadedRunner;
162 KService::List offers = KServiceTypeTrader::self()->query(
"Plasma/Runner", QString(
"[X-KDE-PluginInfo-Name] == '%1'").arg(singleModeRunnerId));
163 if (!offers.isEmpty()) {
164 const KService::Ptr &service = offers[0];
165 currentSingleRunner = loadInstalledRunner(service);
167 if (currentSingleRunner) {
168 emit currentSingleRunner->prepare();
169 singleRunnerWasLoaded =
true;
176 KConfigGroup config = configGroup();
179 const bool loadAll = config.readEntry(
"loadAll",
false);
180 const QStringList whiteList = config.readEntry(
"pluginWhiteList", QStringList());
181 const bool noWhiteList = whiteList.isEmpty();
182 KConfigGroup pluginConf;
183 if (conf.isValid()) {
184 pluginConf = KConfigGroup(&conf,
"Plugins");
186 pluginConf = KConfigGroup(KGlobal::config(),
"Plugins");
189 advertiseSingleRunnerIds.clear();
191 QSet<AbstractRunner *> deadRunners;
192 QMutableListIterator<KPluginInfo> it(offers);
193 while (it.hasNext()) {
194 KPluginInfo &description = it.next();
196 QString tryExec = description.property(
"TryExec").toString();
198 if (!tryExec.isEmpty() && KStandardDirs::findExe(tryExec).isEmpty()) {
203 const QString runnerName = description.pluginName();
204 description.load(pluginConf);
206 const bool loaded = runners.contains(runnerName);
207 const bool selected = loadAll || (description.isPluginEnabled() && (noWhiteList || whiteList.contains(runnerName)));
209 const bool singleQueryModeEnabled = description.property(
"X-Plasma-AdvertiseSingleRunnerQueryMode").toBool();
211 if (singleQueryModeEnabled) {
212 advertiseSingleRunnerIds.insert(runnerName, description.name());
218 AbstractRunner *runner = loadInstalledRunner(description.service());
221 runners.insert(runnerName, runner);
226 deadRunners.insert(runners.take(runnerName));
227 kDebug() <<
"Removing runner: " << runnerName;
231 if (!deadRunners.isEmpty()) {
232 QSet<FindMatchesJob *> deadJobs;
233 foreach (FindMatchesJob *job, searchJobs) {
234 if (deadRunners.contains(job->runner())) {
235 QObject::disconnect(job, SIGNAL(done(ThreadWeaver::Job*)), q, SLOT(jobDone(ThreadWeaver::Job*)));
236 searchJobs.remove(job);
237 deadJobs.insert(job);
241 foreach (FindMatchesJob *job, oldSearchJobs) {
242 if (deadRunners.contains(job->runner())) {
243 oldSearchJobs.remove(job);
244 deadJobs.insert(job);
248 if (deadJobs.isEmpty()) {
249 qDeleteAll(deadRunners);
251 new DelayedJobCleaner(deadJobs, deadRunners);
255 if (!singleRunnerWasLoaded) {
260 kDebug() <<
"All runners loaded, total:" << runners.count();
263 AbstractRunner *loadInstalledRunner(
const KService::Ptr service)
272 runner->setParent(q);
274 const QString api = service->property(
"X-Plasma-API").toString();
278 args << service->storageId();
281 runner = service->createInstance<AbstractRunner>(q, args, &error);
283 kDebug() <<
"Failed to load runner:" << service->
name() <<
". error reported:" << error;
288 runner =
new AbstractRunner(service, q);
293 kDebug() <<
"================= loading runner:" << service->name() <<
"=================";
294 QObject::connect(runner, SIGNAL(matchingSuspended(
bool)), q, SLOT(runnerMatchingSuspended(
bool)));
295 QMetaObject::invokeMethod(runner,
"init");
297 emit runner->prepare();
304 void jobDone(ThreadWeaver::Job *job)
306 FindMatchesJob *runJob =
dynamic_cast<FindMatchesJob *
>(job);
312 if (deferredRun.isEnabled() && runJob->runner() == deferredRun.runner()) {
314 QueryMatch tmpRun = deferredRun;
315 deferredRun = QueryMatch(0);
319 searchJobs.remove(runJob);
320 oldSearchJobs.remove(runJob);
321 runJob->deleteLater();
323 if (searchJobs.isEmpty() && context.matches().isEmpty()) {
327 emit q->matchesChanged(context.matches());
337 if (!prepped || !teardownRequested) {
341 if (Weaver::instance()->isIdle()) {
342 qDeleteAll(searchJobs);
344 qDeleteAll(oldSearchJobs);
345 oldSearchJobs.clear();
348 if (searchJobs.isEmpty() && oldSearchJobs.isEmpty()) {
349 if (allRunnersPrepped) {
350 foreach (AbstractRunner *runner, runners) {
351 emit runner->teardown();
354 allRunnersPrepped =
false;
357 if (singleRunnerPrepped) {
358 if (currentSingleRunner) {
359 emit currentSingleRunner->teardown();
362 singleRunnerPrepped =
false;
365 emit q->queryFinished();
368 teardownRequested =
false;
375 if (searchJobs.isEmpty() && Weaver::instance()->isIdle()) {
376 qDeleteAll(oldSearchJobs);
377 oldSearchJobs.clear();
382 DummyJob *dummy =
new DummyJob(q);
383 Weaver::instance()->enqueue(dummy);
384 QObject::connect(dummy, SIGNAL(done(ThreadWeaver::Job*)), dummy, SLOT(deleteLater()));
387 void runnerMatchingSuspended(
bool suspended)
389 if (suspended || !prepped || teardownRequested) {
393 AbstractRunner *runner = qobject_cast<AbstractRunner *>(q->sender());
400 void startJob(AbstractRunner *runner)
402 if ((runner->ignoredTypes() & context.type()) == 0) {
403 FindMatchesJob *job =
new FindMatchesJob(runner, &context, Weaver::instance());
404 QObject::connect(job, SIGNAL(done(ThreadWeaver::Job*)), q, SLOT(jobDone(ThreadWeaver::Job*)));
406 job->setDelayTimer(&delayTimer);
408 Weaver::instance()->enqueue(job);
409 searchJobs.insert(job);
414 static const int slowRunDelay = 400;
417 QueryMatch deferredRun;
418 RunnerContext context;
419 QTimer matchChangeTimer;
421 QHash<QString, AbstractRunner*> runners;
422 QHash<QString, QString> advertiseSingleRunnerIds;
423 AbstractRunner* currentSingleRunner;
424 QSet<FindMatchesJob*> searchJobs;
425 QSet<FindMatchesJob*> oldSearchJobs;
427 QString singleModeRunnerId;
430 bool allRunnersPrepped : 1;
431 bool singleRunnerPrepped : 1;
432 bool teardownRequested : 1;
434 bool singleRunnerWasLoaded : 1;
443 d(new RunnerManagerPrivate(this))
445 d->loadConfiguration();
451 d(new RunnerManagerPrivate(this))
455 d->conf = KConfigGroup(&c,
"PlasmaRunnerManager");
456 d->loadConfiguration();
462 if (!qApp->closingDown() && (!d->searchJobs.isEmpty() || !d->oldSearchJobs.isEmpty())) {
463 new DelayedJobCleaner(d->searchJobs + d->oldSearchJobs);
471 d->loadConfiguration();
477 KConfigGroup config = d->configGroup();
478 config.writeEntry(
"pluginWhiteList", runners);
480 if (!d->runners.isEmpty()) {
488 KConfigGroup config = d->configGroup();
489 return config.readEntry(
"pluginWhiteList", QStringList());
494 KPluginInfo description(service);
495 const QString runnerName = description.pluginName();
496 if (!runnerName.isEmpty() && !d->runners.contains(runnerName)) {
499 d->runners.insert(runnerName, runner);
506 if (!d->runners.contains(path)) {
508 connect(runner, SIGNAL(matchingSuspended(
bool)),
this, SLOT(runnerMatchingSuspended(
bool)));
509 d->runners.insert(path, runner);
515 if (d->runners.isEmpty()) {
519 return d->runners.value(name, 0);
524 return d->currentSingleRunner;
529 d->singleModeRunnerId = id;
530 d->loadSingleRunner();
535 return d->singleModeRunnerId;
540 return d->singleMode;
545 if (d->singleMode == singleMode) {
552 d->loadSingleRunner();
553 d->singleMode = d->currentSingleRunner;
555 if (prevSingleRunner != d->currentSingleRunner) {
568 return d->runners.values();
573 return d->advertiseSingleRunnerIds.keys();
581 return d->advertiseSingleRunnerIds.value(
id, QString());
593 return d->context.matches();
598 run(d->context.match(
id));
610 foreach (FindMatchesJob *job, d->searchJobs) {
611 if (job->runner() == runner && !job->isFinished()) {
612 kDebug() <<
"deferred run";
613 d->deferredRun = match;
618 if (d->deferredRun.isValid()) {
622 d->context.run(match);
632 return QList<QAction*>();
645 if (runner && QMetaObject::invokeMethod(
647 "mimeDataForMatch", Qt::DirectConnection,
648 Q_RETURN_ARG(QMimeData*, mimeData),
664 d->teardownRequested =
false;
672 if (d->currentSingleRunner) {
673 emit d->currentSingleRunner->prepare();
674 d->singleRunnerPrepped =
true;
678 #ifdef MEASURE_PREPTIME
683 #ifdef MEASURE_PREPTIME
684 kDebug() << t.elapsed() << runner->
name();
688 d->allRunnersPrepped =
true;
698 d->teardownRequested =
true;
710 QString term = untrimmedTerm.trimmed();
715 if (term.isEmpty()) {
716 if (d->singleMode && d->currentSingleRunner && d->currentSingleRunner->defaultSyntax()) {
717 term = d->currentSingleRunner->defaultSyntax()->exampleQueries().first().remove(QRegExp(
":q:"));
724 if (d->context.query() == term) {
729 if (d->singleMode && !d->currentSingleRunner) {
734 if (d->runners.isEmpty()) {
740 d->context.setQuery(term);
742 QHash<QString, AbstractRunner*> runable;
745 if (d->singleMode && d->currentSingleRunner) {
746 runable.insert(QString(), d->currentSingleRunner);
747 d->context.setSingleRunnerQueryMode(
true);
749 runable = d->runners;
761 d->delayTimer.start(RunnerManagerPrivate::slowRunDelay);
771 QString term = untrimmedTerm.trimmed();
773 if (term.isEmpty()) {
778 if (d->runners.isEmpty()) {
782 if (d->context.query() == term) {
790 d->context.setQuery(term);
811 return d->context.query();
817 if (Weaver::instance()->isIdle()) {
818 qDeleteAll(d->searchJobs);
819 qDeleteAll(d->oldSearchJobs);
820 d->oldSearchJobs.clear();
822 Q_FOREACH(FindMatchesJob *job, d->searchJobs) {
823 Weaver::instance()->dequeue(job);
825 d->oldSearchJobs += d->searchJobs;
828 d->searchJobs.clear();
830 if (d->deferredRun.isEnabled()) {
834 tmpRun.
run(d->context);
842 #include "runnermanager.moc"