Debugger: Move server into it's own thread
authorKai Koehne <kai.koehne@nokia.com>
Wed, 24 Aug 2011 11:51:07 +0000 (13:51 +0200)
committerQt by Nokia <qt-info@nokia.com>
Tue, 22 Nov 2011 12:45:27 +0000 (13:45 +0100)
So far the debugger infrastructure was running in the GUI thread,
which required e.g. nested event loops to implement blocking behavior.
The server and networking code are now running in their own thread,
while the services are still running in the main thread.

Because v8 isn't thread safe, we're adding two new JSEngines + isolates
to qv8debugservice: One to decode JSON messages in the debugger thread,
and one in the GUI thread.

Change-Id: I746f5e203968f7bcc510fb66118c88ef0fd0cd14
Reviewed-by: Christiaan Janssen <christiaan.janssen@nokia.com>
src/declarative/debugger/qdeclarativedebugserver.cpp
src/declarative/debugger/qdeclarativedebugserver_p.h
src/declarative/debugger/qdeclarativeenginedebugservice.cpp
src/declarative/debugger/qdeclarativeenginedebugservice_p.h
src/declarative/debugger/qdeclarativeinspectorservice.cpp
src/declarative/debugger/qdeclarativeinspectorservice_p.h
src/declarative/debugger/qv8debugservice.cpp
src/declarative/debugger/qv8debugservice_p.h
src/declarative/debugger/qv8profilerservice.cpp
tests/auto/declarative/debugger/qdeclarativedebugclient/tst_qdeclarativedebugclient.cpp
tests/auto/declarative/debugger/qdeclarativedebugservice/tst_qdeclarativedebugservice.cpp

index 5c86c45..b8963d2 100644 (file)
@@ -47,6 +47,7 @@
 #include <QtCore/QDir>
 #include <QtCore/QPluginLoader>
 #include <QtCore/QStringList>
+#include <QtCore/qwaitcondition.h>
 
 #include <private/qobject_p.h>
 #include <private/qcoreapplication_p.h>
@@ -80,6 +81,8 @@ const int protocolVersion = 1;
 // print detailed information about loading of plugins
 DEFINE_BOOL_CONFIG_OPTION(qmlDebugVerbose, QML_DEBUGGER_VERBOSE)
 
+class QDeclarativeDebugServerThread;
+
 class QDeclarativeDebugServerPrivate : public QObjectPrivate
 {
     Q_DECLARE_PUBLIC(QDeclarativeDebugServer)
@@ -87,30 +90,56 @@ public:
     QDeclarativeDebugServerPrivate();
 
     void advertisePlugins();
+    QDeclarativeDebugServerConnection *loadConnectionPlugin(const QString &pluginName);
 
     QDeclarativeDebugServerConnection *connection;
     QHash<QString, QDeclarativeDebugService *> plugins;
+    mutable QReadWriteLock pluginsLock;
     QStringList clientPlugins;
     bool gotHello;
-    QString waitingForMsgFromService;
-    bool waitingForMsgSucceeded;
+
+    QMutex messageArrivedMutex;
+    QWaitCondition messageArrivedCondition;
+    QStringList waitingForMessageNames;
+    QDeclarativeDebugServerThread *thread;
+    QPluginLoader loader;
 
 private:
     // private slot
-    void _q_deliverMessage(const QString &serviceName, const QByteArray &message);
-    static QDeclarativeDebugServerConnection *loadConnectionPlugin(QPluginLoader *loader, const QString &pluginName);
+    void _q_sendMessage(const QByteArray &message);
+};
 
+class QDeclarativeDebugServerThread : public QThread
+{
+public:
+    void setPluginName(const QString &pluginName) {
+        m_pluginName = pluginName;
+    }
+
+    void setPort(int port, bool block) {
+        m_port = port;
+        m_block = block;
+    }
+
+    void run();
+
+private:
+    QString m_pluginName;
+    int m_port;
+    bool m_block;
 };
 
 QDeclarativeDebugServerPrivate::QDeclarativeDebugServerPrivate() :
     connection(0),
     gotHello(false),
-    waitingForMsgSucceeded(false)
+    thread(0)
 {
 }
 
 void QDeclarativeDebugServerPrivate::advertisePlugins()
 {
+    Q_Q(QDeclarativeDebugServer);
+
     if (!gotHello)
         return;
 
@@ -119,11 +148,12 @@ void QDeclarativeDebugServerPrivate::advertisePlugins()
         QDataStream out(&message, QIODevice::WriteOnly);
         out << QString(QLatin1String("QDeclarativeDebugClient")) << 1 << plugins.keys();
     }
