From 43e20d57011bbddd3beec773f6a8efb1269a8ead Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Wed, 24 Aug 2011 13:51:07 +0200 Subject: [PATCH] Debugger: Move server into it's own thread 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 --- .../debugger/qdeclarativedebugserver.cpp | 228 +++++++++++++-------- .../debugger/qdeclarativedebugserver_p.h | 6 +- .../debugger/qdeclarativeenginedebugservice.cpp | 5 + .../debugger/qdeclarativeenginedebugservice_p.h | 1 + .../debugger/qdeclarativeinspectorservice.cpp | 5 + .../debugger/qdeclarativeinspectorservice_p.h | 3 + src/declarative/debugger/qv8debugservice.cpp | 135 ++++++------ src/declarative/debugger/qv8debugservice_p.h | 6 +- src/declarative/debugger/qv8profilerservice.cpp | 10 + .../tst_qdeclarativedebugclient.cpp | 13 +- .../tst_qdeclarativedebugservice.cpp | 19 +- 11 files changed, 276 insertions(+), 155 deletions(-) diff --git a/src/declarative/debugger/qdeclarativedebugserver.cpp b/src/declarative/debugger/qdeclarativedebugserver.cpp index 5c86c45..b8963d2 100644 --- a/src/declarative/debugger/qdeclarativedebugserver.cpp +++ b/src/declarative/debugger/qdeclarativedebugserver.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -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 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(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(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::Iterator iter = d->plugins.begin(); - for (; iter != d->plugins.end(); ++iter) { + QReadLocker(&d->pluginsLock); + QHash::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::Iterator iter = d->plugins.begin(); - for (; iter != d->plugins.end(); ++iter) { + QReadLocker(&d->pluginsLock); + QHash::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::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::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 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; } diff --git a/src/declarative/debugger/qdeclarativedebugserver_p.h b/src/declarative/debugger/qdeclarativedebugserver_p.h index d80633c..1333634 100644 --- a/src/declarative/debugger/qdeclarativedebugserver_p.h +++ b/src/declarative/debugger/qdeclarativedebugserver_p.h @@ -80,19 +80,21 @@ public: QList 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 diff --git a/src/declarative/debugger/qdeclarativeenginedebugservice.cpp b/src/declarative/debugger/qdeclarativeenginedebugservice.cpp index a1b02ab..ac188b0 100644 --- a/src/declarative/debugger/qdeclarativeenginedebugservice.cpp +++ b/src/declarative/debugger/qdeclarativeenginedebugservice.cpp @@ -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; diff --git a/src/declarative/debugger/qdeclarativeenginedebugservice_p.h b/src/declarative/debugger/qdeclarativeenginedebugservice_p.h index 3674b83..d3e5c79 100644 --- a/src/declarative/debugger/qdeclarativeenginedebugservice_p.h +++ b/src/declarative/debugger/qdeclarativeenginedebugservice_p.h @@ -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: diff --git a/src/declarative/debugger/qdeclarativeinspectorservice.cpp b/src/declarative/debugger/qdeclarativeinspectorservice.cpp index c3f90d2..90ab6ad 100644 --- a/src/declarative/debugger/qdeclarativeinspectorservice.cpp +++ b/src/declarative/debugger/qdeclarativeinspectorservice.cpp @@ -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); } diff --git a/src/declarative/debugger/qdeclarativeinspectorservice_p.h b/src/declarative/debugger/qdeclarativeinspectorservice_p.h index 9aa71ae..6362f5b 100644 --- a/src/declarative/debugger/qdeclarativeinspectorservice_p.h +++ b/src/declarative/debugger/qdeclarativeinspectorservice_p.h @@ -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(); diff --git a/src/declarative/debugger/qv8debugservice.cpp b/src/declarative/debugger/qv8debugservice.cpp index 2f7ca34..f708f59 100644 --- a/src/declarative/debugger/qv8debugservice.cpp +++ b/src/declarative/debugger/qv8debugservice.cpp @@ -45,7 +45,6 @@ #include #include -#include #include #include @@ -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 engines; - QEventLoop loop; + bool isRunning; QHash sourcePath; QHash requestCache; QHash 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()); } diff --git a/src/declarative/debugger/qv8debugservice_p.h b/src/declarative/debugger/qv8debugservice_p.h index d8101ae..9785bb9 100644 --- a/src/declarative/debugger/qv8debugservice_p.h +++ b/src/declarative/debugger/qv8debugservice_p.h @@ -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 &); diff --git a/src/declarative/debugger/qv8profilerservice.cpp b/src/declarative/debugger/qv8profilerservice.cpp index 48d2d13..807fe83 100644 --- a/src/declarative/debugger/qv8profilerservice.cpp +++ b/src/declarative/debugger/qv8profilerservice.cpp @@ -84,6 +84,7 @@ class QV8ProfilerServicePrivate : public QDeclarativeDebugServicePrivate public: QV8ProfilerServicePrivate() :initialized(false) + , isolate(0) { } @@ -96,6 +97,7 @@ public: bool initialized; QList 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") { diff --git a/tests/auto/declarative/debugger/qdeclarativedebugclient/tst_qdeclarativedebugclient.cpp b/tests/auto/declarative/debugger/qdeclarativedebugclient/tst_qdeclarativedebugclient.cpp index 82e2559..46a711a 100644 --- a/tests/auto/declarative/debugger/qdeclarativedebugclient/tst_qdeclarativedebugclient.cpp +++ b/tests/auto/declarative/debugger/qdeclarativedebugclient/tst_qdeclarativedebugclient.cpp @@ -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); diff --git a/tests/auto/declarative/debugger/qdeclarativedebugservice/tst_qdeclarativedebugservice.cpp b/tests/auto/declarative/debugger/qdeclarativedebugservice/tst_qdeclarativedebugservice.cpp index 8f829a6..6063c1f 100644 --- a/tests/auto/declarative/debugger/qdeclarativedebugservice/tst_qdeclarativedebugservice.cpp +++ b/tests/auto/declarative/debugger/qdeclarativedebugservice/tst_qdeclarativedebugservice.cpp @@ -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); -- 2.7.4