huge patch, but it's needed to avoid crashes and i can't wait on other people's pending work forever. so...

QueryMatch by value! shared dptr == no copy of data either! so (nearly) all the speed love of pointers with none of the crash hate. woo!

RunnerContext also by value! die pointers die!

also get rid of tons of locking that just doesn't make sense anymore; get rid of data priority enum as well since it's completely meaningless.

speed, glory, stability! ftw!

svn path=/trunk/KDE/kdebase/workspace/libs/plasma/; revision=805661
This commit is contained in:
Aaron J. Seigo 2008-05-09 03:49:11 +00:00
parent 5de8aff8ce
commit 6019dc7b27
10 changed files with 109 additions and 133 deletions

View File

@ -62,7 +62,7 @@ public:
script = Plasma::loadScriptEngine(api, runner); script = Plasma::loadScriptEngine(api, runner);
if (!script) { if (!script) {
kDebug() << "Could not create a" << api << "ScriptEngine for the" kDebug() << "Could not create a(n)" << api << "ScriptEngine for the"
<< runnerDescription.name() << "Runner."; << runnerDescription.name() << "Runner.";
delete package; delete package;
package = 0; package = 0;
@ -118,7 +118,7 @@ KConfigGroup AbstractRunner::config() const
return KConfigGroup(&runners, group); return KConfigGroup(&runners, group);
} }
void AbstractRunner::performMatch( Plasma::RunnerContext &globalContext ) void AbstractRunner::performMatch(Plasma::RunnerContext &globalContext)
{ {
static const int reasonableRunTime = 1500; static const int reasonableRunTime = 1500;
static const int fastEnoughTime = 250; static const int fastEnoughTime = 250;
@ -126,7 +126,7 @@ void AbstractRunner::performMatch( Plasma::RunnerContext &globalContext )
d->runtime.restart(); d->runtime.restart();
//TODO :this is a copy ctor //TODO :this is a copy ctor
RunnerContext localContext(globalContext,0); RunnerContext localContext(globalContext,0);
match(&localContext); match(localContext);
// automatically rate limit runners that become slooow // automatically rate limit runners that become slooow
const int runtime = d->runtime.elapsed(); const int runtime = d->runtime.elapsed();
bool slowed = speed() == SlowSpeed; bool slowed = speed() == SlowSpeed;
@ -212,8 +212,6 @@ void AbstractRunner::setIgnoredTypes(RunnerContext::Types types)
d->blackListed = types; d->blackListed = types;
} }
KService::List AbstractRunner::serviceQuery(const QString &serviceType, const QString &constraint) const KService::List AbstractRunner::serviceQuery(const QString &serviceType, const QString &constraint) const
{ {
QMutexLocker lock(&Private::bigLock); QMutexLocker lock(&Private::bigLock);
@ -225,14 +223,14 @@ QMutex* AbstractRunner::bigLock() const
return &Private::bigLock; return &Private::bigLock;
} }
void AbstractRunner::run(const Plasma::RunnerContext *search, const Plasma::QueryMatch *action) void AbstractRunner::run(const Plasma::RunnerContext &search, const Plasma::QueryMatch &action)
{ {
if (d->script) { if (d->script) {
return d->script->run(search, action); return d->script->run(search, action);
} }
} }
void AbstractRunner::match(Plasma::RunnerContext *search) void AbstractRunner::match(Plasma::RunnerContext &search)
{ {
if (d->script) { if (d->script) {
return d->script->match(search); return d->script->match(search);

View File

@ -89,14 +89,14 @@ class PLASMA_EXPORT AbstractRunner : public QObject
* to return from this method right away, nor to create all matches * to return from this method right away, nor to create all matches
* here. * here.
*/ */
virtual void match(Plasma::RunnerContext *search); virtual void match(Plasma::RunnerContext &context);
/** /**
* Triggers a call to match. * Triggers a call to match.
* *
* @arg globalContext the search context used in executing this match. * @arg globalContext the search context used in executing this match.
*/ */
void performMatch(Plasma::RunnerContext &globalContext); void performMatch(Plasma::RunnerContext &context);
/** /**
* If the runner has options that the user can interact with to modify * If the runner has options that the user can interact with to modify
@ -132,7 +132,7 @@ class PLASMA_EXPORT AbstractRunner : public QObject
* Called whenever an exact or possible match associated with this * Called whenever an exact or possible match associated with this
* runner is triggered. * runner is triggered.
*/ */
virtual void run(const Plasma::RunnerContext *context, const Plasma::QueryMatch *action); virtual void run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &action);
/** /**
* The nominal speed of the runner. * The nominal speed of the runner.

View File

@ -21,6 +21,7 @@
#include <QPointer> #include <QPointer>
#include <QVariant> #include <QVariant>
#include <QSharedData>
#include <QStringList> #include <QStringList>
#include <QIcon> #include <QIcon>
@ -31,17 +32,32 @@
namespace Plasma namespace Plasma
{ {
class QueryMatch::Private class QueryMatch::Private : public QSharedData
{ {
public: public:
Private(AbstractRunner *r) Private(AbstractRunner *r)
: runner(r), : QSharedData(),
runner(r),
type(QueryMatch::ExactMatch), type(QueryMatch::ExactMatch),
enabled(true), enabled(true),
relevance(.7) relevance(.7)
{ {
} }
Private(const Private &other)
{
kDebug() << "copy";
runner = other.runner;
type = other.type;
id = other.id;
text = other.text;
subtext = other.subtext;
icon = other.icon;
data = other.data;
enabled = other.enabled;
relevance = other.relevance;
}
QPointer<AbstractRunner> runner; QPointer<AbstractRunner> runner;
QueryMatch::Type type; QueryMatch::Type type;
QString id; QString id;
@ -61,9 +77,13 @@ QueryMatch::QueryMatch(AbstractRunner *runner)
// kDebug() << "new match created"; // kDebug() << "new match created";
} }
QueryMatch::QueryMatch(const QueryMatch &other)
: d(other.d)
{
}
QueryMatch::~QueryMatch() QueryMatch::~QueryMatch()
{ {
delete d;
} }
QString QueryMatch::id() const QString QueryMatch::id() const
@ -161,14 +181,19 @@ bool QueryMatch::operator<(const QueryMatch& other) const
return d->relevance < other.d->relevance; return d->relevance < other.d->relevance;
} }
void QueryMatch::run(const RunnerContext *context) const QueryMatch& QueryMatch::operator=(const QueryMatch &other)
{ {
Q_ASSERT(context); kDebug();
d = other.d;
return *this;
}
void QueryMatch::run(const RunnerContext &context) const
{
//kDebug() << "we run the term" << context->query() << "whose type is" << context->mimetype(); //kDebug() << "we run the term" << context->query() << "whose type is" << context->mimetype();
if (d->runner) { if (d->runner) {
//TODO: this could be dangerous if the runner is deleted behind our backs. //TODO: this could be dangerous if the runner is deleted behind our backs.
d->runner->run(context, this); d->runner->run(context, *this);
} }
} }

View File

@ -20,6 +20,8 @@
#ifndef QUERYMATCH_H #ifndef QUERYMATCH_H
#define QUERYMATCH_H #define QUERYMATCH_H
#include <QSharedDataPointer>
#include <plasma/plasma_export.h> #include <plasma/plasma_export.h>
class QIcon; class QIcon;
@ -43,7 +45,8 @@ class PLASMA_EXPORT QueryMatch
/** /**
* The type of match. Value is important here as it is used for sorting * The type of match. Value is important here as it is used for sorting
*/ */
enum Type { CompletionMatch = 10 /**< Possible completion for the data of the query */, enum Type { NoMatch = 0 /**< Null match */,
CompletionMatch = 10 /**< Possible completion for the data of the query */,
PossibleMatch = 30 /**< Something that may match the query */, PossibleMatch = 30 /**< Something that may match the query */,
InformationalMatch = 50 /**< A purely informational, non-actionable match, InformationalMatch = 50 /**< A purely informational, non-actionable match,
such as the answer to a question or calculation*/, such as the answer to a question or calculation*/,
@ -66,6 +69,12 @@ class PLASMA_EXPORT QueryMatch
* @arg runner the runner this match belongs to * @arg runner the runner this match belongs to
*/ */
explicit QueryMatch(AbstractRunner *runner); explicit QueryMatch(AbstractRunner *runner);
/**
* Copy constructor
*/
QueryMatch(const QueryMatch &other);
~QueryMatch(); ~QueryMatch();
/** /**
@ -116,8 +125,14 @@ class PLASMA_EXPORT QueryMatch
bool isEnabled() const; bool isEnabled() const;
bool operator<(const QueryMatch& other) const; bool operator<(const QueryMatch& other) const;
QueryMatch& operator=(const QueryMatch &other);
void run(const RunnerContext *context) const; /**
* Requests this match to activae using the given context
*
* @param context the context to use in conjunction with this run
*/
void run(const RunnerContext &context) const;
/** /**
* Sets data to be used internally by the associated * Sets data to be used internally by the associated
@ -148,7 +163,7 @@ class PLASMA_EXPORT QueryMatch
private: private:
class Private; class Private;
Private * const d; QSharedDataPointer<Private> d;
}; };
} }

View File

@ -34,49 +34,37 @@
#include "querymatch.h" #include "querymatch.h"
#define LOCK_FOR_READ(context) if (context->d->policy == Shared) { context->d->lock.lockForRead(); } //#define LOCK_FOR_READ(context) if (context->d->policy == Shared) { context->d->lock.lockForRead(); }
#define LOCK_FOR_WRITE(context) if (context->d->policy == Shared) { context->d->lock.lockForWrite(); } //#define LOCK_FOR_WRITE(context) if (context->d->policy == Shared) { context->d->lock.lockForWrite(); }
#define UNLOCK(context) if (context->d->policy == Shared) { context->d->lock.unlock(); } //#define UNLOCK(context) if (context->d->policy == Shared) { context->d->lock.unlock(); }
/*
#define LOCK_FOR_READ(context) context->d->lock.lockForRead(); #define LOCK_FOR_READ(context) context->d->lock.lockForRead();
#define LOCK_FOR_WRITE(context) context->d->lock.lockForWrite(); #define LOCK_FOR_WRITE(context) context->d->lock.lockForWrite();
#define UNLOCK(context) context->d->lock.unlock(); #define UNLOCK(context) context->d->lock.unlock();
*/
namespace Plasma namespace Plasma
{ {
class RunnerContext::Private : public QSharedData class RunnerContext::Private : public QSharedData
{ {
public: public:
Private(RunnerContext *context, RunnerContext::DataPolicy p) Private(RunnerContext *context)
: QSharedData(), : QSharedData(),
type(RunnerContext::UnknownType), type(RunnerContext::UnknownType),
q(context), q(context)
policy(p)
{ {
} }
Private(const RunnerContext::Private& p) Private(const RunnerContext::Private& p)
: QSharedData(), : QSharedData(),
term(p.term), type(RunnerContext::None),
mimeType(p.mimeType), q(p.q)
type(p.type),
q(p.q),
policy(p.policy)
{ {
//kDebug() << "¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿boo yeah"; //kDebug() << "¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿boo yeah" << type;
} }
~Private() ~Private()
{ {
if (policy == Shared) {
lock.lockForWrite();
}
qDeleteAll(matches);
matches.clear();
if (policy == Shared) {
lock.unlock();
}
} }
/** /**
@ -84,10 +72,10 @@ class RunnerContext::Private : public QSharedData
*/ */
void determineType() void determineType()
{ {
if (policy == Shared) { // NOTE! this method must NEVER be called from
lock.lockForWrite(); // code that may be running in multiple threads
} // with the same data.
type = UnknownType;
QString path = KShell::tildeExpand(term); QString path = KShell::tildeExpand(term);
int space = term.indexOf(' '); int space = term.indexOf(' ');
@ -113,30 +101,22 @@ class RunnerContext::Private : public QSharedData
mimeType = mimeTypePtr->name(); mimeType = mimeTypePtr->name();
} }
} }
} else if (term.contains('.')) {
// default to a network location so we can can do things like www.kde.org
type = NetworkLocation;
} }
} }
if (policy == Shared) {
lock.unlock();
}
} }
QReadWriteLock lock; QReadWriteLock lock;
QList<QueryMatch*> matches; QList<QueryMatch> matches;
QString term; QString term;
QString mimeType; QString mimeType;
RunnerContext::Type type; RunnerContext::Type type;
RunnerContext * q; RunnerContext * q;
const RunnerContext::DataPolicy policy;
}; };
RunnerContext::RunnerContext(QObject *parent, DataPolicy policy) RunnerContext::RunnerContext(QObject *parent)
: QObject(parent), : QObject(parent),
d(new Private(this, policy)) d(new Private(this))
{ {
} }
@ -155,25 +135,22 @@ RunnerContext::~RunnerContext()
void RunnerContext::reset() void RunnerContext::reset()
{ {
// Locks are needed as other contexts can be copied of this one
// We will detach if we are a copy of someone. But we will reset // We will detach if we are a copy of someone. But we will reset
// if we are the 'main' context others copied from. Resetting // if we are the 'main' context others copied from. Resetting
// one RunnerContext makes all the copies oneobsolete. // one RunnerContext makes all the copies oneobsolete.
d.detach(); d.detach();
LOCK_FOR_WRITE(this)
//kDebug() << "reset searchContext";
d->type = RunnerContext::UnknownType;
d->term.clear();
d->mimeType.clear();
UNLOCK(this);
// we still have to remove all the matches, since if the // we still have to remove all the matches, since if the
// ref count was 1 (e.g. only the RunnerContext is using // ref count was 1 (e.g. only the RunnerContext is using
// the dptr) then we won't get a copy made // the dptr) then we won't get a copy made
removeAllMatches(); if (!d->matches.isEmpty()) {
d->matches.clear();
emit d->q->matchesChanged();
}
d->term.clear();
d->mimeType.clear();
d->type = UnknownType;
//kDebug() << "match count" << d->matches.count(); //kDebug() << "match count" << d->matches.count();
} }
@ -185,19 +162,17 @@ void RunnerContext::setQuery(const QString &term)
return; return;
} }
LOCK_FOR_WRITE(this)
d->term = term; d->term = term;
UNLOCK(this);
d->determineType(); d->determineType();
} }
QString RunnerContext::query() const QString RunnerContext::query() const
{ {
LOCK_FOR_READ(this) // the query term should never be set after
QString term = d->term; // a search starts. in fact, reset() ensures this
UNLOCK(this); // and setQuery(QString) calls reset()
return term; return d->term;
} }
RunnerContext::Type RunnerContext::type() const RunnerContext::Type RunnerContext::type() const
@ -210,9 +185,11 @@ QString RunnerContext::mimeType() const
return d->mimeType; return d->mimeType;
} }
bool RunnerContext::addMatches(const QString& term, const QList<QueryMatch*> &matches) bool RunnerContext::addMatches(const QString& term, const QList<QueryMatch> &matches)
{ {
if (query() != term || matches.isEmpty()) { Q_UNUSED(term)
if (matches.isEmpty()) {
return false; return false;
} }
@ -227,11 +204,9 @@ bool RunnerContext::addMatches(const QString& term, const QList<QueryMatch*> &ma
return true; return true;
} }
bool RunnerContext::addMatch(const QString &term, QueryMatch *match) bool RunnerContext::addMatch(const QString &term, const QueryMatch &match)
{ {
if (query() != term) { Q_UNUSED(term)
return false;
}
LOCK_FOR_WRITE(this) LOCK_FOR_WRITE(this)
d->matches << match; d->matches << match;
@ -242,35 +217,14 @@ bool RunnerContext::addMatch(const QString &term, QueryMatch *match)
return true; return true;
} }
QList<QueryMatch> RunnerContext::matches() const
QList<QueryMatch *> RunnerContext::matches() const
{ {
LOCK_FOR_READ(this) LOCK_FOR_READ(this)
QList<QueryMatch *> matches = d->matches; QList<QueryMatch> matches = d->matches;
UNLOCK(this); UNLOCK(this);
return matches; return matches;
} }
void RunnerContext::removeAllMatches()
{
LOCK_FOR_WRITE(this)
if (!d->matches.isEmpty()) {
QList<QueryMatch*> matches = d->matches;
d->matches.clear();
UNLOCK(this);
// in case someone is still holding on to the Matches
// when we emit the matchesChanged() signal, we don't
// delete the matches until after the signal is handled.
// a bit safer.
emit d->q->matchesChanged();
qDeleteAll(matches);
} else {
UNLOCK(this);
}
}
} }
#include "runnercontext.moc" #include "runnercontext.moc"

