• Skip to content
  • Skip to link menu
KDE 4.2 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 Ryan P. Bitanga <ryan.bitanga@gmail.com>
00004  *   Copyright (2) 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/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 *  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     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 *  FindMatchesJob class
00131 * Class to run queries in different threads
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 //     kDebug() << "Running match for " << m_runner->objectName()
00163 //              << " in Thread " << thread()->id() << endl;
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 *  RunnerManager::Private class
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         //The number of threads used scales with the number of processors.
00209         const int numProcs =
00210             qMax(Solid::Device::listFromType(Solid::DeviceInterface::Processor).count(), 1);
00211         //This entry allows to define a hard upper limit independent of the number of processors.
00212         const int maxThreads = config.readEntry("maxThreads", 16);
00213         const int numThreads = qMin(maxThreads, 2 + ((numProcs - 1) * 2));
00214         //kDebug() << "setting up" << numThreads << "threads for" << numProcs << "processors";
00215         Weaver::instance()->setMaximumNumberOfThreads(numThreads);
00216 
00217         //Preferred order of execution of runners
00218         //prioritylist = config.readEntry("priority", QStringList());
00219 
00220         //If set, this list defines which runners won't be used at runtime
00221         //blacklist = config.readEntry("blacklist", QStringList());
00222     }
00223 
00224     void loadRunners()
00225     {
00226         KService::List offers = KServiceTypeTrader::self()->query("Plasma/Runner");
00227 
00228         bool loadAll = config.readEntry("loadAll", false);
00229         //The plugin configuration is stored under the section Plugins
00230         //and not PlasmaRunnerManager->Plugins
00231         KConfigGroup conf(KGlobal::config(), "Plugins");
00232 
00233         foreach (const KService::Ptr &service, offers) {
00234             //kDebug() << "Loading runner: " << service->name() << service->storageId();
00235             QString tryExec = service->property("TryExec", QVariant::String).toString();
00236             kDebug() << "tryExec is" << tryExec;
00237             if (!tryExec.isEmpty() && KStandardDirs::findExe(tryExec).isEmpty()) {
00238                 // we don't actually have this application!
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                         //kDebug() << "got a script runner known as" << api;
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                 //Remove runner
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             //kDebug() << "job actually done, running now **************";
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 //     QStringList prioritylist;
00304     bool loadAll;
00305     KConfigGroup config;
00306 };
00307 
00308 /*****************************************************
00309 *  RunnerManager::Public class
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     //ThreadWeaver::setDebugLevel(true, 4);
00319 }
00320 
00321 RunnerManager::RunnerManager(KConfigGroup &c, QObject *parent)
00322     : QObject(parent),
00323       d(new RunnerManagerPrivate(this))
00324 {
00325     // Should this be really needed? Maybe d->loadConfiguration(c) would make
00326     // more sense.
00327     KConfigGroup config(&c, "PlasmaRunnerManager");
00328     d->loadConfiguration(config);
00329     //ThreadWeaver::setDebugLevel(true, 4);
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 //Reordering is here so data is not reordered till strictly needed
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     //TODO: this function is not const as it may be used for learning
00375     AbstractRunner *runner = match.runner();
00376 
00377     foreach (FindMatchesJob *job, d->searchJobs) {
00378         if (job->runner() == runner && !job->isFinished()) {
00379             //kDebug() << "!!!!!!!!!!!!!!!!!!! uh oh!";
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         // we already are searching for this!
00420         return;
00421     }
00422 
00423     reset();
00424 //    kDebug() << "runners searching for" << term << "on" << runnerName;
00425     d->context.setQuery(term);
00426 
00427     AbstractRunner::List runable;
00428 
00429     //if the name is not empty we will launch only the specified runner
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 //            kDebug() << "launching" << r->name();
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         // we already are searching for this!
00468         emit matchesChanged(d->context.matches());
00469         return false;
00470     }
00471 
00472     reset();
00473     //kDebug() << "executing query about " << term << "on" << runnerName;
00474     d->context.setQuery(term);
00475     AbstractRunner *r = runner(runnerName);
00476 
00477     if (!r) {
00478         //kDebug() << "failed to find the runner";
00479         return false;
00480     }
00481 
00482     if ((r->ignoredTypes() & d->context.type()) != 0) {
00483         //kDebug() << "ignored!";
00484         return false;
00485     }
00486 
00487     r->performMatch(d->context);
00488     //kDebug() << "succeeded with" << d->context.matches().count() << "results";
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     // If ThreadWeaver is idle, it is safe to clear previous jobs
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         //kDebug() << "job actually done, running now **************";
00510         d->deferredRun.run(d->context);
00511         d->deferredRun = QueryMatch(0);
00512     }
00513 
00514     d->context.reset();
00515 }
00516 
00517 } // Plasma namespace
00518 
00519 #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
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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