Move all resource requests to the same thread

This commit is contained in:
Atlante45 2016-02-25 16:44:27 -08:00
parent c1b3858193
commit 2c1762526d
12 changed files with 106 additions and 71 deletions

View file

@ -55,14 +55,6 @@ Agent::Agent(ReceivedMessage& message) :
{ {
DependencyManager::get<EntityScriptingInterface>()->setPacketSender(&_entityEditSender); DependencyManager::get<EntityScriptingInterface>()->setPacketSender(&_entityEditSender);
auto assetClient = DependencyManager::set<AssetClient>();
QThread* assetThread = new QThread;
assetThread->setObjectName("Asset Thread");
assetClient->moveToThread(assetThread);
connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init);
assetThread->start();
DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>(); DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>();
DependencyManager::set<ResourceCacheSharedItems>(); DependencyManager::set<ResourceCacheSharedItems>();
@ -81,6 +73,8 @@ Agent::Agent(ReceivedMessage& message) :
{ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase }, { PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
this, "handleOctreePacket"); this, "handleOctreePacket");
packetReceiver.registerListener(PacketType::Jurisdiction, this, "handleJurisdictionPacket"); packetReceiver.registerListener(PacketType::Jurisdiction, this, "handleJurisdictionPacket");
ResourceManager::init();
} }
void Agent::handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) { void Agent::handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
@ -471,12 +465,8 @@ void Agent::aboutToFinish() {
// our entity tree is going to go away so tell that to the EntityScriptingInterface // our entity tree is going to go away so tell that to the EntityScriptingInterface
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr); DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr);
// cleanup the AssetClient thread
QThread* assetThread = DependencyManager::get<AssetClient>()->thread();
DependencyManager::destroy<AssetClient>();
assetThread->quit();
assetThread->wait();
// cleanup the AudioInjectorManager (and any still running injectors) // cleanup the AudioInjectorManager (and any still running injectors)
DependencyManager::destroy<AudioInjectorManager>(); DependencyManager::destroy<AudioInjectorManager>();
ResourceManager::cleanup();
} }

View file

@ -370,7 +370,6 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set<AutoUpdater>(); DependencyManager::set<AutoUpdater>();
DependencyManager::set<PathUtils>(); DependencyManager::set<PathUtils>();
DependencyManager::set<InterfaceActionFactory>(); DependencyManager::set<InterfaceActionFactory>();
DependencyManager::set<AssetClient>();
DependencyManager::set<AudioInjectorManager>(); DependencyManager::set<AudioInjectorManager>();
DependencyManager::set<MessagesClient>(); DependencyManager::set<MessagesClient>();
DependencyManager::set<UserInputMapper>(); DependencyManager::set<UserInputMapper>();
@ -528,13 +527,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
audioThread->start(); audioThread->start();
// Setup AssetClient ResourceManager::init();
auto assetClient = DependencyManager::get<AssetClient>();
QThread* assetThread = new QThread;
assetThread->setObjectName("Asset Thread");
assetClient->moveToThread(assetThread);
connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init);
assetThread->start();
// Setup MessagesClient // Setup MessagesClient
auto messagesClient = DependencyManager::get<MessagesClient>(); auto messagesClient = DependencyManager::get<MessagesClient>();
@ -644,13 +637,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
connect(&identityPacketTimer, &QTimer::timeout, getMyAvatar(), &MyAvatar::sendIdentityPacket); connect(&identityPacketTimer, &QTimer::timeout, getMyAvatar(), &MyAvatar::sendIdentityPacket);
identityPacketTimer.start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); identityPacketTimer.start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS);
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkDiskCache* cache = new QNetworkDiskCache();
cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE);
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache");
networkAccessManager.setCache(cache);
ResourceCache::setRequestLimit(3); ResourceCache::setRequestLimit(3);
_glWidget = new GLCanvas(); _glWidget = new GLCanvas();
@ -1129,12 +1115,6 @@ Application::~Application() {
DependencyManager::destroy<ScriptCache>(); DependencyManager::destroy<ScriptCache>();
DependencyManager::destroy<SoundCache>(); DependencyManager::destroy<SoundCache>();
// cleanup the AssetClient thread
QThread* assetThread = DependencyManager::get<AssetClient>()->thread();
DependencyManager::destroy<AssetClient>();
assetThread->quit();
assetThread->wait();
QThread* nodeThread = DependencyManager::get<NodeList>()->thread(); QThread* nodeThread = DependencyManager::get<NodeList>()->thread();
// remove the NodeList from the DependencyManager // remove the NodeList from the DependencyManager
@ -1150,6 +1130,8 @@ Application::~Application() {
ConnexionClient::getInstance().destroy(); ConnexionClient::getInstance().destroy();
#endif #endif
ResourceManager::cleanup();
qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages
} }