View File

@ -57,17 +57,10 @@ class PLASMA_EXPORT RunnerContext : public QObject
Q_DECLARE_FLAGS(Types, Type) Q_DECLARE_FLAGS(Types, Type)
enum DataPolicy { Shared = 0, explicit RunnerContext(QObject *parent = 0);
SingleConsumer
};
explicit RunnerContext(QObject *parent = 0, DataPolicy policy = Shared);
/** /**
* Constructs a RunnerContext with a DataPolicy of SingleConsumer that * Copy constructor
* contains the search metadata (though none of the currently registered
* matches) from the passed in RunnerContext. Primarily useful for creating
* a thread-local copy of a Shared RunnerContext.
*/ */
explicit RunnerContext(RunnerContext& other, QObject *parent = 0); explicit RunnerContext(RunnerContext& other, QObject *parent = 0);
@ -114,21 +107,20 @@ class PLASMA_EXPORT RunnerContext : public QObject
* *
* @return true if matches were added, false if matches were e.g. outdated * @return true if matches were added, false if matches were e.g. outdated
*/ */
bool addMatches(const QString& term, const QList<QueryMatch *> &matches); bool addMatches(const QString& term, const QList<QueryMatch> &matches);
/** /**
* Appends a match to the existing list of matches. * Appends a match to the existing list of matches.
* The RunnerContext takes over ownership of the match on successful addition. * The RunnerContext takes over ownership of the match on successful addition.
* *
* If you are going to be adding multiple matches, it is better to use * If you are going to be adding multiple matches, use addMatches instead.
* addMatches instead.
* *
* @arg term the search term that this match was generated for * @arg term the search term that this match was generated for
* @arg match the match to add * @arg match the match to add
* *
* @return true if the match was added, false otherwise. * @return true if the match was added, false otherwise.
*/ */
bool addMatch(const QString &term, QueryMatch *match); bool addMatch(const QString &term, const QueryMatch &match);
/** /**
* Takes the matches from this RunnerContext and copies to them another. * Takes the matches from this RunnerContext and copies to them another.
@ -143,12 +135,7 @@ class PLASMA_EXPORT RunnerContext : public QObject
/** /**
* Retrieves all available matches for the current search term. * Retrieves all available matches for the current search term.
*/ */
QList<QueryMatch *> matches() const; QList<QueryMatch> matches() const;
/**
* Removes all matches from this RunnerContext.
*/
void removeAllMatches();
Q_SIGNALS: Q_SIGNALS:
void matchesChanged(); void matchesChanged();