-    connection->send(message);
+
+    QMetaObject::invokeMethod(q, "_q_sendMessage", Qt::QueuedConnection, Q_ARG(QByteArray, message));
 }
 
 QDeclarativeDebugServerConnection *QDeclarativeDebugServerPrivate::loadConnectionPlugin(
-        QPluginLoader *loader, const QString &pluginName)
+        const QString &pluginName)
 {
 #ifndef QT_NO_LIBRARY
     QStringList pluginCandidates;
@@ -143,14 +173,13 @@ QDeclarativeDebugServerConnection *QDeclarativeDebugServerPrivate::loadConnectio
         if (qmlDebugVerbose())
             qDebug() << "QDeclarativeDebugServer: Trying to load plugin " << pluginPath << "...";
 
-        loader->setFileName(pluginPath);
-        if (!loader->load()) {
+        loader.setFileName(pluginPath);
+        if (!loader.load()) {
             if (qmlDebugVerbose())
-                qDebug() << "QDeclarativeDebugServer: Error while loading: " << loader->errorString();
+                qDebug() << "QDeclarativeDebugServer: Error while loading: " << loader.errorString();
             continue;
         }
-        QDeclarativeDebugServerConnection *connection = 0;
-        if (QObject *instance = loader->instance())
+        if (QObject *instance = loader.instance())
             connection = qobject_cast<QDeclarativeDebugServerConnection*>(instance);
 
         if (connection) {
@@ -163,12 +192,29 @@ QDeclarativeDebugServerConnection *QDeclarativeDebugServerPrivate::loadConnectio
         if (qmlDebugVerbose())
             qDebug() << "QDeclarativeDebugServer: Plugin does not implement interface QDeclarativeDebugServerConnection.";
 
-        loader->unload();
+        loader.unload();
     }
 #endif
     return 0;
 }
 
+void QDeclarativeDebugServerThread::run()
+{
+    QDeclarativeDebugServer *server = QDeclarativeDebugServer::instance();
+    QDeclarativeDebugServerConnection *connection
+            = server->d_func()->loadConnectionPlugin(m_pluginName);
+    if (connection) {
+        connection->setServer(QDeclarativeDebugServer::instance());
+        connection->setPort(m_port, m_block);
+    } else {
+        QCoreApplicationPrivate *appD = static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(qApp));
+        qWarning() << QString::fromAscii("QDeclarativeDebugServer: Ignoring \"-qmljsdebugger=%1\". "
+                                         "Remote debugger plugin has not been found.").arg(appD->qmljsDebugArgumentsString());
+    }
+
+    exec();
+}
+
 bool QDeclarativeDebugServer::hasDebuggingClient() const
 {
     Q_D(const QDeclarativeDebugServer);
@@ -177,10 +223,11 @@ bool QDeclarativeDebugServer::hasDebuggingClient() const
             && d->gotHello;
 }
 
