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/Job.h>
00039 #include <Weaver/QueuePolicy.h>
00040 #include <Weaver/ThreadWeaver.h>
00041
00042 #include "querymatch.h"
00043
00044 using ThreadWeaver::Weaver;
00045 using ThreadWeaver::Job;
00046
00047 namespace Plasma
00048 {
00049
00050
00051
00052
00053
00054
00055 class RunnerRestrictionPolicy : public ThreadWeaver::QueuePolicy
00056 {
00057 public:
00058 ~RunnerRestrictionPolicy();
00059
00060 static RunnerRestrictionPolicy &instance();
00061
00062 void setCap(int cap)
00063 {
00064 m_cap = cap;
00065 }
00066 int cap() const
00067 {
00068 return m_cap;
00069 }
00070
00071 bool canRun(Job *job);
00072 void free(Job *job);
00073 void release(Job *job);
00074 void destructed(Job *job);
00075 private:
00076 RunnerRestrictionPolicy();
00077
00078 int m_count;
00079 int m_cap;
00080 QMutex m_mutex;
00081 };
00082
00083 RunnerRestrictionPolicy::RunnerRestrictionPolicy()
00084 : QueuePolicy(),
00085 m_count(0),
00086 m_cap(2)
00087 {
00088 }
00089
00090 RunnerRestrictionPolicy::~RunnerRestrictionPolicy()
00091 {
00092 }
00093
00094 RunnerRestrictionPolicy &RunnerRestrictionPolicy::instance()
00095 {
00096 static RunnerRestrictionPolicy policy;
00097 return policy;
00098 }
00099
00100 bool RunnerRestrictionPolicy::canRun(Job *job)
00101 {
00102 Q_UNUSED(job)
00103 QMutexLocker l(&m_mutex);
00104 if (m_count > m_cap) {
00105 return false;
00106 } else {
00107 ++m_count;
00108 return true;
00109 }
00110 }
00111
00112 void RunnerRestrictionPolicy::free(Job *job)
00113 {
00114 Q_UNUSED(job)
00115 QMutexLocker l(&m_mutex);
00116 --m_count;
00117 }
00118
00119 void RunnerRestrictionPolicy::release(Job *job)
00120 {
00121 free(job);
00122 }
00123
00124 void RunnerRestrictionPolicy::destructed(Job *job)
00125 {
00126 Q_UNUSED(job)
00127 }
00128
00129
00130
00131
00132
00133 class FindMatchesJob : public Job
00134 {
00135 public:
00136 FindMatchesJob(Plasma::AbstractRunner *runner,
00137 Plasma::RunnerContext *context, QObject *parent = 0);
00138
00139 int priority() const;
00140 Plasma::AbstractRunner *runner() const;
00141
00142 protected:
00143 void run();
00144 private:
00145 Plasma::RunnerContext *m_context;
00146 Plasma::AbstractRunner *m_runner;
00147 };
00148
00149 FindMatchesJob::FindMatchesJob(Plasma::AbstractRunner *runner,
00150 Plasma::RunnerContext *context, QObject *parent)
00151 : ThreadWeaver::Job(parent),
00152 m_context(context),
00153 m_runner(runner)
00154 {
00155 if (runner->speed() == Plasma::AbstractRunner::SlowSpeed) {
00156 assignQueuePolicy(&RunnerRestrictionPolicy::instance());
00157 }
00158 }
00159
00160 void FindMatchesJob::run()
00161 {
00162
00163
00164 m_runner->performMatch(*m_context);
00165 }
00166
00167 int FindMatchesJob::priority() const
00168 {
00169 return m_runner->priority();
00170 }
00171
00172 Plasma::AbstractRunner *FindMatchesJob::runner() const
00173 {
00174 return m_runner;
00175 }
00176
00177
00178
00179
00180
00181 class RunnerManagerPrivate
00182 {
00183 public:
00184
00185 RunnerManagerPrivate(RunnerManager *parent)
00186 : q(parent),
00187 deferredRun(0)
00188 {
00189 matchChangeTimer.setSingleShot(true);
00190 QObject::connect(&matchChangeTimer, SIGNAL(timeout()), q, SLOT(matchesChanged()));
00191 QObject::connect(&context, SIGNAL(matchesChanged()), q, SLOT(scheduleMatchesChanged()));
00192 }
00193
00194 void scheduleMatchesChanged()
00195 {
00196 matchChangeTimer.start(0);
00197 }
00198
00199 void matchesChanged()
00200 {
00201 emit q->matchesChanged(context.matches());
00202 }
00203
00204 void loadConfiguration(KConfigGroup &conf)
00205 {
00206 config = conf;
00207
00208
00209 const int numProcs =
00210 qMax(Solid::Device::listFromType(Solid::DeviceInterface::Processor).count(), 1);
00211
00212 const int maxThreads = config.readEntry("maxThreads", 16);
00213 const int numThreads = qMin(maxThreads, 2 + ((numProcs - 1) * 2));
00214
00215 Weaver::instance()->setMaximumNumberOfThreads(numThreads);
00216
00217
00218
00219
00220
00221
00222 }
00223
00224 void loadRunners()
00225 {
00226 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Runner");
00227
00228 bool loadAll = config.readEntry("loadAll", false);
00229
00230
00231 KConfigGroup conf(KGlobal::config(), "Plugins");
00232
00233 foreach (const KService::Ptr &service, offers) {
00234
00235 QString tryExec = service->property("TryExec", QVariant::String).toString();
00236 kDebug() << "tryExec is" << tryExec;
00237 if (!tryExec.isEmpty() && KStandardDirs::findExe(tryExec).isEmpty()) {
00238
00239 continue;
00240 }
00241
00242 KPluginInfo description(service);
00243 QString runnerName = description.pluginName();
00244 description.load(conf);
00245
00246 bool loaded = runners.contains(runnerName);
00247 bool selected = loadAll || description.isPluginEnabled();
00248
00249 if (selected) {
00250 if (!loaded) {
00251 QString api = service->property("X-Plasma-API").toString();
00252 QString error;
00253 AbstractRunner *runner = 0;
00254
00255 if (api.isEmpty()) {
00256 QVariantList args;
00257 args << service->storageId();
00258 if (Plasma::isPluginVersionCompatible(KPluginLoader(*service).pluginVersion())) {
00259 runner = service->createInstance<AbstractRunner>(q, args, &error);
00260 }
00261 } else {
00262
00263 runner = new AbstractRunner(q, service->storageId());
00264 }
00265
00266 if (runner) {
00267 kDebug() << "loading runner:" << service->name();
00268 runners.insert(runnerName, runner);
00269 } else {
00270 kDebug() << "failed to load runner:" << service->name()
00271 << ". error reported:" << error;
00272 }
00273 }
00274 } else if (loaded) {
00275
00276 AbstractRunner *runner = runners.take(runnerName);
00277 kDebug() << "Removing runner: " << runnerName;
00278 delete runner;
00279 }
00280 }
00281
00282 kDebug() << "All runners loaded, total:" << runners.count();
00283 }
00284
00285 void jobDone(ThreadWeaver::Job *job)
00286 {
00287 FindMatchesJob *runJob = static_cast<FindMatchesJob*>(job);
00288 if (deferredRun.isEnabled() && runJob->runner() == deferredRun.runner()) {
00289
00290 deferredRun.run(context);
00291 deferredRun = QueryMatch(0);
00292 }
00293 searchJobs.removeAll(runJob);
00294 delete runJob;
00295 }
00296
00297 RunnerManager *q;
00298 QueryMatch deferredRun;
00299 RunnerContext context;
00300 QTimer matchChangeTimer;
00301 QHash<QString, AbstractRunner*> runners;
00302 QList<FindMatchesJob*> searchJobs;
00303
00304 bool loadAll;
00305 KConfigGroup config;
00306 };
00307
00308
00309
00310
00311
00312 RunnerManager::RunnerManager(QObject *parent)
00313 : QObject(parent),
00314 d(new RunnerManagerPrivate(this))
00315 {
00316 KConfigGroup config(KGlobal::config(), "PlasmaRunnerManager");
00317 d->loadConfiguration(config);
00318
00319 }
00320
00321 RunnerManager::RunnerManager(KConfigGroup &c, QObject *parent)
00322 : QObject(parent),
00323 d(new RunnerManagerPrivate(this))
00324 {
00325
00326
00327 KConfigGroup config(&c, "PlasmaRunnerManager");
00328 d->loadConfiguration(config);
00329
00330 }
00331
00332 RunnerManager::~RunnerManager()
00333 {
00334 delete d;
00335 }
00336
00337 void RunnerManager::reloadConfiguration()
00338 {
00339 d->loadConfiguration(d->config);
00340 d->loadRunners();
00341 }
00342
00343 AbstractRunner *RunnerManager::runner(const QString &name) const
00344 {
00345 if (d->runners.isEmpty()) {
00346 d->loadRunners();
00347 }
00348
00349 return d->runners.value(name, 0);
00350 }
00351
00352 RunnerContext *RunnerManager::searchContext() const
00353 {
00354 return &d->context;
00355 }
00356
00357
00358 QList<QueryMatch> RunnerManager::matches() const
00359 {
00360 return d->context.matches();
00361 }
00362
00363 void RunnerManager::run(const QString &id)
00364 {
00365 run(d->context.match(id));
00366 }
00367
00368 void RunnerManager::run(const QueryMatch &match)
00369 {
00370 if (!match.isEnabled()) {
00371 return;
00372 }
00373
00374
00375 AbstractRunner *runner = match.runner();
00376
00377 foreach (FindMatchesJob *job, d->searchJobs) {
00378 if (job->runner() == runner && !job->isFinished()) {
00379
00380 d->deferredRun = match;
00381 return;
00382 }
00383 }
00384
00385 match.run(d->context);
00386
00387 if (d->deferredRun.isValid()) {
00388 d->deferredRun = QueryMatch(0);
00389 }
00390 }
00391
00392 QList<QAction*> RunnerManager::actionsForMatch(const QueryMatch &match)
00393 {
00394 AbstractRunner *runner = match.runner();
00395 if (runner) {
00396 return runner->actionsForMatch(match);
00397 }
00398
00399 return QList<QAction*>();
00400 }
00401
00402 void RunnerManager::launchQuery(const QString &term)
00403 {
00404 launchQuery(term, QString());
00405 }
00406
00407 void RunnerManager::launchQuery(const QString &term, const QString &runnerName)
00408 {
00409 if (d->runners.isEmpty()) {
00410 d->loadRunners();
00411 }
00412
00413 if (term.isEmpty()) {
00414 reset();
00415 return;
00416 }
00417
00418 if (d->context.query() == term) {
00419
00420 return;
00421 }
00422
00423 reset();
00424
00425 d->context.setQuery(term);
00426
00427 AbstractRunner::List runable;
00428
00429
00430 if (!runnerName.isEmpty()) {
00431 AbstractRunner *r = runner(runnerName);
00432 if (r) {
00433 runable.append(r);
00434 }
00435 } else {
00436 runable = d->runners.values();
00437 }
00438
00439 foreach (Plasma::AbstractRunner *r, runable) {
00440 if ((r->ignoredTypes() & d->context.type()) == 0) {
00441
00442 FindMatchesJob *job = new FindMatchesJob(r, &d->context, this);
00443 connect(job, SIGNAL(done(ThreadWeaver::Job*)), this, SLOT(jobDone(ThreadWeaver::Job*)));
00444 Weaver::instance()->enqueue(job);
00445 d->searchJobs.append(job);
00446 }
00447 }
00448 }
00449
00450 bool RunnerManager::execQuery(const QString &term)
00451 {
00452 return execQuery(term, QString());
00453 }
00454
00455 bool RunnerManager::execQuery(const QString &term, const QString &runnerName)
00456 {
00457 if (d->runners.isEmpty()) {
00458 d->loadRunners();
00459 }
00460
00461 if (term.isEmpty()) {
00462 reset();
00463 return false;
00464 }
00465
00466 if (d->context.query() == term) {
00467
00468 emit matchesChanged(d->context.matches());
00469 return false;
00470 }
00471
00472 reset();
00473
00474 d->context.setQuery(term);
00475 AbstractRunner *r = runner(runnerName);
00476
00477 if (!r) {
00478
00479 return false;
00480 }
00481
00482 if ((r->ignoredTypes() & d->context.type()) != 0) {
00483
00484 return false;
00485 }
00486
00487 r->performMatch(d->context);
00488
00489 emit matchesChanged(d->context.matches());
00490 return true;
00491 }
00492
00493 QString RunnerManager::query() const
00494 {
00495 return d->context.query();
00496 }
00497
00498 void RunnerManager::reset()
00499 {
00500
00501 if (Weaver::instance()->isIdle()) {
00502 qDeleteAll(d->searchJobs);
00503 d->searchJobs.clear();
00504 } else {
00505 Weaver::instance()->dequeue();
00506 }
00507
00508 if (d->deferredRun.isEnabled()) {
00509
00510 d->deferredRun.run(d->context);
00511 d->deferredRun = QueryMatch(0);
00512 }
00513
00514 d->context.reset();
00515 }
00516
00517 }
00518
00519 #include "runnermanager.moc"