View file

@ -47,12 +47,10 @@ AssetClient::AssetClient() {
} }
void AssetClient::init() { void AssetClient::init() {
if (QThread::currentThread() != thread()) { Q_ASSERT(QThread::currentThread() == thread());
QMetaObject::invokeMethod(this, "init", Qt::BlockingQueuedConnection);
}
// Setup disk cache if not already // Setup disk cache if not already
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); auto& networkAccessManager = NetworkAccessManager::getInstance();
if (!networkAccessManager.cache()) { if (!networkAccessManager.cache()) {
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
cachePath = !cachePath.isEmpty() ? cachePath : "interfaceCache"; cachePath = !cachePath.isEmpty() ? cachePath : "interfaceCache";
@ -61,7 +59,7 @@ void AssetClient::init() {
cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE); cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE);
cache->setCacheDirectory(cachePath); cache->setCacheDirectory(cachePath);
networkAccessManager.setCache(cache); networkAccessManager.setCache(cache);
qCDebug(asset_client) << "AssetClient disk cache setup at" << cachePath qDebug() << "ResourceManager disk cache setup at" << cachePath
<< "(size:" << MAXIMUM_CACHE_SIZE / BYTES_PER_GIGABYTES << "GB)"; << "(size:" << MAXIMUM_CACHE_SIZE / BYTES_PER_GIGABYTES << "GB)";
} }
} }

View file