+static QDeclarativeDebugServer *qDeclarativeDebugServer = 0;
+
 QDeclarativeDebugServer *QDeclarativeDebugServer::instance()
 {
     static bool commandLineTested = false;
-    static QDeclarativeDebugServer *server = 0;
 
     if (!commandLineTested) {
         commandLineTested = true;
@@ -215,20 +262,22 @@ QDeclarativeDebugServer *QDeclarativeDebugServer::instance()
             block = appD->qmljsDebugArgumentsString().contains(QLatin1String("block"));
 
             if (ok) {
-                server = new QDeclarativeDebugServer();
-                QPluginLoader *loader = new QPluginLoader(server);
-                QDeclarativeDebugServerConnection *connection
-                        = QDeclarativeDebugServerPrivate::loadConnectionPlugin(loader, pluginName);
-                if (connection) {
-                    server->d_func()->connection = connection;
-
-                    connection->setServer(server);
-                    connection->setPort(port, block);
-                } else {
-                    qWarning() << QString::fromLatin1(
-                                      "QDeclarativeDebugServer: Ignoring \"-qmljsdebugger=%1\". "
-                                      "Remote debugger plugin has not been found.").arg(
-                                      appD->qmljsDebugArgumentsString());
+                qDeclarativeDebugServer = new QDeclarativeDebugServer();
+
+                QDeclarativeDebugServerThread *thread = new QDeclarativeDebugServerThread;
+
+                qDeclarativeDebugServer = new QDeclarativeDebugServer();
+                qDeclarativeDebugServer->d_func()->thread = thread;
+                qDeclarativeDebugServer->moveToThread(thread);
+                thread->setPluginName(pluginName);
+                thread->setPort(port, block);
+                thread->start();
+
+                if (block) {
+                    QDeclarativeDebugServerPrivate *d = qDeclarativeDebugServer->d_func();
+                    d->messageArrivedMutex.lock();
+                    d->messageArrivedCondition.wait(&d->messageArrivedMutex);
+                    d->messageArrivedMutex.unlock();
                 }
 
             } else {
@@ -248,7 +297,7 @@ QDeclarativeDebugServer *QDeclarativeDebugServer::instance()
 #endif
     }
 
-    return server;
+    return qDeclarativeDebugServer;
 }
 
 QDeclarativeDebugServer::QDeclarativeDebugServer()
@@ -261,6 +310,7 @@ void QDeclarativeDebugServer::receiveMessage(const QByteArray &message)
     Q_D(QDeclarativeDebugServer);
 
     QDataStream in(message);
+
     QString name;
 
     in >> name;
@@ -282,8 +332,9 @@ void QDeclarativeDebugServer::receiveMessage(const QByteArray &message)
 
             d->gotHello = true;
 
-            QHash<QString, QDeclarativeDebugService*>::Iterator iter = d->plugins.begin();
-            for (; iter != d->plugins.end(); ++iter) {
+            QReadLocker(&d->pluginsLock);
+            QHash<QString, QDeclarativeDebugService*>::ConstIterator iter = d->plugins.constBegin();
+            for (; iter != d->plugins.constEnd(); ++iter) {
                 QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::Unavailable;
                 if (d->clientPlugins.contains(iter.key()))
                     newStatus = QDeclarativeDebugService::Enabled;
@@ -292,6 +343,7 @@ void QDeclarativeDebugServer::receiveMessage(const QByteArray &message)
             }
 
             qWarning("QDeclarativeDebugServer: Connection established");
+            d->messageArrivedCondition.wakeAll();
 
         } else if (op == 1) {
 
@@ -299,8 +351,9 @@ void QDeclarativeDebugServer::receiveMessage(const QByteArray &message)
             QStringList oldClientPlugins = d->clientPlugins;
             in >> d->clientPlugins;
 
-            QHash<QString, QDeclarativeDebugService*>::Iterator iter = d->plugins.begin();
-            for (; iter != d->plugins.end(); ++iter) {
+            QReadLocker(&d->pluginsLock);
+            QHash<QString, QDeclarativeDebugService*>::ConstIterator iter = d->plugins.constBegin();
+            for (; iter != d->plugins.constEnd(); ++iter) {
                 const QString pluginName = iter.key();
                 QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::Unavailable;
                 if (d->clientPlugins.contains(pluginName))
@@ -324,18 +377,15 @@ void QDeclarativeDebugServer::receiveMessage(const QByteArray &message)
             QByteArray message;
             in >> message;
 
-            if (d->waitingForMsgFromService == name) {
-                // deliver directly so that it is delivered before waitForMessage is returning.
-                d->_q_deliverMessage(name, message);
-                d->waitingForMsgSucceeded = true;
+            QReadLocker(&d->pluginsLock);
+            QHash<QString, QDeclarativeDebugService *>::Iterator iter = d->plugins.find(name);
+            if (iter == d->plugins.end()) {
+                qWarning() << "QDeclarativeDebugServer: Message received for missing plugin" << name;
             } else {
-                // deliver message in next event loop run.
-                // Fixes the case that the service does start it's own event loop ...,
-                // but the networking code doesn't deliver any new messages because readyRead
-                // hasn't returned.
-                QMetaObject::invokeMethod(this, "_q_deliverMessage", Qt::QueuedConnection,
-                                          Q_ARG(QString, name),
-                                          Q_ARG(QByteArray, message));
+                (*iter)->messageReceived(message);
+
+                if (d->waitingForMessageNames.removeOne(name))
+                    d->messageArrivedCondition.wakeAll();
             }
         } else {
             qWarning("QDeclarativeDebugServer: Invalid hello message");
@@ -344,89 +394,105 @@ void QDeclarativeDebugServer::receiveMessage(const QByteArray &message)
     }
 }
 
-void QDeclarativeDebugServerPrivate::_q_deliverMessage(const QString &serviceName, const QByteArray &message)
+void QDeclarativeDebugServerPrivate::_q_sendMessage(const QByteArray &message)
 {
-    QHash<QString, QDeclarativeDebugService *>::Iterator iter = plugins.find(serviceName);
-    if (iter == plugins.end()) {
-        qWarning() << "QDeclarativeDebugServer: Message received for missing plugin" << serviceName;
-    } else {
-        (*iter)->messageReceived(message);
-    }
+    if (connection)
+        connection->send(message);
 }
 
 QList<QDeclarativeDebugService*> QDeclarativeDebugServer::services() const
 {
     const Q_D(QDeclarativeDebugServer);
+    QReadLocker(&d->pluginsLock);
     return d->plugins.values();
 }
 
 QStringList QDeclarativeDebugServer::serviceNames() const
 {
     const Q_D(QDeclarativeDebugServer);
+    QReadLocker(&d->pluginsLock);
     return d->plugins.keys();
 }
 
 bool QDeclarativeDebugServer::addService(QDeclarativeDebugService *service)
 {
     Q_D(QDeclarativeDebugServer);
-    if (!service || d->plugins.contains(service->name()))
-        return false;
-
-    d->plugins.insert(service->name(), service);
-    d->advertisePlugins();
-
-    QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::Unavailable;
-    if (d->clientPlugins.contains(service->name()))
-        newStatus = QDeclarativeDebugService::Enabled;
-    service->d_func()->status = newStatus;
-    service->statusChanged(newStatus);
+    {
+        QWriteLocker(&d->pluginsLock);
+        if (!service || d->plugins.contains(service->name()))
+            return false;
+        d->plugins.insert(service->name(), service);
+    }
+    {
+        QReadLocker(&d->pluginsLock);
+        d->advertisePlugins();
+        QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::Unavailable;
+        if (d->clientPlugins.contains(service->name()))
+            newStatus = QDeclarativeDebugService::Enabled;
+        service->d_func()->status = newStatus;
+    }
     return true;
 }
 
 bool QDeclarativeDebugServer::removeService(QDeclarativeDebugService *service)
 {
     Q_D(QDeclarativeDebugServer);
-    if (!service || !d->plugins.contains(service->name()))
-        return false;
-
-    d->plugins.remove(service->name());
-    d->advertisePlugins();
+    {
+        QWriteLocker(&d->pluginsLock);
+        if (!service || !d->plugins.contains(service->name()))
+            return false;
+        d->plugins.remove(service->name());
+    }
+    {
+        QReadLocker(&d->pluginsLock);
+        d->advertisePlugins();
+        QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::NotConnected;
+        service->d_func()->server = 0;
+        service->d_func()->status = newStatus;
+        service->statusChanged(newStatus);
+
+        // Last service? Then stop thread & delete instance
+        if (d->plugins.isEmpty()) {
+            d->thread->exit();
+            d->thread->wait();
+            delete d->thread;
+            delete d->connection;
+
+            qDeclarativeDebugServer = 0;
+            deleteLater();
+        }
+    }
 
-    QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::NotConnected;
-    service->d_func()->server = 0;
-    service->d_func()->status = newStatus;
-    service->statusChanged(newStatus);
     return true;
 }
 
 void QDeclarativeDebugServer::sendMessage(QDeclarativeDebugService *service,
                                           const QByteArray &message)
 {
-    Q_D(QDeclarativeDebugServer);
     QByteArray msg;
     {
         QDataStream out(&msg, QIODevice::WriteOnly);
         out << service->name() << message;
     }
-    d->connection->send(msg);
+
+    QMetaObject::invokeMethod(this, "_q_sendMessage", Qt::QueuedConnection, Q_ARG(QByteArray, msg));
 }
 
 bool QDeclarativeDebugServer::waitForMessage(QDeclarativeDebugService *service)
 {
     Q_D(QDeclarativeDebugServer);
+    QReadLocker(&d->pluginsLock);
 
     if (!service
-            || !d->plugins.contains(service->name())
-            || !d->waitingForMsgFromService.isEmpty())
+            || !d->plugins.contains(service->name()))
         return false;
 
-    d->waitingForMsgSucceeded = false;
-    d->waitingForMsgFromService = service->name();
-
+    d->messageArrivedMutex.lock();
+    d->waitingForMessageNames << service->name();
     do {
-        d->connection->waitForMessage();
-    } while (!d->waitingForMsgSucceeded);
-    d->waitingForMsgFromService.clear();
+        d->messageArrivedCondition.wait(&d->messageArrivedMutex);
+    } while (d->waitingForMessageNames.contains(service->name()));
+    d->messageArrivedMutex.unlock();
     return true;
 }
 
index d80633c..1333634 100644 (file)
@@ -80,19 +80,21 @@ public:
     QList<QDeclarativeDebugService*> services() const;
     QStringList serviceNames() const;
 
+
     bool addService(QDeclarativeDebugService *service);
     bool removeService(QDeclarativeDebugService *service);
 
-    void sendMessage(QDeclarativeDebugService *service, const QByteArray &message);
     void receiveMessage(const QByteArray &message);
 
     bool waitForMessage(QDeclarativeDebugService *service);
+    void sendMessage(QDeclarativeDebugService *service, const QByteArray &message);
 
 private:
     friend class QDeclarativeDebugService;
     friend class QDeclarativeDebugServicePrivate;
+    friend class QDeclarativeDebugServerThread;
     QDeclarativeDebugServer();
-    Q_PRIVATE_SLOT(d_func(), void _q_deliverMessage(QString, QByteArray))
+    Q_PRIVATE_SLOT(d_func(), void _q_sendMessage(QByteArray))
 };
 
 QT_END_NAMESPACE
index a1b02ab..ac188b0 100644 (file)
@@ -387,6 +387,11 @@ QDeclarativeEngineDebugService::objectData(QObject *object)
 
 void QDeclarativeEngineDebugService::messageReceived(const QByteArray &message)
 {
+    QMetaObject::invokeMethod(this, "processMessage", Qt::QueuedConnection, Q_ARG(QByteArray, message));
+}
+
+void QDeclarativeEngineDebugService::processMessage(const QByteArray &message)
+{
     QDataStream ds(message);
 
     QByteArray type;
index 3674b83..d3e5c79 100644 (file)
@@ -104,6 +104,7 @@ protected:
     virtual void messageReceived(const QByteArray &);
 
 private Q_SLOTS:
+    void processMessage(const QByteArray &msg);
     void propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value);
 
 private:
index c3f90d2..90ab6ad 100644 (file)
@@ -133,6 +133,11 @@ void QDeclarativeInspectorService::updateStatus()
 
 void QDeclarativeInspectorService::messageReceived(const QByteArray &message)
 {
+    QMetaObject::invokeMethod(this, "processMessage", Qt::QueuedConnection, Q_ARG(QByteArray, message));
+}
+
+void QDeclarativeInspectorService::processMessage(const QByteArray &message)
+{
     if (m_currentInspectorPlugin)
         m_currentInspectorPlugin->clientMessage(message);
 }
index 9aa71ae..6362f5b 100644 (file)
@@ -83,6 +83,9 @@ protected:
     virtual void statusChanged(Status status);
     virtual void messageReceived(const QByteArray &);
 
+private slots:
+    void processMessage(const QByteArray &message);
+
 private:
     void updateStatus();
     void loadInspectorPlugins();
index 2f7ca34..f708f59 100644 (file)
@@ -45,7 +45,6 @@
 #include <private/qv8engine_p.h>
 #include <private/qdeclarativeengine_p.h>
 
-#include <QtCore/QEventLoop>
 #include <QtCore/QHash>
 #include <QtCore/QFileInfo>
 
@@ -72,12 +71,9 @@ void DebugMessageHandler(const v8::Debug::Message& message)
                                   message.GetJSON()));
 
     QV8DebugService *service = QV8DebugService::instance();
