summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Kamm <christian.d.kamm@nokia.com>2010-05-18 09:38:31 +0200
committerChristian Kamm <christian.d.kamm@nokia.com>2010-05-18 09:38:31 +0200
commite67705486cc18d44f13bbb20b863e245795fe7ee (patch)
tree241ed4e241cf3e468121c1e0fd11c39a30dcf00b
parent1ab9164ae7c782854a89f4a5021633c1c2c67aa5 (diff)
Scripts: Use synchronized threads instead of coroutines.
This commit removes the Coroutine dependency and the need to specify the DEPENDENCY_PATH qmake argument. Coroutines were able to move WebKit's thread local storage into an invalid state sometimes, leading to hard-to-reproduce crashes. Threads don't have that problem. However, it means signals emitted from ScriptInterfaces will now be queued. So their arguments need to be registered with the Qt metatype system. Task-number: QTSIM-22 Reviewed-by: owolff
-rw-r--r--library/components/locationui.cpp2
-rw-r--r--library/remotecontrolwidget.pro18
-rw-r--r--library/scriptadapter.cpp158
-rw-r--r--library/scriptadapter.h36
4 files changed, 96 insertions, 118 deletions
diff --git a/library/components/locationui.cpp b/library/components/locationui.cpp
index d658ab1..263aa06 100644
--- a/library/components/locationui.cpp
+++ b/library/components/locationui.cpp
@@ -46,6 +46,8 @@ LocationUi::LocationUi(QWidget *parent)
, mTimeEdit(0)
, mScriptInterface(0)
{
+ qRegisterMetaType<LocationUi::LocationData>("LocationUi::LocationData");
+
mScriptInterface = new LocationScriptInterface(this);
QStringList tags;
diff --git a/library/remotecontrolwidget.pro b/library/remotecontrolwidget.pro
index 3174d14..ed18905 100644
--- a/library/remotecontrolwidget.pro
+++ b/library/remotecontrolwidget.pro
@@ -64,24 +64,6 @@ RESOURCES += \
style/style.qrc \
components/component.qrc
-
-# set dependency include and lib paths
-!isEmpty(DEPENDENCY_PATH) {
- INCLUDEPATH *= $$DEPENDENCY_PATH/include
- !mac {
- LIBS *= -L$$DEPENDENCY_PATH/lib
- } else {
- QMAKE_LFLAGS *= -F$$DEPENDENCY_PATH/lib
- }
- unix: isEmpty(INSTALLER) {
- LIBS *= -Wl,-rpath,$$DEPENDENCY_PATH/lib
- }
-}
-
-# Coroutine library
-qtAddLibrary(coroutine)
-
-
# install rules
isEmpty(PREFIX): PREFIX = $$OUT_PWD/install
diff --git a/library/scriptadapter.cpp b/library/scriptadapter.cpp
index a07aa5f..7885277 100644
--- a/library/scriptadapter.cpp
+++ b/library/scriptadapter.cpp
@@ -31,8 +31,6 @@
#include "optionsitem.h"
-#include <coroutine/coroutine.h>
-
#include <QtCore/QMetaEnum>
#include <QtCore/QCoreApplication>
#include <QtCore/QTimer>
@@ -50,7 +48,6 @@ ScriptAdapter::ScriptAdapter(QObject *parent)
: QObject(parent)
{
mScriptRunner = new QTimer(this);
- mScriptRunner->setSingleShot(true);
mScriptRunner->setInterval(100);
connect(mScriptRunner, SIGNAL(timeout()), this, SLOT(continueScripts()));
mScriptRunner->start();
@@ -60,6 +57,16 @@ ScriptAdapter::ScriptAdapter(QObject *parent)
ScriptAdapter::~ScriptAdapter()
{
+ // stop active scripts
+ foreach (Script *script, mActiveScripts) {
+ if (!script->isFinished()) {
+ mCurrentScript = script;
+ script->requestTerminate();
+ script->mWaitCondition.wakeOne();
+ script->wait(10000);
+ }
+ }
+
delete mMessageBox;
}
@@ -99,6 +106,7 @@ Script *ScriptAdapter::run(const QString &filePath)
QFileInfo fileInfo(file);
Script *script = new Script(fileInfo.baseName(), file.readAll(), this);
+ script->start();
mActiveScripts += script;
emit scriptStart(script);
@@ -110,7 +118,7 @@ void ScriptAdapter::continueScripts() {
for (int i = 0; i < mActiveScripts.length(); ++i) {
Script *script = mActiveScripts[i];
- if (script->mCoroutine->status() == Coroutine::Terminated) {
+ if (script->isFinished()) {
emit scriptStop(i);
mActiveScripts.removeOne(script);
@@ -120,125 +128,95 @@ void ScriptAdapter::continueScripts() {
if (!script->isPaused() || script->isTerminating()) {
mCurrentScript = script;
- script->mCoroutine->cont();
+ script->mSuspended = false;
+ script->mWaitCondition.wakeOne();
+ while (!script->mSuspended && !script->isFinished())
+ QThread::yieldCurrentThread();
mCurrentScript = 0;
}
- }
-
- mScriptRunner->start();
-}
-
-void ScriptAdapter::yield(int ms)
-{
- if (!mCurrentScript)
- return;
- QTime timer;
- timer.start();
- while (timer.elapsed() < ms && !mCurrentScript->isTerminating()) {
- mCurrentScript->suspend();
+ foreach (const MessageBoxData &message, mPendingMessages) {
+ mMessageBox->setText(message.text);
+ mMessageBox->setWindowTitle(message.title);
+ mMessageBox->exec();
+ }
+ mPendingMessages.clear();
}
}
-void ScriptAdapter::messageBox(const QString &text, const QString &title)
-{
- if (!mCurrentScript)
- return;
-
- mMessageBox->setText(text);
- mMessageBox->setWindowTitle(title);
-
- // message boxes should block
- mCurrentScript->beginBlocking();
- mMessageBox->exec();
- mCurrentScript->endBlocking();
-}
-
-static QScriptValue safePrint(QScriptContext *context, QScriptEngine *engine)
-{
- QString result;
- for (int i = 0; i < context->argumentCount(); ++i) {
- if (i != 0)
- result.append(QLatin1Char(' '));
- QString s(context->argument(i).toString());
- if (engine->hasUncaughtException())
- break;
- result.append(s);
- }
- if (engine->hasUncaughtException())
- return engine->uncaughtException();
-
-#ifdef Q_OS_WIN
- if (IsDebuggerPresent())
- qDebug("%s", qPrintable(result));
-#else
- qDebug("%s", qPrintable(result));
-#endif
-
- return engine->undefinedValue();
-}
Script::Script(const QString &name, const QString &scriptCode,
ScriptAdapter *adapter)
- : QObject(adapter)
+ : QThread(adapter)
, mName(name)
, mScriptCode(scriptCode)
, mAdapter(adapter)
+ , mSuspended(false)
, mPaused(false)
, mTerminate(false)
{
- mCoroutine = Coroutine::build(this, &Script::run);
- mCoroutine->createStack(524228);
-
- mStopTimer.setInterval(10);
- connect(&mStopTimer, SIGNAL(timeout()), this, SLOT(suspend()));
+}
+void Script::setupScriptEngine()
+{
// trigger event processing during script evaluation
- mEngine.setProcessEventsInterval(10);
+ mEngine->setProcessEventsInterval(10);
- // inject the ScriptAdapters functions into the global scope
- QScriptValue adapterValue = mEngine.newQObject(adapter);
+ // inject the functions into the global scope
+ QScriptValue adapterValue = mEngine->newQObject(this);
QStringList exposedFunctions = QStringList() << "yield" << "messageBox";
foreach (const QString &functionName, exposedFunctions) {
QScriptValue functionValue = adapterValue.property(functionName);
- mEngine.globalObject().setProperty(functionName, functionValue);
+ mEngine->globalObject().setProperty(functionName, functionValue);
}
- // override print, the default uses qDebug and that is not allowed in coroutines!
- QScriptValue printFn = mEngine.newFunction(safePrint, 1);
- mEngine.globalObject().setProperty("print", printFn);
-
+ // setup script interfaces
QHashIterator<QString, QObject *> iter(mAdapter->mScriptInterfaces);
while (iter.hasNext()) {
iter.next();
QStringList qualifiedName = iter.key().split(QLatin1Char('.'));
- QScriptValue targetValue = mEngine.globalObject();
+ QScriptValue targetValue = mEngine->globalObject();
for (int i = 0; i < qualifiedName.size() - 1; ++i) {
QScriptValue v = targetValue.property(qualifiedName.at(i));
if (! v.isValid()) {
- v = mEngine.newObject();
+ v = mEngine->newObject();
targetValue.setProperty(qualifiedName.at(i), v);
}
targetValue = v;
}
- QScriptValue scriptInterface = mEngine.newQObject(iter.value());
+ QScriptValue scriptInterface = mEngine->newQObject(iter.value());
targetValue.setProperty(qualifiedName.last(), scriptInterface);
}
}
void Script::run()
{
- mStopTimer.start();
- QScriptValue value = mEngine.evaluate(mScriptCode);
+ mEngine = new QScriptEngine;
+ setupScriptEngine();
+
+ mStopTimer = new QTimer;
+ mStopTimer->setInterval(10);
+ mStopTimer->setSingleShot(true);
+ connect(mStopTimer, SIGNAL(timeout()), this, SLOT(suspend()));
+
+ mMutex.lock();
+ suspend();
+
+ QScriptValue value = mEngine->evaluate(mScriptCode);
if (value.isError()) {
- mAdapter->messageBox(value.toString(), tr("Script errors"));
+ messageBox(value.toString(), tr("Script errors"));
}
- mStopTimer.stop();
+
+ mMutex.unlock();
+
+ delete mStopTimer;
+ delete mEngine;
}
void Script::requestTerminate()
{
mTerminate = true;
+ mEngine->abortEvaluation(mEngine->currentContext()->throwError("Aborted"));
}
void Script::togglePause()
@@ -248,22 +226,28 @@ void Script::togglePause()
void Script::suspend()
{
- if (Coroutine::currentCoroutine() == mCoroutine) {
- Coroutine::yield();
+ if (mTerminate)
+ return;
- if (mTerminate) {
- mEngine.abortEvaluation(mEngine.currentContext()->throwError("Aborted"));
- mStopTimer.stop();
- }
- }
+ mSuspended = true;
+ mWaitCondition.wait(&mMutex);
+ mStopTimer->start();
}
-void Script::beginBlocking()
+void Script::yield(int ms)
{
- mStopTimer.stop();
+ QTime timer;
+ timer.start();
+ while (timer.elapsed() < ms && !isTerminating()) {
+ suspend();
+ }
}
-void Script::endBlocking()
+void Script::messageBox(const QString &text, const QString &title)
{
- mStopTimer.start();
+ ScriptAdapter::MessageBoxData data;
+ data.text = text;
+ data.title = title;
+ mAdapter->mPendingMessages.append(data);
+ suspend();
}
diff --git a/library/scriptadapter.h b/library/scriptadapter.h
index c430d30..847280e 100644
--- a/library/scriptadapter.h
+++ b/library/scriptadapter.h
@@ -36,6 +36,9 @@
#include <QtCore/QTimer>
#include <QtCore/QStringList>
#include <QtCore/QHash>
+#include <QtCore/QThread>
+#include <QtCore/QMutex>
+#include <QtCore/QWaitCondition>
#include <QtScript/QScriptEngine>
class Script;
@@ -48,9 +51,6 @@ public:
explicit ScriptAdapter(QObject *parent = 0);
virtual ~ScriptAdapter();
- Q_INVOKABLE void yield(int ms);
- Q_INVOKABLE void messageBox(const QString &text, const QString &title = QString());
-
void runAutostartScripts();
Script *script(int index);
@@ -74,12 +74,16 @@ private:
QMessageBox *mMessageBox;
+ struct MessageBoxData {
+ QString title;
+ QString text;
+ };
+ QList<MessageBoxData> mPendingMessages;
+
friend class Script;
};
-class Coroutine;
-
-class Script : public QObject
+class Script : public QThread
{
Q_OBJECT
public:
@@ -97,25 +101,31 @@ public:
bool isPaused() const
{ return mPaused; }
- void beginBlocking();
- void endBlocking();
-
public slots:
void suspend();
+protected:
+ virtual void run();
+
+ Q_INVOKABLE void yield(int ms);
+ Q_INVOKABLE void messageBox(const QString &text, const QString &title = QString());
+
private:
- void run();
+ void setupScriptEngine();
+
+ QWaitCondition mWaitCondition;
+ QMutex mMutex;
QString mName;
QString mScriptCode;
ScriptAdapter *mAdapter;
- Coroutine *mCoroutine;
+ bool mSuspended;
bool mPaused;
bool mTerminate;
- QScriptEngine mEngine;
- QTimer mStopTimer;
+ QScriptEngine *mEngine;
+ QTimer *mStopTimer;
friend class ScriptAdapter;
};