Merge pull request #4187 from huffman/parallel-script-loading

Parallel script loading
This commit is contained in:
Brad Hefta-Gaub 2015-01-29 13:26:37 -08:00
commit f5e092c0d2
5 changed files with 191 additions and 54 deletions

View file

@ -12,34 +12,37 @@
// //
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
Script.include("libraries/stringHelpers.js");
Script.include("libraries/dataviewHelpers.js");
Script.include("libraries/httpMultiPart.js");
Script.include("libraries/modelUploader.js");
Script.include("libraries/toolBars.js");
Script.include("libraries/progressDialog.js");
Script.include("libraries/entitySelectionTool.js"); Script.include([
"http://public.highfidelity.io/scripts/libraries/stringHelpers.js",
"http://public.highfidelity.io/scripts/libraries/dataviewHelpers.js",
"http://public.highfidelity.io/scripts/libraries/httpMultiPart.js",
"http://public.highfidelity.io/scripts/libraries/modelUploader.js",
"http://public.highfidelity.io/scripts/libraries/toolBars.js",
"http://public.highfidelity.io/scripts/libraries/progressDialog.js",
"http://public.highfidelity.io/scripts/libraries/entitySelectionTool.js",
"http://public.highfidelity.io/scripts/libraries/ModelImporter.js",
"http://public.highfidelity.io/scripts/libraries/ExportMenu.js",
"http://public.highfidelity.io/scripts/libraries/ToolTip.js",
"http://public.highfidelity.io/scripts/libraries/entityPropertyDialogBox.js",
"http://public.highfidelity.io/scripts/libraries/entityCameraTool.js",
"http://public.highfidelity.io/scripts/libraries/gridTool.js",
"http://public.highfidelity.io/scripts/libraries/entityList.js",
]);
var selectionDisplay = SelectionDisplay; var selectionDisplay = SelectionDisplay;
var selectionManager = SelectionManager; var selectionManager = SelectionManager;
Script.include("libraries/ModelImporter.js");
var modelImporter = new ModelImporter(); var modelImporter = new ModelImporter();
Script.include("libraries/ExportMenu.js");
Script.include("libraries/ToolTip.js");
Script.include("libraries/entityPropertyDialogBox.js");
var entityPropertyDialogBox = EntityPropertyDialogBox; var entityPropertyDialogBox = EntityPropertyDialogBox;
Script.include("libraries/entityCameraTool.js");
var cameraManager = new CameraManager(); var cameraManager = new CameraManager();
Script.include("libraries/gridTool.js");
var grid = Grid(); var grid = Grid();
gridTool = GridTool({ horizontalGrid: grid }); gridTool = GridTool({ horizontalGrid: grid });
Script.include("libraries/entityList.js");
var entityListTool = EntityListTool(); var entityListTool = EntityListTool();
var hasShownPropertiesTool = false; var hasShownPropertiesTool = false;

View file

@ -0,0 +1,79 @@
//
// BatchLoader.cpp
// libraries/script-engine/src
//
// Created by Ryan Huffman on 01/22/15
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QFile>
#include "BatchLoader.h"
#include <NetworkAccessManager.h>
BatchLoader::BatchLoader(const QList<QUrl>& urls)
: QObject(),
_started(false),
_finished(false),
_urls(urls.toSet()),
_data() {
}
void BatchLoader::start() {
if (_started) {
return;
}
_started = true;
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
for (QUrl url : _urls) {
if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "ftp") {
QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url));
qDebug() << "Downloading file at" << url;
connect(reply, &QNetworkReply::finished, [=]() {
if (reply->error()) {
_data.insert(url, QString());
} else {
_data.insert(url, reply->readAll());
}
reply->deleteLater();
checkFinished();
});
// If we end up being destroyed before the reply finishes, clean it up
connect(this, &QObject::destroyed, reply, &QObject::deleteLater);
} else {
#ifdef _WIN32
QString fileName = url.toString();
#else
QString fileName = url.toLocalFile();
#endif
qDebug() << "Reading file at " << fileName;
QFile scriptFile(fileName);
if (scriptFile.open(QFile::ReadOnly | QFile::Text)) {
QTextStream in(&scriptFile);
_data.insert(url, in.readAll());
} else {
_data.insert(url, QString());
}
}
}
checkFinished();
}
void BatchLoader::checkFinished() {
if (!_finished && _urls.size() == _data.size()) {
_finished = true;
emit finished(_data);
}
}

View file

@ -0,0 +1,42 @@
//
// BatchLoader.h
// libraries/script-engine/src
//
// Created by Ryan Huffman on 01/22/15
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_BatchLoader_h
#define hifi_BatchLoader_h
#include <QList>
#include <QMap>
#include <QObject>
#include <QSet>
#include <QString>
#include <QUrl>
class BatchLoader : public QObject {
Q_OBJECT
public:
BatchLoader(const QList<QUrl>& urls) ;
void start();
bool isFinished() const { return _finished; };
signals:
void finished(const QMap<QUrl, QString>& data);
private:
void checkFinished();
bool _started;
bool _finished;
QSet<QUrl> _urls;
QMap<QUrl, QString> _data;
};
#endif // hifi_BatchLoader_h

View file