-    service->debugMessageHandler(response);
+    service->debugMessageHandler(response, message.WillStartRunning());
 
-    if ((event == v8::Break || event == v8::Exception) &&
-            !message.WillStartRunning()) {
-        service->executionStopped();
-    } else if (event == v8::AfterCompile) {
+    if (event == v8::AfterCompile) {
         service->appendSourcePath(response);
     } //TODO::v8::Exception
 }
@@ -87,32 +83,41 @@ class QV8DebugServicePrivate : public QDeclarativeDebugServicePrivate
 public:
     QV8DebugServicePrivate()
         :initialized(false)
+        , scheduleBreak(false)
+        , debuggerThreadIsolate(0)
+        , debuggerThreadEngine(0)
+        , isRunning(true)
     {
-        //Create a new isolate
-        isolate = v8::Isolate::New();
-
-        //Enter the isolate and initialize
-        v8::Isolate::Scope i_scope(isolate);
+        //Create an isolate & engine in GUI thread
+        guiThreadIsolate = v8::Isolate::New();
+        v8::Isolate::Scope i_scope(guiThreadIsolate);
         v8::V8::Initialize();
-
-        //Create an instance in the new isolate
-        engine = new QJSEngine();
+        guiThreadEngine = new QJSEngine();
     }
 
     ~QV8DebugServicePrivate()
     {
-        delete engine;
-        isolate->Dispose();
+        delete debuggerThreadEngine;
+        if (debuggerThreadIsolate)
+            debuggerThreadIsolate->Dispose();
+        delete guiThreadEngine;
+        if (guiThreadIsolate)
+            guiThreadIsolate->Dispose();
     }
 
     void sendDebugMessage(const QString &message);
     static QByteArray packMessage(const QString &message);
 
     bool initialized;
