• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Plasma

runnermanager.cpp

Go to the documentation of this file.
00001 /*
00002  *   Copyright (C) 2006 Aaron Seigo <aseigo@kde.org>
00003  *   Copyright (C) 2007, 2009 Ryan P. Bitanga <ryan.bitanga@gmail.com>
00004  *   Copyright (C) 2008 Jordi Polo <mumismo@gmail.com>
00005  *
00006  *   This program is free software; you can redistribute it and/or modify
00007  *   it under the terms of the GNU Library General Public License as
00008  *   published by the Free Software Foundation; either version 2, or
00009  *   (at your option) any later version.
00010  *
00011  *   This program is distributed in the hope that it will be useful,
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *   GNU General Public License for more details
00015  *
00016  *   You should have received a copy of the GNU Library General Public
00017  *   License along with this program; if not, write to the
00018  *   Free Software Foundation, Inc.,
00019  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
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 *  RunnerManager::Private class
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         //The number of threads used scales with the number of processors.
00089         const int numProcs =
00090             qMax(Solid::Device::listFromType(Solid::DeviceInterface::Processor).count(), 1);
00091         //This entry allows to define a hard upper limit independent of the number of processors.
00092         const int maxThreads = config.readEntry("maxThreads", 16);
00093         const int numThreads = qMin(maxThreads, 2 + ((numProcs - 1) * 2));
00094         //kDebug() << "setting up" << numThreads << "threads for" << numProcs << "processors";
00095         if (numThreads > Weaver::instance()->maximumNumberOfThreads()) {
00096             Weaver::instance()->setMaximumNumberOfThreads(numThreads);
00097         }
00098         // Limit the number of instances of a single normal speed runner and all of the slow runners
00099         // to half the number of threads
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         //The plugin configuration is stored under the section Plugins
00112         //and not PlasmaRunnerManager->Plugins
00113         KConfigGroup conf(KGlobal::config(), "Plugins");
00114 
00115         foreach (const KService::Ptr &service, offers) {
00116             //kDebug() << "Loading runner: " << service->name() << service->storageId();
00117             QString tryExec = service->property("TryExec", QVariant::String).toString();
00118             //kDebug() << "TryExec is" << tryExec;
00119             if (!tryExec.isEmpty() && KStandardDirs::findExe(tryExec).isEmpty()) {
00120                 // we don't actually have this application!
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                         //kDebug() << "got a script runner known as" << api;
00145                         runner = new AbstractRunner(q, service->storageId());
00146                     }
00147 
00148                     if (runner) {
00149                         kDebug() << "================= loading runner:" << service->name() << "=================";
00150 
00151                         /*
00152                         foreach (const RunnerSyntax &syntax, runner->syntaxes()) {
00153                             kDebug() << syntax.exampleQueriesWithTermDescription().join(", ") << " ==> " << syntax.description();
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                 //Remove runner
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             //kDebug() << "job actually done, running now **************";
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             // we finished our run, and there are no valid matches, and so no
00196             // signal will have been sent out. so we need to emit the signal
00197             // ourselves here
00198             emit q->matchesChanged(context.matches());
00199         }
00200     }
00201 
00202     void unblockJobs()
00203     {
00204         // WORKAROUND: Queue an empty job to force ThreadWeaver to awaken threads
00205         // kDebug() << "- Unblocking jobs -" << endl;
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     // Delay in ms before slow runners are allowed to run
00212     static const int slowRunDelay = 400;
00213 
00214     RunnerManager *q;
00215     QueryMatch deferredRun;
00216     RunnerContext context;
00217     QTimer matchChangeTimer;
00218     QTimer delayTimer; // Timer to control when to run slow runners
00219     QHash<QString, AbstractRunner*> runners;
00220     QSet<FindMatchesJob*> searchJobs;
00221     QSet<FindMatchesJob*> oldSearchJobs;
00222     bool loadAll;
00223     KConfigGroup config;
00224 };
00225 
00226 /*****************************************************
00227 *  RunnerManager::Public class
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     //ThreadWeaver::setDebugLevel(true, 4);
00237 }
00238 
00239 RunnerManager::RunnerManager(KConfigGroup &c, QObject *parent)
00240     : QObject(parent),
00241       d(new RunnerManagerPrivate(this))
00242 {
00243     // Should this be really needed? Maybe d->loadConfiguration(c) would make
00244     // more sense.
00245     KConfigGroup config(&c, "PlasmaRunnerManager");
00246     d->loadConfiguration(config);
00247     //ThreadWeaver::setDebugLevel(true, 4);
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 //Reordering is here so data is not reordered till strictly needed
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     //TODO: this function is not const as it may be used for learning
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         // we already are searching for this!
00349         return;
00350     }
00351 
00352     reset();
00353 //    kDebug() << "runners searching for" << term << "on" << runnerName;
00354     d->context.setQuery(term);
00355 
00356     AbstractRunner::List runable;
00357 
00358     //if the name is not empty we will launch only the specified runner
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 //            kDebug() << "launching" << r->name();
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     // Start timer to unblock slow runners
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         // we already are searching for this!
00404         emit matchesChanged(d->context.matches());
00405         return false;
00406     }
00407 
00408     reset();
00409     //kDebug() << "executing query about " << term << "on" << runnerName;
00410     d->context.setQuery(term);
00411     AbstractRunner *r = runner(runnerName);
00412 
00413     if (!r) {
00414         //kDebug() << "failed to find the runner";
00415         return false;
00416     }
00417 
00418     if ((r->ignoredTypes() & d->context.type()) != 0) {
00419         //kDebug() << "ignored!";
00420         return false;
00421     }
00422 
00423     r->performMatch(d->context);
00424     //kDebug() << "succeeded with" << d->context.matches().count() << "results";
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     // If ThreadWeaver is idle, it is safe to clear previous jobs
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         //kDebug() << "job actually done, running now **************";
00450         QueryMatch tmpRun = d->deferredRun;
00451         d->deferredRun = QueryMatch(0);
00452         tmpRun.run(d->context);
00453     }
00454 
00455     d->context.reset();
00456 }
00457 
00458 } // Plasma namespace
00459 
00460 #include "runnermanager.moc"

Plasma

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal