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

libplasma

runnermanager.cpp

Go to the documentation of this file.
00001 /*
00002  *   Copyright (C) 2007 Ryan P. Bitanga <ryan.bitanga@gmail.com> 
00003  *   Copyright (C) 2006 Aaron Seigo <aseigo@kde.org> 
00004  *   Copyright 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 <KServiceTypeTrader>
00025 #include <KPluginInfo>
00026 #include <KDebug>
00027 
00028 #include <Solid/Device>
00029 #include <Solid/DeviceInterface>
00030 
00031 #include <threadweaver/DebuggingAids.h>
00032 #include <ThreadWeaver/Thread>
00033 #include <ThreadWeaver/Job>
00034 #include <ThreadWeaver/QueuePolicy>
00035 #include <ThreadWeaver/Weaver>
00036 #include <QMutex>
00037 #include <QTimer>
00038 #include <QCoreApplication>
00039 
00040 #include "querymatch.h"
00041 
00042 using ThreadWeaver::Weaver;
00043 using ThreadWeaver::Job;
00044 
00045 
00046 namespace Plasma
00047 {
00048 
00049 
00050 /*****************************************************
00051 *  RunnerRestrictionPolicy class
00052 * Restricts simultaneous jobs of the same type
00053 * Similar to ResourceRestrictionPolicy but check the object type first
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 //     QHash<QString, int> m_runCounts;
00079     int m_count;
00080     int m_cap;
00081     QMutex m_mutex;
00082 };
00083 
00084 RunnerRestrictionPolicy::RunnerRestrictionPolicy()
00085     : QueuePolicy(),
00086       m_count(0),
00087       m_cap(2)
00088 {
00089 }
00090 
00091 RunnerRestrictionPolicy::~RunnerRestrictionPolicy()
00092 {
00093 }
00094 
00095 RunnerRestrictionPolicy& RunnerRestrictionPolicy::instance()
00096 {
00097     static RunnerRestrictionPolicy policy;
00098     return policy;
00099 }
00100 
00101 bool RunnerRestrictionPolicy::canRun(Job* job)
00102 {
00103     Q_UNUSED(job)
00104     QMutexLocker l(&m_mutex);
00105 //     QString type = job->objectName();
00106     if (m_count/*m_runCounts.value(type)*/ > m_cap) {
00107 //         kDebug() << "Denying job " << type << " because of " << m_count/*m_runCounts[type]*/ << " current jobs" << endl;
00108         return false;
00109     } else {
00110 //         m_runCounts[type]++;
00111         ++m_count;
00112         return true;
00113     }
00114 }
00115 
00116 void RunnerRestrictionPolicy::free(Job* job)
00117 {
00118     Q_UNUSED(job)
00119     QMutexLocker l(&m_mutex);
00120 //     QString type = job->objectName();
00121     --m_count;
00122 //     if (m_runCounts.value(type)) {
00123 //         m_runCounts[type]--;
00124 //     }
00125 }
00126 
00127 void RunnerRestrictionPolicy::release(Job* job)
00128 {
00129     free(job);
00130 }
00131 
00132 void RunnerRestrictionPolicy::destructed(Job* job)
00133 {
00134     Q_UNUSED(job)
00135 }
00136 
00137 
00138 /*****************************************************
00139 *  FindMatchesJob class
00140 * Class to run queries in different threads
00141 ******************************************************/
00142 class FindMatchesJob : public Job
00143 {
00144 public:
00145     FindMatchesJob(Plasma::AbstractRunner* runner, Plasma::RunnerContext* context, QObject* parent = 0);
00146 
00147     int priority() const;
00148     Plasma::AbstractRunner* runner() const;
00149 
00150 protected:
00151     void run();
00152 private:
00153     Plasma::RunnerContext* m_context;
00154     Plasma::AbstractRunner* m_runner;
00155 };
00156 
00157 FindMatchesJob::FindMatchesJob(Plasma::AbstractRunner* runner,
00158                                Plasma::RunnerContext* context, QObject* parent)
00159     : ThreadWeaver::Job(parent),
00160       m_context(context),
00161       m_runner(runner)
00162 {
00163     if (runner->speed() == Plasma::AbstractRunner::SlowSpeed) {
00164         assignQueuePolicy(&RunnerRestrictionPolicy::instance());
00165     }
00166 }
00167 
00168 void FindMatchesJob::run()
00169 {
00170 //     kDebug() << "Running match for " << m_runner->objectName() << " in Thread " << thread()->id() << endl;
00171     m_runner->performMatch(*m_context);
00172 }
00173 
00174 int FindMatchesJob::priority() const
00175 {
00176     return m_runner->priority();
00177 }
00178 
00179 Plasma::AbstractRunner* FindMatchesJob::runner() const
00180 {
00181     return m_runner;
00182 }
00183 
00184 /*****************************************************
00185 *  RunnerManager::Private class
00186 *
00187 *****************************************************/
00188 class RunnerManagerPrivate
00189 {
00190 public:
00191 
00192     RunnerManagerPrivate(RunnerManager *parent)
00193       : q(parent),
00194         deferredRun(0)
00195     {
00196         matchChangeTimer.setSingleShot(true);
00197         QObject::connect(&matchChangeTimer, SIGNAL(timeout()), q, SLOT(matchesChanged()));
00198         QObject::connect(&context, SIGNAL(matchesChanged()), q, SLOT(scheduleMatchesChanged()));
00199     }
00200 
00201     void scheduleMatchesChanged()
00202     {
00203         matchChangeTimer.start(100);
00204     }
00205 
00206     void matchesChanged()
00207     {
00208         emit q->matchesChanged(context.matches());
00209     }
00210 
00211     void loadConfiguration(KConfigGroup& conf)
00212     {
00213         config = conf;
00214 
00215         //The number of threads used scales with the number of processors.     
00216         const int numProcs = qMax(Solid::Device::listFromType(Solid::DeviceInterface::Processor).count(), 1);
00217         //This entry allows to define a hard upper limit independent of the number of processors.
00218         const int maxThreads = config.readEntry("maxThreads",16);
00219         const int numThreads = qMin(maxThreads, 2 + ((numProcs - 1) * 2));
00220         //kDebug() << "setting up" << numThreads << "threads for" << numProcs << "processors";
00221         Weaver::instance()->setMaximumNumberOfThreads(numThreads);
00222 
00223         //Preferred order of execution of runners
00224         //prioritylist = config.readEntry("priority", QStringList());
00225 
00226         //If set, this list defines which runners won't be used at runtime
00227         //blacklist = config.readEntry("blacklist", QStringList());
00228     }
00229 
00230     void loadRunners()
00231     {
00232         KService::List offers = KServiceTypeTrader::self()->query("Plasma/Runner");
00233 
00234         bool loadAll = config.readEntry("loadAll", false);
00235         KConfigGroup conf(KGlobal::config(), "Plugins");
00236 
00237         foreach (const KService::Ptr &service, offers) {
00238             //kDebug() << "Loading runner: " << service->name() << service->storageId();
00239             KPluginInfo description(service);
00240             QString runnerName = description.pluginName();
00241             description.load(conf);
00242 
00243             bool loaded = runners.contains(runnerName);
00244             bool selected = loadAll || description.isPluginEnabled();
00245 
00246             if (selected) {
00247                 if (!loaded) {
00248                     QString api = service->property("X-Plasma-API").toString();
00249                     QString error;
00250                     AbstractRunner* runner = 0;
00251 
00252                     if (api.isEmpty()) {
00253                         QVariantList args;
00254                         args << service->storageId();
00255                         if (Plasma::isPluginVersionCompatible(KPluginLoader(*service).pluginVersion())) {
00256                             runner = service->createInstance<AbstractRunner>(q, args, &error);
00257                         }
00258                     } else {
00259                         //kDebug() << "got a script runner known as" << api;
00260                         runner = new AbstractRunner(q, service->storageId());
00261                     }
00262 
00263                     if (runner) {
00264                         kDebug() << "loading runner:" << service->name();
00265                         runners.insert(runnerName, runner);
00266                     } else {
00267                         kDebug() << "failed to load runner:" << service->name() << ". error reported:" << error;
00268                     }
00269                 }
00270             } else if (loaded) {
00271                 //Remove runner
00272                 AbstractRunner* runner = runners.take(runnerName);
00273                 kDebug() << "Removing runner: " << runnerName;
00274                 delete runner;
00275             }
00276         }
00277 
00278         //kDebug() << "All runners loaded, total:" << runners.count();
00279     }
00280 
00281     void jobDone(ThreadWeaver::Job* job)
00282     {
00283         FindMatchesJob *runJob = static_cast<FindMatchesJob*>(job);
00284         if (deferredRun.isEnabled() && runJob->runner() == deferredRun.runner()) {
00285             //kDebug() << "job actually done, running now **************";
00286             deferredRun.run(context);
00287             deferredRun = QueryMatch(0);
00288         }
00289         searchJobs.removeAll(runJob);
00290         delete runJob;
00291     }
00292 
00293     RunnerManager *q;
00294     QueryMatch deferredRun;
00295     RunnerContext context;
00296     QTimer matchChangeTimer;
00297     QHash<QString, AbstractRunner*> runners;
00298     QList<FindMatchesJob*> searchJobs;
00299 //     QStringList prioritylist;
00300     bool loadAll;
00301     KConfigGroup config;
00302 };
00303 
00304 
00305 
00306 /*****************************************************
00307 *  RunnerManager::Public class
00308 *
00309 *****************************************************/
00310 RunnerManager::RunnerManager(QObject *parent)
00311     : QObject(parent), 
00312       d(new RunnerManagerPrivate(this))
00313 {
00314     KConfigGroup config(KGlobal::config(), "PlasmaRunnerManager");
00315     d->loadConfiguration(config);
00316     //ThreadWeaver::setDebugLevel(true, 4);
00317 }
00318 
00319 
00320 RunnerManager::RunnerManager(KConfigGroup& config, QObject *parent)
00321     : QObject(parent), 
00322       d(new RunnerManagerPrivate(this))
00323 {
00324     d->loadConfiguration(config);
00325     //ThreadWeaver::setDebugLevel(true, 4);
00326 }
00327 
00328 RunnerManager::~RunnerManager()
00329 {
00330     delete d;
00331 }
00332 
00333 void RunnerManager::reloadConfiguration()
00334 {
00335     d->loadConfiguration(d->config);
00336     d->loadRunners();
00337 }
00338 
00339 AbstractRunner* RunnerManager::runner(const QString &name) const
00340 {
00341     if (d->runners.isEmpty()) {
00342         d->loadRunners();
00343     }
00344 
00345     return d->runners.value(name);
00346 }
00347 
00348 RunnerContext* RunnerManager::searchContext() const
00349 {
00350     return &d->context;
00351 }
00352 
00353 //Reordering is here so data is not reordered till strictly needed
00354 QList<QueryMatch> RunnerManager::matches() const
00355 {
00356     return d->context.matches();
00357 }
00358 
00359 void RunnerManager::run(const QString &id)
00360 {
00361     run(d->context.match(id));
00362 }
00363 
00364 void RunnerManager::run(const QueryMatch &match)
00365 {
00366     if (!match.isEnabled()) {
00367         return;
00368     }
00369 
00370     //TODO: this function is not const as it may be used for learning
00371     AbstractRunner *runner = match.runner();
00372 
00373     foreach (FindMatchesJob *job, d->searchJobs) {
00374         if (job->runner() == runner && !job->isFinished()) {
00375             //kDebug() << "!!!!!!!!!!!!!!!!!!! uh oh!";
00376             d->deferredRun = match;
00377             return;
00378         }
00379     }
00380 
00381     match.run(d->context);
00382 
00383     if (d->deferredRun.isValid()) {
00384         d->deferredRun = QueryMatch(0);
00385     }
00386 }
00387 
00388 void RunnerManager::launchQuery(const QString &term)
00389 {
00390     launchQuery(term, QString());
00391 }
00392 
00393 void RunnerManager::launchQuery(const QString &term, const QString &runnerName)
00394 {
00395     if (d->runners.isEmpty()) {
00396         d->loadRunners();
00397     }
00398 
00399     if (term.isEmpty()) {
00400         reset();
00401         return;
00402     }
00403 
00404     if (d->context.query() == term) {
00405         // we already are searching for this!
00406         return;
00407     }
00408 
00409     reset();
00410 //    kDebug() << "runners searching for" << term << "on" << runnerName;
00411     d->context.setQuery(term);
00412 
00413     AbstractRunner::List runable;
00414 
00415     //if the name is not empty we will launch only the specified runner
00416     if (!runnerName.isEmpty()) {
00417         runable.append(runner(runnerName));
00418     } else {
00419         runable = d->runners.values(); 
00420     }
00421 
00422     foreach (Plasma::AbstractRunner* r, runable) {
00423         if ((r->ignoredTypes() & d->context.type()) == 0) {
00424 //            kDebug() << "launching" << r->name();
00425             FindMatchesJob *job = new FindMatchesJob(r, &d->context, this);
00426             connect(job, SIGNAL(done(ThreadWeaver::Job*)), this, SLOT(jobDone(ThreadWeaver::Job*)));
00427             Weaver::instance()->enqueue(job);
00428             d->searchJobs.append(job);
00429         }
00430     }
00431 }
00432 
00433 bool RunnerManager::execQuery(const QString &term)
00434 {
00435     return execQuery(term, QString());
00436 }
00437 
00438 bool RunnerManager::execQuery(const QString &term, const QString &runnerName)
00439 {
00440     if (d->runners.isEmpty()) {
00441         d->loadRunners();
00442     }
00443 
00444     if (term.isEmpty()) {
00445         reset();
00446         return false;
00447     }
00448 
00449     if (d->context.query() == term) {
00450         // we already are searching for this!
00451         emit matchesChanged(d->context.matches());
00452         return false;
00453     }
00454 
00455     reset();
00456     //kDebug() << "executing query about " << term << "on" << runnerName;
00457     d->context.setQuery(term);
00458     AbstractRunner *r = runner(runnerName);
00459 
00460     if (!r) {
00461         //kDebug() << "failed to find the runner";
00462         return false;
00463     }
00464 
00465     if ((r->ignoredTypes() & d->context.type()) != 0) {
00466         //kDebug() << "ignored!";
00467         return false;
00468     }
00469 
00470     r->performMatch(d->context);
00471     //kDebug() << "succeeded with" << d->context.matches().count() << "results";
00472     emit matchesChanged(d->context.matches());
00473     return true;
00474 }
00475 
00476 QString RunnerManager::query() const
00477 {
00478     return d->context.query();
00479 }
00480 
00481 void RunnerManager::reset()
00482 {
00483     // If ThreadWeaver is idle, it is safe to clear previous jobs 
00484     if (Weaver::instance()->isIdle()) {
00485         qDeleteAll(d->searchJobs);
00486         d->searchJobs.clear();
00487     } else {
00488         Weaver::instance()->dequeue();
00489     }
00490 
00491     if (d->deferredRun.isEnabled()) {
00492         //kDebug() << "job actually done, running now **************";
00493         d->deferredRun.run(d->context);
00494         d->deferredRun = QueryMatch(0);
00495     }
00496 
00497     d->context.reset();
00498 }
00499 
00500 } // Plasma namespace
00501 
00502 #include "runnermanager.moc"

libplasma

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

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libplasma
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.4
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