-    QJSEngine *engine;
-    v8::Isolate *isolate;
+    bool scheduleBreak;
+
+    v8::Isolate *debuggerThreadIsolate;
+    QJSEngine *debuggerThreadEngine;
+    v8::Isolate *guiThreadIsolate;
+    QJSEngine *guiThreadEngine;
+
     QList<QDeclarativeEngine *> engines;
-    QEventLoop loop;
+    bool isRunning;
     QHash<QString, QString> sourcePath;
     QHash<QString, QString> requestCache;
     QHash<int, SignalHandlerData> handlersList;
@@ -166,20 +171,18 @@ void QV8DebugService::removeEngine(QDeclarativeEngine *engine)
     d->engines.removeAll(engine);
 }
 
-void QV8DebugService::debugMessageHandler(const QString &message)
-{
-    sendMessage(QV8DebugServicePrivate::packMessage(message));
-}
-
-void QV8DebugService::executionStopped()
+void QV8DebugService::debugMessageHandler(const QString &message, bool willStartRunning)
 {
     Q_D(QV8DebugService);
+    d->isRunning = willStartRunning;
 
-    if (!d->loop.isRunning()) {
-        d->loop.exec(QEventLoop::ExcludeUserInputEvents);
-    }
+    if (d->scheduleBreak)
+        scheduledDebugBreak();
+
+    sendMessage(QV8DebugServicePrivate::packMessage(message));
 }
 
