00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "runnermanager.h"
00023
00024 #include <QMutex>
00025 #include <QTimer>
00026 #include <QCoreApplication>
00027
00028 #include <kdebug.h>
00029 #include <kplugininfo.h>
00030 #include <kservicetypetrader.h>
00031 #include <kstandarddirs.h>
00032
00033 #include <solid/device.h>
00034 #include <solid/deviceinterface.h>
00035
00036 #include <Weaver/DebuggingAids.h>
00037 #include <Weaver/Thread.h>
00038 #include <Weaver/ThreadWeaver.h>
00039
00040 #include "private/runnerjobs.h"
00041 #include "querymatch.h"
00042
00043 using ThreadWeaver::Weaver;
00044 using ThreadWeaver::Job;
00045
00046 namespace Plasma
00047 {
00048
00049
00050
00051
00052
00053 class RunnerManagerPrivate
00054 {
00055 public:
00056
00057 RunnerManagerPrivate(RunnerManager *parent)
00058 : q(parent),
00059 deferredRun(0)
00060 {
00061 matchChangeTimer.setSingleShot(true);
00062 delayTimer.setSingleShot(true);
00063
00064 QObject::connect(&matchChangeTimer, SIGNAL(timeout()), q, SLOT(matchesChanged()));
00065 QObject::connect(&context, SIGNAL(matchesChanged()), q, SLOT(scheduleMatchesChanged()));
00066 QObject::connect(&delayTimer, SIGNAL(timeout()), q, SLOT(unblockJobs()));
00067 }
00068
00069 ~RunnerManagerPrivate()
00070 {
00071 context.save(config);
00072 }
00073
00074 void scheduleMatchesChanged()
00075 {
00076 matchChangeTimer.start(50);
00077 }
00078
00079 void matchesChanged()
00080 {
00081 emit q->matchesChanged(context.matches());
00082 }
00083
00084 void loadConfiguration(KConfigGroup &conf)
00085 {
00086 config = conf;
00087
00088
00089 const int numProcs =
00090 qMax(Solid::Device::listFromType(Solid::DeviceInterface::Processor).count(), 1);
00091
00092 const int maxThreads = config.readEntry("maxThreads", 16);
00093 const int numThreads = qMin(maxThreads, 2 + ((numProcs - 1) * 2));
00094
00095 if (numThreads > Weaver::instance()->maximumNumberOfThreads()) {
00096 Weaver::instance()->setMaximumNumberOfThreads(numThreads);
00097 }
00098
00099
00100 const int cap = qMax(2, numThreads/2);
00101 DefaultRunnerPolicy::instance().setCap(cap);
00102
00103 context.restore(config);
00104 }
00105
00106 void loadRunners()
00107 {
00108 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Runner");
00109
00110 bool loadAll = config.readEntry("loadAll", false);
00111
00112
00113 KConfigGroup conf(KGlobal::config(), "Plugins");
00114
00115 foreach (const KService::Ptr &service, offers) {
00116
00117 QString tryExec = service->property("TryExec", QVariant::String).toString();
00118
00119 if (!tryExec.isEmpty() && KStandardDirs::findExe(tryExec).isEmpty()) {
00120
00121 continue;
00122 }
00123
00124 KPluginInfo description(service);
00125 QString runnerName = description.pluginName();
00126 description.load(conf);
00127
00128 bool loaded = runners.contains(runnerName);
00129 bool selected = loadAll || description.isPluginEnabled();
00130
00131 if (selected) {
00132 if (!loaded) {
00133 QString api = service->property("X-Plasma-API").toString();
00134 QString error;
00135 AbstractRunner *runner = 0;
00136
00137 if (api.isEmpty()) {
00138 QVariantList args;
00139 args << service->storageId();
00140 if (Plasma::isPluginVersionCompatible(KPluginLoader(*service).pluginVersion())) {
00141 runner = service->createInstance<AbstractRunner>(q, args, &error);
00142 }
00143 } else {
00144
00145 runner = new AbstractRunner(q, service->storageId());
00146 }
00147
00148 if (runner) {
00149 kDebug() << "================= loading runner:" << service->name() << "=================";
00150
00151
00152
00153
00154
00155
00156
00157 QMetaObject::invokeMethod(runner, "init");
00158 runners.insert(runnerName, runner);
00159 } else {
00160 kDebug() << "failed to load runner:" << service->name()
00161 << ". error reported:" << error;
00162 }
00163 }
00164 } else if (loaded) {
00165
00166 AbstractRunner *runner = runners.take(runnerName);
00167 kDebug() << "Removing runner: " << runnerName;
00168 delete runner;
00169 }
00170 }
00171
00172 kDebug() << "All runners loaded, total:" << runners.count();
00173 }
00174
00175 void jobDone(ThreadWeaver::Job *job)
00176 {
00177 FindMatchesJob *runJob = dynamic_cast<FindMatchesJob *>(job);
00178
00179 if (!runJob) {
00180 return;
00181 }
00182
00183 if (deferredRun.isEnabled() && runJob->runner() == deferredRun.runner()) {
00184
00185 QueryMatch tmpRun = deferredRun;
00186 deferredRun = QueryMatch(0);
00187 tmpRun.run(context);
00188 }
00189
00190 searchJobs.remove(runJob);
00191 oldSearchJobs.remove(runJob);
00192 delete runJob;
00193
00194 if (searchJobs.isEmpty() && context.matches().isEmpty()) {
00195
00196
00197
00198 emit q->matchesChanged(context.matches());
00199 }
00200 }
00201
00202 void unblockJobs()
00203 {
00204
00205
00206 DummyJob *dummy = new DummyJob(q);
00207 Weaver::instance()->enqueue(dummy);
00208 QObject::connect(dummy, SIGNAL(done(ThreadWeaver::Job*)), dummy, SLOT(deleteLater()));
00209 }
00210
00211
00212 static const int slowRunDelay = 400;
00213
00214 RunnerManager *q;
00215 QueryMatch deferredRun;
00216 RunnerContext context;
00217 QTimer matchChangeTimer;
00218 QTimer delayTimer;
00219 QHash<QString, AbstractRunner*> runners;
00220 QSet<FindMatchesJob*> searchJobs;
00221 QSet<FindMatchesJob*> oldSearchJobs;
00222 bool loadAll;
00223 KConfigGroup config;
00224 };
00225
00226
00227
00228
00229
00230 RunnerManager::RunnerManager(QObject *parent)
00231 : QObject(parent),
00232 d(new RunnerManagerPrivate(this))
00233 {
00234 KConfigGroup config(KGlobal::config(), "PlasmaRunnerManager");
00235 d->loadConfiguration(config);
00236
00237 }
00238
00239 RunnerManager::RunnerManager(KConfigGroup &c, QObject *parent)
00240 : QObject(parent),
00241 d(new RunnerManagerPrivate(this))
00242 {
00243
00244
00245 KConfigGroup config(&c, "PlasmaRunnerManager");
00246 d->loadConfiguration(config);
00247
00248 }
00249
00250 RunnerManager::~RunnerManager()
00251 {
00252 if (!qApp->closingDown() && (!d->searchJobs.isEmpty() || !d->oldSearchJobs.isEmpty())) {
00253 new DelayedJobCleaner(d->searchJobs + d->oldSearchJobs, Weaver::instance());
00254 }
00255
00256 delete d;
00257 }
00258
00259 void RunnerManager::reloadConfiguration()
00260 {
00261 d->loadConfiguration(d->config);
00262 d->loadRunners();
00263 }
00264
00265 AbstractRunner* RunnerManager::runner(const QString &name) const
00266 {
00267 if (d->runners.isEmpty()) {
00268 d->loadRunners();
00269 }
00270
00271 return d->runners.value(name, 0);
00272 }
00273
00274 QList<AbstractRunner *> RunnerManager::runners() const
00275 {
00276 return d->runners.values();
00277 }
00278
00279 RunnerContext* RunnerManager::searchContext() const
00280 {
00281 return &d->context;
00282 }
00283
00284
00285 QList<QueryMatch> RunnerManager::matches() const
00286 {
00287 return d->context.matches();
00288 }
00289
00290 void RunnerManager::run(const QString &id)
00291 {
00292 run(d->context.match(id));
00293 }
00294
00295 void RunnerManager::run(const QueryMatch &match)
00296 {
00297 if (!match.isEnabled()) {
00298 return;
00299 }
00300
00301
00302 AbstractRunner *runner = match.runner();
00303
00304 foreach (FindMatchesJob *job, d->searchJobs) {
00305 if (job->runner() == runner && !job->isFinished()) {
00306 kDebug() << "deferred run";
00307 d->deferredRun = match;
00308 return;
00309 }
00310 }
00311
00312 if (d->deferredRun.isValid()) {
00313 d->deferredRun = QueryMatch(0);
00314 }
00315
00316 d->context.run(match);
00317 }
00318
00319 QList<QAction*> RunnerManager::actionsForMatch(const QueryMatch &match)
00320 {
00321 AbstractRunner *runner = match.runner();
00322 if (runner) {
00323 return runner->actionsForMatch(match);
00324 }
00325
00326 return QList<QAction*>();
00327 }
00328
00329 void RunnerManager::launchQuery(const QString &term)
00330 {
00331 launchQuery(term, QString());
00332 }
00333
00334 void RunnerManager::launchQuery(const QString &untrimmedTerm, const QString &runnerName)
00335 {
00336 QString term = untrimmedTerm.trimmed();
00337
00338 if (d->runners.isEmpty()) {
00339 d->loadRunners();
00340 }
00341
00342 if (term.isEmpty()) {
00343 reset();
00344 return;
00345 }
00346
00347 if (d->context.query() == term) {
00348
00349 return;
00350 }
00351
00352 reset();
00353
00354 d->context.setQuery(term);
00355
00356 AbstractRunner::List runable;
00357
00358
00359 if (!runnerName.isEmpty()) {
00360 AbstractRunner *r = runner(runnerName);
00361 if (r) {
00362 runable.append(r);
00363 }
00364 } else {
00365 runable = d->runners.values();
00366 }
00367
00368 foreach (Plasma::AbstractRunner *r, runable) {
00369 if ((r->ignoredTypes() & d->context.type()) == 0) {
00370
00371 FindMatchesJob *job = new FindMatchesJob(r, &d->context, Weaver::instance());
00372 connect(job, SIGNAL(done(ThreadWeaver::Job*)), this, SLOT(jobDone(ThreadWeaver::Job*)));
00373 if (r->speed() == AbstractRunner::SlowSpeed) {
00374 job->setDelayTimer(&d->delayTimer);
00375 }
00376 Weaver::instance()->enqueue(job);
00377 d->searchJobs.insert(job);
00378 }
00379 }
00380
00381 d->delayTimer.start(RunnerManagerPrivate::slowRunDelay);
00382 }
00383
00384 bool RunnerManager::execQuery(const QString &term)
00385 {
00386 return execQuery(term, QString());
00387 }
00388
00389 bool RunnerManager::execQuery(const QString &untrimmedTerm, const QString &runnerName)
00390 {
00391 QString term = untrimmedTerm.trimmed();
00392
00393 if (d->runners.isEmpty()) {
00394 d->loadRunners();
00395 }
00396
00397 if (term.isEmpty()) {
00398 reset();
00399 return false;
00400 }
00401
00402 if (d->context.query() == term) {
00403
00404 emit matchesChanged(d->context.matches());
00405 return false;
00406 }
00407
00408 reset();
00409
00410 d->context.setQuery(term);
00411 AbstractRunner *r = runner(runnerName);
00412
00413 if (!r) {
00414
00415 return false;
00416 }
00417
00418 if ((r->ignoredTypes() & d->context.type()) != 0) {
00419
00420 return false;
00421 }
00422
00423 r->performMatch(d->context);
00424
00425 emit matchesChanged(d->context.matches());
00426 return true;
00427 }
00428
00429 QString RunnerManager::query() const
00430 {
00431 return d->context.query();
00432 }
00433
00434 void RunnerManager::reset()
00435 {
00436
00437 if (Weaver::instance()->isIdle()) {
00438 qDeleteAll(d->searchJobs);
00439 qDeleteAll(d->oldSearchJobs);
00440 d->oldSearchJobs.clear();
00441 } else {
00442 Weaver::instance()->dequeue();
00443 d->oldSearchJobs += d->searchJobs;
00444 }
00445
00446 d->searchJobs.clear();
00447
00448 if (d->deferredRun.isEnabled()) {
00449
00450 QueryMatch tmpRun = d->deferredRun;
00451 d->deferredRun = QueryMatch(0);
00452 tmpRun.run(d->context);
00453 }
00454
00455 d->context.reset();
00456 }
00457
00458 }
00459
00460 #include "runnermanager.moc"