View File

@ -310,13 +310,10 @@ RunnerManager::RunnerManager(KConfigGroup& config, QObject *parent)
//ThreadWeaver::setDebugLevel(true, 4); //ThreadWeaver::setDebugLevel(true, 4);
} }
RunnerManager::~RunnerManager() RunnerManager::~RunnerManager()
{ {
d->context.removeAllMatches();
} }
AbstractRunner* RunnerManager::runner(const QString &name) const AbstractRunner* RunnerManager::runner(const QString &name) const
{ {
//TODO: using a list of runners, if this gets slow, switch to hash //TODO: using a list of runners, if this gets slow, switch to hash
@ -336,15 +333,15 @@ RunnerContext* RunnerManager::searchContext() const
} }
//Reordering is here so data is not reordered till strictly needed //Reordering is here so data is not reordered till strictly needed
QList<QueryMatch *> RunnerManager::matches() const QList<QueryMatch> RunnerManager::matches() const
{ {
return d->context.matches(); return d->context.matches();
} }
void RunnerManager::run(const QueryMatch *match) void RunnerManager::run(const QueryMatch &match)
{ {
//TODO: this function is not const as it may be used for learning //TODO: this function is not const as it may be used for learning
match->run(&d->context); match.run(d->context);
} }
void RunnerManager::launchQuery (const QString & term, const QString & runnerName) void RunnerManager::launchQuery (const QString & term, const QString & runnerName)