+
 void QV8DebugService::appendSourcePath(const QString &message)
 {
     Q_D(QV8DebugService);
@@ -189,8 +192,8 @@ void QV8DebugService::appendSourcePath(const QString &message)
     This will ensure that the debug message handler does not
     receive any messages related to this operation */
     {
-        v8::Isolate::Scope i_scope(d->isolate);
-        QJSValue parser = d->engine->evaluate(QLatin1String("JSON.parse"));
+        v8::Isolate::Scope scope(d->guiThreadIsolate);
+        QJSValue parser = d->guiThreadEngine->evaluate(QLatin1String("JSON.parse"));
         QJSValue out = parser.call(QJSValue(), QJSValueList() << QJSValue(message));
         msgMap = out.toVariant().toMap();
     }
@@ -220,18 +223,26 @@ void QV8DebugService::signalEmitted(const QString &signal)
     //is no need for additional check.
     Q_D(QV8DebugService);
 
-    bool debugBreak = false;
     //Parse just the name and remove the class info
     //Normalize to Lower case.
     QString signalName = signal.left(signal.indexOf(QLatin1String("("))).toLower();
     foreach (const SignalHandlerData &data, d->handlersList) {
         if (data.functionName == signalName
                 && data.enabled) {
-            debugBreak = true;
+            d->scheduleBreak = true;
         }
     }
-    if (debugBreak)
+    if (d->scheduleBreak)
+        scheduledDebugBreak();
+}
+
+void QV8DebugService::scheduledDebugBreak()
+{
+    Q_D(QV8DebugService);
+    if (d->scheduleBreak) {
         v8::Debug::DebugBreak();
+        d->scheduleBreak = false;
+    }
 }
 
 void QV8DebugService::messageReceived(const QByteArray &message)