@ -31,6 +31,7 @@
#include "AnimationObject.h" #include "AnimationObject.h"
#include "ArrayBufferViewClass.h" #include "ArrayBufferViewClass.h"
#include "BatchLoader.h"
#include "DataViewClass.h" #include "DataViewClass.h"
#include "EventTypes.h" #include "EventTypes.h"
#include "MenuItemProperties.h" #include "MenuItemProperties.h"
@ -111,7 +112,7 @@ void ScriptEngine::setIsAvatar(bool isAvatar) {
_avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); _avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS);
_avatarBillboardTimer->start(AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS); _avatarBillboardTimer->start(AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS);
} }
if (!_isAvatar) { if (!_isAvatar) {
delete _avatarIdentityTimer; delete _avatarIdentityTimer;
_avatarIdentityTimer = NULL; _avatarIdentityTimer = NULL;
@ -304,7 +305,7 @@ QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileN
QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber); QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber);
if (hasUncaughtException()) { if (hasUncaughtException()) {
int line = uncaughtExceptionLineNumber(); int line = uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at (" << _fileNameString << ") line" << line << ": " << result.toString(); qDebug() << "Uncaught exception at (" << _fileNameString << " : " << fileName << ") line" << line << ": " << result.toString();
} }
emit evaluationFinished(result, hasUncaughtException()); emit evaluationFinished(result, hasUncaughtException());
clearExceptions(); clearExceptions();
@ -595,46 +596,57 @@ void ScriptEngine::print(const QString& message) {
emit printedMessage(message); emit printedMessage(message);
} }
void ScriptEngine::include(const QString& includeFile) { /**
QUrl url = resolvePath(includeFile); * If a callback is specified, the included files will be loaded asynchronously and the callback will be called
QString includeContents; * when all of the files have finished loading.
* If no callback is specified, the included files will be loaded synchronously and will block execution until
* all of the files have finished loading.
*/
void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) {
QList<QUrl> urls;
for (QString file : includeFiles) {
urls.append(resolvePath(file));
}
if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "ftp") { BatchLoader* loader = new BatchLoader(urls);
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url)); auto evaluateScripts = [=](const QMap<QUrl, QString>& data) {
qDebug() << "Downloading included script at" << includeFile; for (QUrl url : urls) {
QEventLoop loop; QString contents = data[url];
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); if (contents.isNull()) {
loop.exec(); qDebug() << "Error loading file: " << url;
includeContents = reply->readAll(); } else {
reply->deleteLater(); QScriptValue result = evaluate(contents, url.toString());
} else { }
#ifdef _WIN32
QString fileName = url.toString();
#else
QString fileName = url.toLocalFile();
#endif
QFile scriptFile(fileName);
if (scriptFile.open(QFile::ReadOnly | QFile::Text)) {
qDebug() << "Including file:" << fileName;
QTextStream in(&scriptFile);
includeContents = in.readAll();
} else {
qDebug() << "ERROR Including file:" << fileName;
emit errorMessage("ERROR Including file:" + fileName);
} }
}
QScriptValue result = evaluate(includeContents); if (callback.isFunction()) {
if (hasUncaughtException()) { QScriptValue(callback).call();
int line = uncaughtExceptionLineNumber(); }
qDebug() << "Uncaught exception at (" << includeFile << ") line" << line << ":" << result.toString();
emit errorMessage("Uncaught exception at (" + includeFile + ") line" + QString::number(line) + ":" + result.toString()); loader->deleteLater();
clearExceptions(); };
connect(loader, &BatchLoader::finished, this, evaluateScripts);
// If we are destroyed before the loader completes, make sure to clean it up
connect(this, &QObject::destroyed, loader, &QObject::deleteLater);
loader->start();
if (!callback.isFunction() && !loader->isFinished()) {
QEventLoop loop;
QObject::connect(loader, &BatchLoader::finished, &loop, &QEventLoop::quit);
loop.exec();
} }
} }
void ScriptEngine::include(const QString& includeFile, QScriptValue callback) {
QStringList urls;
urls.append(includeFile);
include(urls, callback);
}
void ScriptEngine::load(const QString& loadFile) { void ScriptEngine::load(const QString& loadFile) {
QUrl url = resolvePath(loadFile); QUrl url = resolvePath(loadFile);
emit loadScript(url.toString(), false); emit loadScript(url.toString(), false);

View file

@ -96,7 +96,8 @@ public slots:
QObject* setTimeout(const QScriptValue& function, int timeoutMS); QObject* setTimeout(const QScriptValue& function, int timeoutMS);
void clearInterval(QObject* timer) { stopTimer(reinterpret_cast<QTimer*>(timer)); } void clearInterval(QObject* timer) { stopTimer(reinterpret_cast<QTimer*>(timer)); }
void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast<QTimer*>(timer)); } void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast<QTimer*>(timer)); }
void include(const QString& includeFile); void include(const QStringList& includeFiles, QScriptValue callback = QScriptValue());
void include(const QString& includeFile, QScriptValue callback = QScriptValue());
void load(const QString& loadfile); void load(const QString& loadfile);
void print(const QString& message); void print(const QString& message);
QUrl resolvePath(const QString& path) const; QUrl resolvePath(const QString& path) const;