aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@theqtcompany.com>2014-10-16 16:57:08 +0200
committerJani Heikkinen <jani.heikkinen@theqtcompany.com>2014-11-13 19:58:28 +0100
commitdd08a22a4e8d7120341a1227e227de3f0628dd2f (patch)
tree6130ebf44600d5ec579752f617d9189875e7bb0b
parentdd6e14a7df6e40ff8b9ffe22fe6124630a15bda0 (diff)
Invalidate font caches when switching between threadsv5.4.0-rc1
The font caches can only be used from a single thread at a time. QFontEngineFT for instance, uses a global static thread storage which is accessed on releasing and creating engines, and this causes a crash if the font engine is created on one thread and released on another. We use the updatePolish() function to make sure the caches are empty before entering updatePaintNode(), and then we invalidate the cache again after updatePaintNode() is done. [ChangeLog][Text] Fixed uncommon crash in text nodes. Change-Id: I01dbc2ed58aeebd03d77a157c700330334bdb385 Task-number: QTBUG-38800 Reviewed-by: Konstantin Ritt <ritt.ks@gmail.com>
-rw-r--r--src/quick/items/qquicktext.cpp45
-rw-r--r--src/quick/items/qquicktext_p.h1
-rw-r--r--src/quick/items/qquicktext_p_p.h1
-rw-r--r--src/quick/items/qquicktextedit.cpp31
-rw-r--r--src/quick/items/qquicktextedit_p.h2
-rw-r--r--src/quick/items/qquicktextinput.cpp27
-rw-r--r--src/quick/items/qquicktextinput_p.h4
7 files changed, 108 insertions, 3 deletions
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index bfa2fccd67..87255f4bd9 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -79,6 +79,7 @@ QQuickTextPrivate::QQuickTextPrivate()
, requireImplicitSize(false), implicitWidthValid(false), implicitHeightValid(false)
, truncated(false), hAlignImplicit(true), rightToLeftText(false)
, layoutTextElided(false), textHasChanged(true), needToUpdateLayout(false), formatModifiesFontSize(false)
+ , polishSize(false)
{
implicitAntialiasing = true;
}
@@ -356,6 +357,8 @@ void QQuickTextPrivate::updateLayout()
textHasChanged = true;
updateLayout();
}
+
+ q->polish();
}
void QQuickText::imageDownloadFinished()
@@ -2248,13 +2251,22 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data
node->addImage(QRectF(img->pos.x() + dx, img->pos.y() + dy, pix->width(), pix->height()), pix->image());
}
}
+
+ // The font caches have now been initialized on the render thread, so they have to be
+ // invalidated before we can use them from the main thread again.
+ invalidateFontCaches();
+
return node;
}
void QQuickText::updatePolish()
{
Q_D(QQuickText);
- d->updateSize();
+ if (d->polishSize) {
+ d->updateSize();
+ d->polishSize = false;
+ }
+ invalidateFontCaches();
}
/*!
@@ -2381,6 +2393,7 @@ void QQuickText::setFontSizeMode(FontSizeMode mode)
if (d->fontSizeMode() == mode)
return;
+ d->polishSize = true;
polish();
d->extra.value().fontSizeMode = mode;
@@ -2409,8 +2422,10 @@ void QQuickText::setMinimumPixelSize(int size)
if (d->minimumPixelSize() == size)
return;
- if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid()))
+ if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid())) {
+ d->polishSize = true;
polish();
+ }
d->extra.value().minimumPixelSize = size;
emit minimumPixelSizeChanged();
}
@@ -2437,8 +2452,10 @@ void QQuickText::setMinimumPointSize(int size)
if (d->minimumPointSize() == size)
return;
- if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid()))
+ if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid())) {
+ d->polishSize = true;
polish();
+ }
d->extra.value().minimumPointSize = size;
emit minimumPointSizeChanged();
}
@@ -2699,4 +2716,26 @@ QString QQuickText::linkAt(qreal x, qreal y) const
return d->anchorAt(QPointF(x, y));
}
+/*!
+ * \internal
+ *
+ * Invalidates font caches owned by the text objects owned by the element
+ * to work around the fact that text objects cannot be used from multiple threads.
+ */
+void QQuickText::invalidateFontCaches()
+{
+ Q_D(QQuickText);
+
+ if (d->richText && d->extra->doc != 0) {
+ QTextBlock block;
+ for (block = d->extra->doc->firstBlock(); block.isValid(); block = block.next()) {
+ if (block.layout() != 0 && block.layout()->engine() != 0)
+ block.layout()->engine()->resetFontEngineCache();
+ }
+ } else {
+ if (d->layout.engine() != 0)
+ d->layout.engine()->resetFontEngineCache();
+ }
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h
index c9f64d3fab..c3fa9e6935 100644
--- a/src/quick/items/qquicktext_p.h
+++ b/src/quick/items/qquicktext_p.h
@@ -245,6 +245,7 @@ protected:
void hoverEnterEvent(QHoverEvent *event);
void hoverMoveEvent(QHoverEvent *event);
void hoverLeaveEvent(QHoverEvent *event);
+ void invalidateFontCaches();
private Q_SLOTS:
void q_imagesLoaded();
diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h
index 022c2ac9d5..d645ce5ed5 100644
--- a/src/quick/items/qquicktext_p_p.h
+++ b/src/quick/items/qquicktext_p_p.h
@@ -151,6 +151,7 @@ public:
bool textHasChanged:1;
bool needToUpdateLayout:1;
bool formatModifiesFontSize:1;
+ bool polishSize:1; // Workaround for problem with polish called after updateSize (QTBUG-42636)
static const QChar elideChar;
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index aca7150ca2..b950f96277 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -1739,6 +1739,7 @@ void QQuickTextEdit::triggerPreprocess()
Q_D(QQuickTextEdit);
if (d->updateType == QQuickTextEditPrivate::UpdateNone)
d->updateType = QQuickTextEditPrivate::UpdateOnlyPreprocess;
+ polish();
update();
}
@@ -1758,6 +1759,25 @@ static inline void updateNodeTransform(QQuickTextNode* node, const QPointF &topL
node->setMatrix(transformMatrix);
}
+/*!
+ * \internal
+ *
+ * Invalidates font caches owned by the text objects owned by the element
+ * to work around the fact that text objects cannot be used from multiple threads.
+ */
+void QQuickTextEdit::invalidateFontCaches()
+{
+ Q_D(QQuickTextEdit);
+ if (d->document == 0)
+ return;
+
+ QTextBlock block;
+ for (block = d->document->firstBlock(); block.isValid(); block = block.next()) {
+ if (block.layout() != 0 && block.layout()->engine() != 0)
+ block.layout()->engine()->resetFontEngineCache();
+ }
+}
+
QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
{
Q_UNUSED(updatePaintNodeData);
@@ -1911,9 +1931,16 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
rootNode->resetCursorNode(cursor);
}
+ invalidateFontCaches();
+
return rootNode;
}
+void QQuickTextEdit::updatePolish()
+{
+ invalidateFontCaches();
+}
+
/*!
\qmlproperty bool QtQuick::TextEdit::canPaste
@@ -2079,6 +2106,7 @@ void QQuickTextEdit::q_contentsChange(int pos, int charsRemoved, int charsAdded)
markDirtyNodesForRange(pos, editRange, delta);
+ polish();
if (isComponentComplete()) {
d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
update();
@@ -2106,6 +2134,7 @@ void QQuickTextEdit::updateSelection()
// No need for node updates when we go from an empty selection to another empty selection
if (d->control->textCursor().hasSelection() || d->hadSelection) {
markDirtyNodesForRange(qMin(d->lastSelectionStart, d->control->textCursor().selectionStart()), qMax(d->control->textCursor().selectionEnd(), d->lastSelectionEnd), 0);
+ polish();
if (isComponentComplete()) {
d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
update();
@@ -2246,6 +2275,7 @@ void QQuickTextEdit::updateWholeDocument()
node->setDirty();
}
+ polish();
if (isComponentComplete()) {
d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
update();
@@ -2260,6 +2290,7 @@ void QQuickTextEdit::invalidateBlock(const QTextBlock &block)
void QQuickTextEdit::updateCursor()
{
Q_D(QQuickTextEdit);
+ polish();
if (isComponentComplete()) {
d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
update();
diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h
index 54e6dd229a..5d1c026c5c 100644
--- a/src/quick/items/qquicktextedit_p.h
+++ b/src/quick/items/qquicktextedit_p.h
@@ -330,6 +330,7 @@ private Q_SLOTS:
private:
void markDirtyNodesForRange(int start, int end, int charDelta);
void updateTotalLines();
+ void invalidateFontCaches();
protected:
virtual void geometryChanged(const QRectF &newGeometry,
@@ -354,6 +355,7 @@ protected:
void inputMethodEvent(QInputMethodEvent *e);
#endif
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData);
+ void updatePolish();
friend class QQuickTextUtil;
friend class QQuickTextDocument;
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index 1f03fb21e2..a9c60273d2 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -56,6 +56,8 @@
#include "qquickaccessibleattached_p.h"
#endif
+#include <QtGui/private/qtextengine_p.h>
+
QT_BEGIN_NAMESPACE
DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
@@ -362,6 +364,7 @@ void QQuickTextInput::setColor(const QColor &c)
d->color = c;
d->textLayoutDirty = true;
d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
+ polish();
update();
emit colorChanged();
}
@@ -389,6 +392,7 @@ void QQuickTextInput::setSelectionColor(const QColor &color)
if (d->hasSelectedText()) {
d->textLayoutDirty = true;
d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
+ polish();
update();
}
emit selectionColorChanged();
@@ -414,6 +418,7 @@ void QQuickTextInput::setSelectedTextColor(const QColor &color)
if (d->hasSelectedText()) {
d->textLayoutDirty = true;
d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
+ polish();
update();
}
emit selectedTextColorChanged();
@@ -723,6 +728,7 @@ void QQuickTextInput::setCursorVisible(bool on)
if (!d->cursorItem) {
d->setCursorBlinkPeriod(on ? qApp->styleHints()->cursorFlashTime() : 0);
d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
+ polish();
update();
}
emit cursorVisibleChanged(d->cursorVisible);
@@ -1830,9 +1836,23 @@ void QQuickTextInput::triggerPreprocess()
Q_D(QQuickTextInput);
if (d->updateType == QQuickTextInputPrivate::UpdateNone)
d->updateType = QQuickTextInputPrivate::UpdateOnlyPreprocess;
+ polish();
update();
}
+void QQuickTextInput::updatePolish()
+{
+ invalidateFontCaches();
+}
+
+void QQuickTextInput::invalidateFontCaches()
+{
+ Q_D(QQuickTextInput);
+
+ if (d->m_textLayout.engine() != 0)
+ d->m_textLayout.engine()->resetFontEngineCache();
+}
+
QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
{
Q_UNUSED(data);
@@ -1891,6 +1911,8 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
d->textLayoutDirty = false;
}
+ invalidateFontCaches();
+
return node;
}
@@ -2651,6 +2673,7 @@ void QQuickTextInput::updateCursorRectangle(bool scroll)
d->updateVerticalScroll();
}
d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
+ polish();
update();
emit cursorRectangleChanged();
if (d->cursorItem) {
@@ -2668,6 +2691,7 @@ void QQuickTextInput::selectionChanged()
Q_D(QQuickTextInput);
d->textLayoutDirty = true; //TODO: Only update rect in selection
d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
+ polish();
update();
emit selectedTextChanged();
@@ -2879,6 +2903,7 @@ void QQuickTextInputPrivate::updateLayout()
contentSize = QSizeF(width, height);
updateType = UpdatePaintNode;
+ q->polish();
q->update();
if (!requireImplicitWidth && !q->widthValid())
@@ -4167,6 +4192,7 @@ void QQuickTextInputPrivate::setCursorBlinkPeriod(int msec)
m_blinkTimer = 0;
if (m_blinkStatus == 1) {
updateType = UpdatePaintNode;
+ q->polish();
q->update();
}
}
@@ -4179,6 +4205,7 @@ void QQuickTextInput::timerEvent(QTimerEvent *event)
if (event->timerId() == d->m_blinkTimer) {
d->m_blinkStatus = !d->m_blinkStatus;
d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
+ polish();
update();
} else if (event->timerId() == d->m_passwordEchoTimer.timerId()) {
d->m_passwordEchoTimer.stop();
diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h
index 4b94530587..2386fc5642 100644
--- a/src/quick/items/qquicktextinput_p.h
+++ b/src/quick/items/qquicktextinput_p.h
@@ -314,6 +314,9 @@ Q_SIGNALS:
#endif
void renderTypeChanged();
+private:
+ void invalidateFontCaches();
+
protected:
virtual void geometryChanged(const QRectF &newGeometry,
const QRectF &oldGeometry);
@@ -332,6 +335,7 @@ protected:
void focusInEvent(QFocusEvent *event);
void timerEvent(QTimerEvent *event);
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data);
+ void updatePolish();
public Q_SLOTS:
void selectAll();