@@ -243,6 +254,16 @@ void QV8DebugService::messageReceived(const QByteArray &message)
     ds >> command;
 
     if (command == "V8DEBUG") {
+
+        if (!d->debuggerThreadEngine) {
+            //Create an isolate & engine in debugger thread
+            d->debuggerThreadIsolate = v8::Isolate::New();
+            v8::Isolate::Scope i_scope(d->debuggerThreadIsolate);
+            v8::V8::Initialize();
+            d->debuggerThreadEngine = new QJSEngine();
+        }
+
+
         QString request;
         {
             QByteArray requestArray;
@@ -251,12 +272,9 @@ void QV8DebugService::messageReceived(const QByteArray &message)
         }
 
         QVariantMap reqMap;
-        /* Parse the byte string in a separate isolate
-        This will ensure that the debug message handler does not
-        receive any messages related to this operation */
         {
-            v8::Isolate::Scope i_scope(d->isolate);
-            QJSValue parser = d->engine->evaluate(QLatin1String("JSON.parse"));
+            v8::Isolate::Scope i_scope(d->debuggerThreadIsolate);
+            QJSValue parser = d->debuggerThreadEngine->evaluate(QLatin1String("JSON.parse"));
             QJSValue out = parser.call(QJSValue(), QJSValueList() << QJSValue(request));
             reqMap = out.toVariant().toMap();
         }
@@ -276,9 +294,9 @@ void QV8DebugService::messageReceived(const QByteArray &message)
             //   "success"     : true
             // }
             {
-                v8::Isolate::Scope i_scope(d->isolate);
                 const QString obj(QLatin1String("{}"));
-                QJSValue parser = d->engine->evaluate(QLatin1String("JSON.parse"));
+                v8::Isolate::Scope i_scope(d->debuggerThreadIsolate);
+                QJSValue parser = d->debuggerThreadEngine->evaluate(QLatin1String("JSON.parse"));
                 QJSValue jsonVal = parser.call(QJSValue(), QJSValueList() << obj);
                 jsonVal.setProperty(QLatin1String("type"), QJSValue(QLatin1String("response")));
 
@@ -286,16 +304,14 @@ void QV8DebugService::messageReceived(const QByteArray &message)
                 jsonVal.setProperty(QLatin1String("request_seq"), QJSValue(sequence));
                 jsonVal.setProperty(QLatin1String("command"), QJSValue(debugCommand));
                 jsonVal.setProperty(QLatin1String("success"), QJSValue(true));
-                jsonVal.setProperty(QLatin1String("running"), QJSValue(!d->loop.isRunning()));
+                jsonVal.setProperty(QLatin1String("running"), QJSValue(d->isRunning));
 
-                QJSValue stringify = d->engine->evaluate(QLatin1String("JSON.stringify"));
+                QJSValue stringify = d->debuggerThreadEngine->evaluate(QLatin1String("JSON.stringify"));
                 QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal);
-                debugMessageHandler(json.toString());
-
+                sendMessage(QV8DebugServicePrivate::packMessage(json.toString()));
             }
 
         } else if (debugCommand == QLatin1String("interrupt")) {
-            v8::Debug::DebugBreak();
             //Prepare the response string
             //Create a json message using v8 debugging protocol
             //and send it to client
@@ -307,9 +323,9 @@ void QV8DebugService::messageReceived(const QByteArray &message)
             //   "success"     : true
             // }
             {
-                v8::Isolate::Scope i_scope(d->isolate);
                 const QString obj(QLatin1String("{}"));
-                QJSValue parser = d->engine->evaluate(QLatin1String("JSON.parse"));
+                v8::Isolate::Scope i_scope(d->debuggerThreadIsolate);
+                QJSValue parser = d->debuggerThreadEngine->evaluate(QLatin1String("JSON.parse"));
                 QJSValue jsonVal = parser.call(QJSValue(), QJSValueList() << obj);
                 jsonVal.setProperty(QLatin1String("type"), QJSValue(QLatin1String("response")));
 
@@ -317,14 +333,15 @@ void QV8DebugService::messageReceived(const QByteArray &message)
                 jsonVal.setProperty(QLatin1String("request_seq"), QJSValue(sequence));
                 jsonVal.setProperty(QLatin1String("command"), QJSValue(debugCommand));
                 jsonVal.setProperty(QLatin1String("success"), QJSValue(true));
-                jsonVal.setProperty(QLatin1String("running"), QJSValue(!d->loop.isRunning()));
+                jsonVal.setProperty(QLatin1String("running"), QJSValue(d->isRunning));
 
-                QJSValue stringify = d->engine->evaluate(QLatin1String("JSON.stringify"));
+                QJSValue stringify = d->debuggerThreadEngine->evaluate(QLatin1String("JSON.stringify"));
                 QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal);
-                debugMessageHandler(json.toString());
-
+                sendMessage(QV8DebugServicePrivate::packMessage(json.toString()));
             }
-
+            // break has to be executed in gui thread
+            d->scheduleBreak = true;
+            QMetaObject::invokeMethod(this, "scheduledDebugBreak", Qt::QueuedConnection);
         } else {
             bool forwardRequestToV8 = true;
 
@@ -363,9 +380,9 @@ void QV8DebugService::messageReceived(const QByteArray &message)
                     //   "success"     : true
                     // }
                     {
-                        v8::Isolate::Scope i_scope(d->isolate);
                         const QString obj(QLatin1String("{}"));
-                        QJSValue parser = d->engine->evaluate(QLatin1String("JSON.parse"));
+                        v8::Isolate::Scope i_scope(d->debuggerThreadIsolate);
+                        QJSValue parser = d->debuggerThreadEngine->evaluate(QLatin1String("JSON.parse"));
                         QJSValue jsonVal = parser.call(QJSValue(), QJSValueList() << obj);
                         jsonVal.setProperty(QLatin1String("type"), QJSValue(QLatin1String("response")));
 
@@ -398,12 +415,11 @@ void QV8DebugService::messageReceived(const QByteArray &message)
                         }
 
 
-                        jsonVal.setProperty(QLatin1String("running"), QJSValue(!d->loop.isRunning()));
+                        jsonVal.setProperty(QLatin1String("running"), QJSValue(d->isRunning));
 
-                        QJSValue stringify = d->engine->evaluate(QLatin1String("JSON.stringify"));
+                        QJSValue stringify = d->debuggerThreadEngine->evaluate(QLatin1String("JSON.stringify"));
                         QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal);
