Merge pull request #10354 from zzmp/fix/tablet-deadlock

Fix deadlock from accessing an initializing tablet
This commit is contained in:
Seth Alves 2017-05-04 09:52:50 -07:00 committed by GitHub
commit 19327ef5a7
2 changed files with 47 additions and 32 deletions

View file

@ -21,6 +21,8 @@
#include <InfoView.h> #include <InfoView.h>
#include "SoundEffect.h" #include "SoundEffect.h"
const QString SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system";
const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";
QScriptValue tabletToScriptValue(QScriptEngine* engine, TabletProxy* const &in) { QScriptValue tabletToScriptValue(QScriptEngine* engine, TabletProxy* const &in) {
return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects);
@ -35,11 +37,11 @@ TabletScriptingInterface::TabletScriptingInterface() {
} }
QObject* TabletScriptingInterface::getSystemToolbarProxy() { QObject* TabletScriptingInterface::getSystemToolbarProxy() {
const QString SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system";
Qt::ConnectionType connectionType = Qt::AutoConnection; Qt::ConnectionType connectionType = Qt::AutoConnection;
if (QThread::currentThread() != _toolbarScriptingInterface->thread()) { if (QThread::currentThread() != _toolbarScriptingInterface->thread()) {
connectionType = Qt::BlockingQueuedConnection; connectionType = Qt::BlockingQueuedConnection;
} }
QObject* toolbarProxy = nullptr; QObject* toolbarProxy = nullptr;
bool hasResult = QMetaObject::invokeMethod(_toolbarScriptingInterface, "getToolbar", connectionType, Q_RETURN_ARG(QObject*, toolbarProxy), Q_ARG(QString, SYSTEM_TOOLBAR)); bool hasResult = QMetaObject::invokeMethod(_toolbarScriptingInterface, "getToolbar", connectionType, Q_RETURN_ARG(QObject*, toolbarProxy), Q_ARG(QString, SYSTEM_TOOLBAR));
if (hasResult) { if (hasResult) {
@ -51,28 +53,38 @@ QObject* TabletScriptingInterface::getSystemToolbarProxy() {
} }
TabletProxy* TabletScriptingInterface::getTablet(const QString& tabletId) { TabletProxy* TabletScriptingInterface::getTablet(const QString& tabletId) {
TabletProxy* tabletProxy = nullptr;
{
// the only thing guarded should be map mutation
// this avoids a deadlock with the Main thread
// from Qt::BlockingQueuedEvent invocations later in the call-tree
std::lock_guard<std::mutex> guard(_mapMutex);
std::lock_guard<std::mutex> guard(_mutex); auto iter = _tabletProxies.find(tabletId);
if (iter != _tabletProxies.end()) {
// look up tabletId in the map. // tablet already exists
auto iter = _tabletProxies.find(tabletId); return iter->second;
if (iter != _tabletProxies.end()) { } else {
// tablet already exists, just return it. // tablet must be created
return iter->second; tabletProxy = new TabletProxy(this, tabletId);
} else { _tabletProxies[tabletId] = tabletProxy;
// allocate a new tablet, add it to the map then return it. }
auto tabletProxy = new TabletProxy(tabletId);
tabletProxy->setParent(this);
_tabletProxies[tabletId] = tabletProxy;
tabletProxy->setToolbarMode(_toolbarMode);
return tabletProxy;
} }
assert(tabletProxy);
// initialize new tablet
tabletProxy->setToolbarMode(_toolbarMode);
return tabletProxy;
} }
void TabletScriptingInterface::setToolbarMode(bool toolbarMode) { void TabletScriptingInterface::setToolbarMode(bool toolbarMode) {
std::lock_guard<std::mutex> guard(_mutex); {
// the only thing guarded should be _toolbarMode
_toolbarMode = toolbarMode; // this avoids a deadlock with the Main thread
// from Qt::BlockingQueuedEvent invocations later in the call-tree
std::lock_guard<std::mutex> guard(_mapMutex);
_toolbarMode = toolbarMode;
}
for (auto& iter : _tabletProxies) { for (auto& iter : _tabletProxies) {
iter.second->setToolbarMode(toolbarMode); iter.second->setToolbarMode(toolbarMode);
@ -89,8 +101,9 @@ void TabletScriptingInterface::setQmlTabletRoot(QString tabletId, QQuickItem* qm
} }
QQuickWindow* TabletScriptingInterface::getTabletWindow() { QQuickWindow* TabletScriptingInterface::getTabletWindow() {
TabletProxy* tablet = qobject_cast<TabletProxy*>(getTablet("com.highfidelity.interface.tablet.system")); TabletProxy* tablet = qobject_cast<TabletProxy*>(getTablet(SYSTEM_TABLET));
QObject* qmlSurface = tablet->getTabletSurface(); QObject* qmlSurface = tablet->getTabletSurface();
OffscreenQmlSurface* surface = dynamic_cast<OffscreenQmlSurface*>(qmlSurface); OffscreenQmlSurface* surface = dynamic_cast<OffscreenQmlSurface*>(qmlSurface);
if (!surface) { if (!surface) {
@ -156,7 +169,7 @@ void TabletScriptingInterface::processTabletEvents(QObject* object, const QKeyEv
void TabletScriptingInterface::processEvent(const QKeyEvent* event) { void TabletScriptingInterface::processEvent(const QKeyEvent* event) {
TabletProxy* tablet = qobject_cast<TabletProxy*>(getTablet("com.highfidelity.interface.tablet.system")); TabletProxy* tablet = qobject_cast<TabletProxy*>(getTablet(SYSTEM_TABLET));
QObject* qmlTablet = tablet->getQmlTablet(); QObject* qmlTablet = tablet->getQmlTablet();
QObject* qmlMenu = tablet->getQmlMenu(); QObject* qmlMenu = tablet->getQmlMenu();
@ -185,10 +198,12 @@ class TabletRootWindow : public QmlWindowClass {
virtual QString qmlSource() const override { return "hifi/tablet/WindowRoot.qml"; } virtual QString qmlSource() const override { return "hifi/tablet/WindowRoot.qml"; }
}; };
TabletProxy::TabletProxy(QString name) : _name(name) { TabletProxy::TabletProxy(QObject* parent, QString name) : QObject(parent), _name(name) {
} }
void TabletProxy::setToolbarMode(bool toolbarMode) { void TabletProxy::setToolbarMode(bool toolbarMode) {
std::lock_guard<std::mutex> guard(_tabletMutex);
if (toolbarMode == _toolbarMode) { if (toolbarMode == _toolbarMode) {
return; return;
} }
@ -288,7 +303,7 @@ bool TabletProxy::isPathLoaded(QVariant path) {
return path.toString() == _currentPathLoaded.toString(); return path.toString() == _currentPathLoaded.toString();
} }
void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscreenSurface) { void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscreenSurface) {
std::lock_guard<std::mutex> guard(_mutex); std::lock_guard<std::mutex> guard(_tabletMutex);
_qmlOffscreenSurface = qmlOffscreenSurface; _qmlOffscreenSurface = qmlOffscreenSurface;
_qmlTabletRoot = qmlTabletRoot; _qmlTabletRoot = qmlTabletRoot;
if (_qmlTabletRoot && _qmlOffscreenSurface) { if (_qmlTabletRoot && _qmlOffscreenSurface) {
@ -519,7 +534,7 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS
QObject* TabletProxy::addButton(const QVariant& properties) { QObject* TabletProxy::addButton(const QVariant& properties) {
auto tabletButtonProxy = QSharedPointer<TabletButtonProxy>(new TabletButtonProxy(properties.toMap())); auto tabletButtonProxy = QSharedPointer<TabletButtonProxy>(new TabletButtonProxy(properties.toMap()));
std::lock_guard<std::mutex> guard(_mutex); std::lock_guard<std::mutex> guard(_tabletMutex);
_tabletButtonProxies.push_back(tabletButtonProxy); _tabletButtonProxies.push_back(tabletButtonProxy);
if (!_toolbarMode && _qmlTabletRoot) { if (!_toolbarMode && _qmlTabletRoot) {
auto tablet = getQmlTablet(); auto tablet = getQmlTablet();
@ -555,7 +570,7 @@ bool TabletProxy::onHomeScreen() {
} }
void TabletProxy::removeButton(QObject* tabletButtonProxy) { void TabletProxy::removeButton(QObject* tabletButtonProxy) {
std::lock_guard<std::mutex> guard(_mutex); std::lock_guard<std::mutex> guard(_tabletMutex);
auto tablet = getQmlTablet(); auto tablet = getQmlTablet();
if (!tablet) { if (!tablet) {
@ -743,12 +758,12 @@ TabletButtonProxy::TabletButtonProxy(const QVariantMap& properties) :
} }
void TabletButtonProxy::setQmlButton(QQuickItem* qmlButton) { void TabletButtonProxy::setQmlButton(QQuickItem* qmlButton) {
std::lock_guard<std::mutex> guard(_mutex); std::lock_guard<std::mutex> guard(_buttonMutex);
_qmlButton = qmlButton; _qmlButton = qmlButton;
} }
void TabletButtonProxy::setToolbarButtonProxy(QObject* toolbarButtonProxy) { void TabletButtonProxy::setToolbarButtonProxy(QObject* toolbarButtonProxy) {
std::lock_guard<std::mutex> guard(_mutex); std::lock_guard<std::mutex> guard(_buttonMutex);
_toolbarButtonProxy = toolbarButtonProxy; _toolbarButtonProxy = toolbarButtonProxy;
if (_toolbarButtonProxy) { if (_toolbarButtonProxy) {
QObject::connect(_toolbarButtonProxy, SIGNAL(clicked()), this, SLOT(clickedSlot())); QObject::connect(_toolbarButtonProxy, SIGNAL(clicked()), this, SLOT(clickedSlot()));
@ -756,12 +771,12 @@ void TabletButtonProxy::setToolbarButtonProxy(QObject* toolbarButtonProxy) {
} }
QVariantMap TabletButtonProxy::getProperties() const { QVariantMap TabletButtonProxy::getProperties() const {
std::lock_guard<std::mutex> guard(_mutex); std::lock_guard<std::mutex> guard(_buttonMutex);
return _properties; return _properties;
} }
void TabletButtonProxy::editProperties(QVariantMap properties) { void TabletButtonProxy::editProperties(QVariantMap properties) {
std::lock_guard<std::mutex> guard(_mutex); std::lock_guard<std::mutex> guard(_buttonMutex);
QVariantMap::const_iterator iter = properties.constBegin(); QVariantMap::const_iterator iter = properties.constBegin();
while (iter != properties.constEnd()) { while (iter != properties.constEnd()) {

View file

@ -71,7 +71,7 @@ private:
void processTabletEvents(QObject* object, const QKeyEvent* event); void processTabletEvents(QObject* object, const QKeyEvent* event);
protected: protected:
std::mutex _mutex; std::mutex _mapMutex;
std::map<QString, TabletProxy*> _tabletProxies; std::map<QString, TabletProxy*> _tabletProxies;
QObject* _toolbarScriptingInterface { nullptr }; QObject* _toolbarScriptingInterface { nullptr };
bool _toolbarMode { false }; bool _toolbarMode { false };
@ -90,7 +90,7 @@ class TabletProxy : public QObject {
Q_PROPERTY(bool landscape READ getLandscape WRITE setLandscape) Q_PROPERTY(bool landscape READ getLandscape WRITE setLandscape)
Q_PROPERTY(bool tabletShown MEMBER _tabletShown NOTIFY tabletShownChanged) Q_PROPERTY(bool tabletShown MEMBER _tabletShown NOTIFY tabletShownChanged)
public: public:
TabletProxy(QString name); TabletProxy(QObject* parent, QString name);
void setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscreenSurface); void setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscreenSurface);
@ -248,7 +248,7 @@ protected:
QVariant _initialPath { "" }; QVariant _initialPath { "" };
QVariant _currentPathLoaded { "" }; QVariant _currentPathLoaded { "" };
QString _name; QString _name;
std::mutex _mutex; std::mutex _tabletMutex;
std::vector<QSharedPointer<TabletButtonProxy>> _tabletButtonProxies; std::vector<QSharedPointer<TabletButtonProxy>> _tabletButtonProxies;
QQuickItem* _qmlTabletRoot { nullptr }; QQuickItem* _qmlTabletRoot { nullptr };
QObject* _qmlOffscreenSurface { nullptr }; QObject* _qmlOffscreenSurface { nullptr };
@ -309,7 +309,7 @@ signals:
protected: protected:
QUuid _uuid; QUuid _uuid;
int _stableOrder; int _stableOrder;
mutable std::mutex _mutex; mutable std::mutex _buttonMutex;
QQuickItem* _qmlButton { nullptr }; QQuickItem* _qmlButton { nullptr };
QObject* _toolbarButtonProxy { nullptr }; QObject* _toolbarButtonProxy { nullptr };
QVariantMap _properties; QVariantMap _properties;