View File

@ -65,13 +65,13 @@ class PLASMA_EXPORT RunnerManager : public QObject
* Retrieves all available matches found so far for the previously launched query * Retrieves all available matches found so far for the previously launched query
* @return List of matches * @return List of matches
*/ */
QList<QueryMatch *> matches() const; QList<QueryMatch> matches() const;
/** /**
* Runs a given match * Runs a given match
* @arg pointer to the match to be executed * @arg pointer to the match to be executed
*/ */
void run(const QueryMatch *match); void run(const QueryMatch &match);
public Q_SLOTS: public Q_SLOTS:
/** /**
@ -104,7 +104,7 @@ class PLASMA_EXPORT RunnerManager : public QObject
/** /**
* Emited each time a new match is added to the list * Emited each time a new match is added to the list
*/ */
void matchesChanged(const QList<Plasma::QueryMatch*> &matches); void matchesChanged(const QList<Plasma::QueryMatch> &matches);
private: private:
Q_PRIVATE_SLOT(d, void scheduleMatchesChanged()) Q_PRIVATE_SLOT(d, void scheduleMatchesChanged())

View File

@ -52,12 +52,12 @@ AbstractRunner* RunnerScript::runner() const
return d->runner; return d->runner;
} }
void RunnerScript::match(Plasma::RunnerContext *search) void RunnerScript::match(Plasma::RunnerContext &search)
{ {
Q_UNUSED(search) Q_UNUSED(search)
} }
void RunnerScript::run(const Plasma::RunnerContext *search, const Plasma::QueryMatch *action) void RunnerScript::run(const Plasma::RunnerContext &search, const Plasma::QueryMatch &action)
{ {
Q_UNUSED(search) Q_UNUSED(search)
Q_UNUSED(action) Q_UNUSED(action)

View File

@ -62,13 +62,13 @@ public:
* RunnerContext::addInformationalMatch, RunnerContext::addExactMatch, and * RunnerContext::addInformationalMatch, RunnerContext::addExactMatch, and
* RunnerContext::addPossibleMatch. * RunnerContext::addPossibleMatch.
*/ */
virtual void match(Plasma::RunnerContext *search); virtual void match(Plasma::RunnerContext &search);
/** /**
* Called whenever an exact or possible match associated with this * Called whenever an exact or possible match associated with this
* runner is triggered. * runner is triggered.
*/ */
virtual void run(const Plasma::RunnerContext *search, const Plasma::QueryMatch *action); virtual void run(const Plasma::RunnerContext &search, const Plasma::QueryMatch &action);
protected: protected:
/** /**