-                        debugMessageHandler(json.toString());
-
+                        sendMessage(QV8DebugServicePrivate::packMessage(json.toString()));
                     }
                 }
             } else if (debugCommand == QLatin1String("changebreakpoint")) {
@@ -440,9 +456,6 @@ void QV8DebugService::messageReceived(const QByteArray &message)
 
 void QV8DebugServicePrivate::sendDebugMessage(const QString &message)
 {
-    if (loop.isRunning())
-        loop.exit();
-
     v8::Debug::SendCommand(message.utf16(), message.size());
 }
 
index d8101ae..9785bb9 100644 (file)
@@ -76,13 +76,15 @@ public:
     void addEngine(QDeclarativeEngine *);
     void removeEngine(QDeclarativeEngine *);
 
-    void debugMessageHandler(const QString &message);
-    void executionStopped();
+    void debugMessageHandler(const QString &message, bool willStartRunning);
 
     void appendSourcePath(const QString &message);
 
     void signalEmitted(const QString &signal);
 
+private slots:
+    void scheduledDebugBreak();
+
 protected:
     void messageReceived(const QByteArray &);
 
index 48d2d13..807fe83 100644 (file)
@@ -84,6 +84,7 @@ class QV8ProfilerServicePrivate : public QDeclarativeDebugServicePrivate
 public:
     QV8ProfilerServicePrivate()
         :initialized(false)
+        , isolate(0)
     {
     }
 
@@ -96,6 +97,7 @@ public:
 
     bool initialized;
     QList<QDeclarativeEngine *> engines;
+    v8::Isolate *isolate;
 };
 
 QV8ProfilerService::QV8ProfilerService(QObject *parent)
@@ -146,6 +148,14 @@ void QV8ProfilerService::messageReceived(const QByteArray &message)
     QByteArray title;
     ds >> command >> option;
 
+    if (!d->isolate) {
+        d->isolate = v8::Isolate::New();
+        v8::Isolate::Scope scope(d->isolate);
+        v8::V8::Initialize();
+    }
+
+    v8::Isolate::Scope scope(d->isolate);
+
     if (command == "V8PROFILER") {
         ds >>  title;
         if (option == "start") {
index 82e2559..46a711a 100644 (file)
@@ -80,11 +80,16 @@ void tst_QDeclarativeDebugClient::initTestCase()
     QDeclarativeDebugTestClient client("tst_QDeclarativeDebugClient::handshake()", m_conn);
     QDeclarativeDebugTestService service("tst_QDeclarativeDebugClient::handshake()");
 
-    m_conn->connectToHost("127.0.0.1", PORT);
-
     QTest::ignoreMessage(QtWarningMsg, "QDeclarativeDebugServer: Connection established");
-    bool ok = m_conn->waitForConnected();
-    QVERIFY(ok);
+    for (int i = 0; i < 50; ++i) {
+        // try for 5 seconds ...
+        m_conn->connectToHost("127.0.0.1", PORT);
+        if (m_conn->waitForConnected())
+            break;
+        QTest::qSleep(100);
+    }
+
+    QVERIFY(m_conn->isConnected());
 
     QTRY_VERIFY(QDeclarativeDebugService::hasDebuggingClient());
     QTRY_COMPARE(client.status(), QDeclarativeDebugClient::Enabled);
index 8f829a6..6063c1f 100644 (file)
@@ -53,6 +53,8 @@
 #include "../../shared/util.h"
 #include "../shared/debugutil_p.h"
 
+#define PORT 13769
+#define STR_PORT "13769"
 
 class tst_QDeclarativeDebugService : public QObject
 {
@@ -73,15 +75,22 @@ private slots:
 
 void tst_QDeclarativeDebugService::initTestCase()
 {
-    QTest::ignoreMessage(QtWarningMsg, "QDeclarativeDebugServer: Waiting for connection on port 13769...");
+    const QString waitingMsg = QString("QDeclarativeDebugServer: Waiting for connection on port %1...").arg(PORT);
+    QTest::ignoreMessage(QtWarningMsg, waitingMsg.toAscii().constData());
     new QDeclarativeEngine(this);
 
     m_conn = new QDeclarativeDebugConnection(this);
-    m_conn->connectToHost("127.0.0.1", 13769);
+
 
     QTest::ignoreMessage(QtWarningMsg, "QDeclarativeDebugServer: Connection established");
-    bool ok = m_conn->waitForConnected();
-    QVERIFY(ok);
+    for (int i = 0; i < 50; ++i) {
+        // try for 5 seconds ...
+        m_conn->connectToHost("127.0.0.1", PORT);
+        if (m_conn->waitForConnected())
+            break;
+        QTest::qSleep(100);
+    }
+    QVERIFY(m_conn->isConnected());
 
     QTRY_VERIFY(QDeclarativeDebugService::hasDebuggingClient());
 }
@@ -187,7 +196,7 @@ int main(int argc, char *argv[])
     char **_argv = new char*[_argc];
     for (int i = 0; i < argc; ++i)
         _argv[i] = argv[i];
-    char arg[] = "-qmljsdebugger=port:13769";
+    char arg[] = "-qmljsdebugger=port:" STR_PORT;
     _argv[_argc - 1] = arg;
 
     QGuiApplication app(_argc, _argv);