@ -44,12 +44,13 @@ class AssetClient : public QObject, public Dependency {
public: public:
AssetClient(); AssetClient();
Q_INVOKABLE void init();
Q_INVOKABLE AssetRequest* createRequest(const QString& hash, const QString& extension); Q_INVOKABLE AssetRequest* createRequest(const QString& hash, const QString& extension);
Q_INVOKABLE AssetUpload* createUpload(const QString& filename); Q_INVOKABLE AssetUpload* createUpload(const QString& filename);
Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data, const QString& extension); Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data, const QString& extension);
public slots:
void init();
private slots: private slots:
void handleAssetGetInfoReply(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode); void handleAssetGetInfoReply(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleAssetGetReply(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode); void handleAssetGetReply(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);

View file

@ -20,7 +20,7 @@
class AssetResourceRequest : public ResourceRequest { class AssetResourceRequest : public ResourceRequest {
Q_OBJECT Q_OBJECT
public: public:
AssetResourceRequest(QObject* parent, const QUrl& url) : ResourceRequest(parent, url) { } AssetResourceRequest(const QUrl& url) : ResourceRequest(url) { }
~AssetResourceRequest(); ~AssetResourceRequest();
protected: protected:

View file

@ -19,7 +19,7 @@
class FileResourceRequest : public ResourceRequest { class FileResourceRequest : public ResourceRequest {
Q_OBJECT Q_OBJECT
public: public:
FileResourceRequest(QObject* parent, const QUrl& url) : ResourceRequest(parent, url) { } FileResourceRequest(const QUrl& url) : ResourceRequest(url) { }
protected: protected:
virtual void doSend() override; virtual void doSend() override;

View file

@ -28,6 +28,25 @@ HTTPResourceRequest::~HTTPResourceRequest() {
} }
} }
void HTTPResourceRequest::setupTimer() {
Q_ASSERT(!_sendTimer);
static const int TIMEOUT_MS = 10000;
_sendTimer = new QTimer();
connect(this, &QObject::destroyed, _sendTimer, &QTimer::deleteLater);
connect(_sendTimer, &QTimer::timeout, this, &HTTPResourceRequest::onTimeout);
_sendTimer->setSingleShot(true);
_sendTimer->start(TIMEOUT_MS);
}
void HTTPResourceRequest::cleanupTimer() {
Q_ASSERT(_sendTimer);
_sendTimer->disconnect(this);
_sendTimer->deleteLater();
_sendTimer = nullptr;
}
void HTTPResourceRequest::doSend() { void HTTPResourceRequest::doSend() {
QNetworkRequest networkRequest(_url); QNetworkRequest networkRequest(_url);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
@ -42,18 +61,15 @@ void HTTPResourceRequest::doSend() {
connect(_reply, &QNetworkReply::finished, this, &HTTPResourceRequest::onRequestFinished); connect(_reply, &QNetworkReply::finished, this, &HTTPResourceRequest::onRequestFinished);
connect(_reply, &QNetworkReply::downloadProgress, this, &HTTPResourceRequest::onDownloadProgress); connect(_reply, &QNetworkReply::downloadProgress, this, &HTTPResourceRequest::onDownloadProgress);
connect(&_sendTimer, &QTimer::timeout, this, &HTTPResourceRequest::onTimeout);
static const int TIMEOUT_MS = 10000; setupTimer();
_sendTimer.setSingleShot(true);
_sendTimer.start(TIMEOUT_MS);
} }
void HTTPResourceRequest::onRequestFinished() { void HTTPResourceRequest::onRequestFinished() {
Q_ASSERT(_state == InProgress); Q_ASSERT(_state == InProgress);
Q_ASSERT(_reply); Q_ASSERT(_reply);
_sendTimer.stop(); cleanupTimer();
switch(_reply->error()) { switch(_reply->error()) {
case QNetworkReply::NoError: case QNetworkReply::NoError:
@ -80,7 +96,7 @@ void HTTPResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesT
Q_ASSERT(_state == InProgress); Q_ASSERT(_state == InProgress);
// We've received data, so reset the timer // We've received data, so reset the timer
_sendTimer.start(); _sendTimer->start();
emit progress(bytesReceived, bytesTotal); emit progress(bytesReceived, bytesTotal);
} }
@ -92,6 +108,8 @@ void HTTPResourceRequest::onTimeout() {
_reply->deleteLater(); _reply->deleteLater();
_reply = nullptr; _reply = nullptr;
cleanupTimer();
_result = Timeout; _result = Timeout;
_state = Finished; _state = Finished;
emit finished(); emit finished();

View file

@ -21,7 +21,7 @@
class HTTPResourceRequest : public ResourceRequest { class HTTPResourceRequest : public ResourceRequest {
Q_OBJECT Q_OBJECT
public: public:
HTTPResourceRequest(QObject* parent, const QUrl& url) : ResourceRequest(parent, url) { } HTTPResourceRequest(const QUrl& url) : ResourceRequest(url) { }
~HTTPResourceRequest(); ~HTTPResourceRequest();
protected: protected:
@ -33,7 +33,10 @@ private slots:
void onRequestFinished(); void onRequestFinished();
private: private:
QTimer _sendTimer; void setupTimer();
void cleanupTimer();
QTimer* _sendTimer { nullptr };
QNetworkReply* _reply { nullptr }; QNetworkReply* _reply { nullptr };
}; };

View file

@ -11,12 +11,20 @@
#include "ResourceManager.h" #include "ResourceManager.h"
#include "AssetResourceRequest.h" #include <QNetworkDiskCache>
#include "FileResourceRequest.h" #include <QStandardPaths>
#include "HTTPResourceRequest.h" #include <QThread>
#include <SharedUtil.h> #include <SharedUtil.h>
#include "AssetResourceRequest.h"
#include "FileResourceRequest.h"
#include "HTTPResourceRequest.h"
#include "NetworkAccessManager.h"
QThread ResourceManager::_thread;
ResourceManager::PrefixMap ResourceManager::_prefixMap; ResourceManager::PrefixMap ResourceManager::_prefixMap;
QMutex ResourceManager::_prefixMapLock; QMutex ResourceManager::_prefixMapLock;
@ -67,18 +75,41 @@ QUrl ResourceManager::normalizeURL(const QUrl& originalUrl) {
return url; return url;
} }
void ResourceManager::init() {
_thread.setObjectName("Ressource Manager Thread");
auto assetClient = DependencyManager::set<AssetClient>();
assetClient->moveToThread(&_thread);
QObject::connect(&_thread, &QThread::started, assetClient.data(), &AssetClient::init);
_thread.start();
}
void ResourceManager::cleanup() {
// cleanup the AssetClient thread
DependencyManager::destroy<AssetClient>();
_thread.quit();
_thread.wait();
}
ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const QUrl& url) { ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const QUrl& url) {
auto normalizedURL = normalizeURL(url); auto normalizedURL = normalizeURL(url);
auto scheme = normalizedURL.scheme(); auto scheme = normalizedURL.scheme();
ResourceRequest* request = nullptr;
if (scheme == URL_SCHEME_FILE) { if (scheme == URL_SCHEME_FILE) {
return new FileResourceRequest(parent, normalizedURL); request = new FileResourceRequest(normalizedURL);
} else if (scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || scheme == URL_SCHEME_FTP) { } else if (scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || scheme == URL_SCHEME_FTP) {
return new HTTPResourceRequest(parent, normalizedURL); request = new HTTPResourceRequest(normalizedURL);
} else if (scheme == URL_SCHEME_ATP) { } else if (scheme == URL_SCHEME_ATP) {
return new AssetResourceRequest(parent, normalizedURL); request = new AssetResourceRequest(normalizedURL);
} } else {
qDebug() << "Unknown scheme (" << scheme << ") for URL: " << url.url(); qDebug() << "Unknown scheme (" << scheme << ") for URL: " << url.url();
return nullptr; return nullptr;
} }
Q_ASSERT(request);
request->moveToThread(&_thread);
return request;
}

View file

@ -29,8 +29,15 @@ public:
static void setUrlPrefixOverride(const QString& prefix, const QString& replacement); static void setUrlPrefixOverride(const QString& prefix, const QString& replacement);
static QString normalizeURL(const QString& urlString); static QString normalizeURL(const QString& urlString);
static QUrl normalizeURL(const QUrl& url); static QUrl normalizeURL(const QUrl& url);
static ResourceRequest* createResourceRequest(QObject* parent, const QUrl& url); static ResourceRequest* createResourceRequest(QObject* parent, const QUrl& url);
static void init();
static void cleanup();
private: private:
static QThread _thread;
using PrefixMap = std::map<QString, QString>; using PrefixMap = std::map<QString, QString>;
static PrefixMap _prefixMap; static PrefixMap _prefixMap;

View file

@ -11,12 +11,15 @@
#include "ResourceRequest.h" #include "ResourceRequest.h"
ResourceRequest::ResourceRequest(QObject* parent, const QUrl& url) : #include <QtCore/QThread>
QObject(parent),
_url(url) { ResourceRequest::ResourceRequest(const QUrl& url) : _url(url) { }
}
void ResourceRequest::send() { void ResourceRequest::send() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "send", Qt::QueuedConnection);
return;
}
Q_ASSERT(_state == NotStarted); Q_ASSERT(_state == NotStarted);
_state = InProgress; _state = InProgress;

View file

@ -20,7 +20,7 @@
class ResourceRequest : public QObject { class ResourceRequest : public QObject {
Q_OBJECT Q_OBJECT
public: public:
ResourceRequest(QObject* parent, const QUrl& url); ResourceRequest(const QUrl& url);
enum State { enum State {
NotStarted = 0, NotStarted = 0,
@ -38,7 +38,6 @@ public:
NotFound NotFound
}; };
void send();
QByteArray getData() { return _data; } QByteArray getData() { return _data; }
State getState() const { return _state; } State getState() const { return _state; }
Result getResult() const { return _result; } Result getResult() const { return _result; }
@ -47,8 +46,11 @@ public:
void setCacheEnabled(bool value) { _cacheEnabled = value; } void setCacheEnabled(bool value) { _cacheEnabled = value; }
public slots:
void send();
signals: signals:
void progress(uint64_t bytesReceived, uint64_t bytesTotal); void progress(qint64 bytesReceived, qint64 bytesTotal);
void finished(); void finished();
protected: protected: