Skip to content

Commit 832815c

Browse files
committed
Fix for bug 7234184 F/TextLayoutCache: Failed to put an entry...
This bug was triggered by user code concurrently mutating the character array while calling into a drawText method in another thread. When the value of the array changed, it caused inconsistent state, leading to assert failures. This is arguably bad behavior by the user code, but it shouldn't cause a native crash. The fix is to do a defensive copy of the text into the key, so the value is guaranteed to remain constant throughout the text layout process. The change is mostly deletion of code, because there was an optimization to try to avoid such a copy. That optimization was not actually effective, however, because the indexOfKey() operation in the KeyedVector underlying the TextLayoutCache did the copy anyway. Thus, even though this change looks like it's introducing a copy where there wasn't one before, the actual performance impact should be nil. Note that the ability to handle a mutating argument is now part of the contract for TextLayoutEngine::getValue(), and is now documented. That contract may change, as the result of future optimization. Also, care was taken to only use the value after the copy. Other performance issues with TextLayoutCache are tracked in bug 7271109. Change-Id: I9c90e8e4d501f3f37e2f22a7851f032808d46fbe
1 parent 4831543 commit 832815c

File tree

2 files changed

+16
-28
lines changed

2 files changed

+16
-28
lines changed

core/jni/android/graphics/TextLayoutCache.cpp

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ sp<TextLayoutValue> TextLayoutCache::getValue(const SkPaint* paint,
111111

112112
// Compute advances and store them
113113
mShaper->computeValues(value.get(), paint,
114-
reinterpret_cast<const UChar*>(text), start, count,
114+
reinterpret_cast<const UChar*>(key.getText()), start, count,
115115
size_t(contextCount), int(dirFlags));
116116

117117
if (mDebugEnabled) {
@@ -139,15 +139,12 @@ sp<TextLayoutValue> TextLayoutCache::getValue(const SkPaint* paint,
139139
// Update current cache size
140140
mSize += size;
141141

142-
// Copy the text when we insert the new entry
143-
key.internalTextCopy();
144-
145142
bool putOne = mCache.put(key, value);
146143
LOG_ALWAYS_FATAL_IF(!putOne, "Failed to put an entry into the cache. "
147144
"This indicates that the cache already has an entry with the "
148145
"same key but it should not since we checked earlier!"
149146
" - start = %d, count = %d, contextCount = %d - Text = '%s'",
150-
start, count, contextCount, String8(text + start, count).string());
147+
start, count, contextCount, String8(key.getText() + start, count).string());
151148

152149
if (mDebugEnabled) {
153150
nsecs_t totalTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
@@ -158,7 +155,7 @@ sp<TextLayoutValue> TextLayoutCache::getValue(const SkPaint* paint,
158155
value.get(), start, count, contextCount, size, mMaxSize - mSize,
159156
value->getElapsedTime() * 0.000001f,
160157
(totalTime - value->getElapsedTime()) * 0.000001f,
161-
String8(text + start, count).string());
158+
String8(key.getText() + start, count).string());
162159
}
163160
} else {
164161
if (mDebugEnabled) {
@@ -168,7 +165,7 @@ sp<TextLayoutValue> TextLayoutCache::getValue(const SkPaint* paint,
168165
" - Compute time %0.6f ms - Text = '%s'",
169166
start, count, contextCount, size, mMaxSize - mSize,
170167
value->getElapsedTime() * 0.000001f,
171-
String8(text + start, count).string());
168+
String8(key.getText() + start, count).string());
172169
}
173170
}
174171
} else {
@@ -188,7 +185,7 @@ sp<TextLayoutValue> TextLayoutCache::getValue(const SkPaint* paint,
188185
value->getElapsedTime() * 0.000001f,
189186
elapsedTimeThruCacheGet * 0.000001f,
190187
deltaPercent,
191-
String8(text + start, count).string());
188+
String8(key.getText() + start, count).string());
192189
}
193190
if (mCacheHitCount % DEFAULT_DUMP_STATS_CACHE_HIT_INTERVAL == 0) {
194191
dumpCacheStats();
@@ -225,15 +222,16 @@ void TextLayoutCache::dumpCacheStats() {
225222
/**
226223
* TextLayoutCacheKey
227224
*/
228-
TextLayoutCacheKey::TextLayoutCacheKey(): text(NULL), start(0), count(0), contextCount(0),
225+
TextLayoutCacheKey::TextLayoutCacheKey(): start(0), count(0), contextCount(0),
229226
dirFlags(0), typeface(NULL), textSize(0), textSkewX(0), textScaleX(0), flags(0),
230227
hinting(SkPaint::kNo_Hinting), variant(SkPaint::kDefault_Variant), language() {
231228
}
232229

233230
TextLayoutCacheKey::TextLayoutCacheKey(const SkPaint* paint, const UChar* text,
234231
size_t start, size_t count, size_t contextCount, int dirFlags) :
235-
text(text), start(start), count(count), contextCount(contextCount),
232+
start(start), count(count), contextCount(contextCount),
236233
dirFlags(dirFlags) {
234+
textCopy.setTo(text, contextCount);
237235
typeface = paint->getTypeface();
238236
textSize = paint->getTextSize();
239237
textSkewX = paint->getTextSkewX();
@@ -245,7 +243,6 @@ TextLayoutCacheKey::TextLayoutCacheKey(const SkPaint* paint, const UChar* text,
245243
}
246244

247245
TextLayoutCacheKey::TextLayoutCacheKey(const TextLayoutCacheKey& other) :
248-
text(NULL),
249246
textCopy(other.textCopy),
250247
start(other.start),
251248
count(other.count),
@@ -259,9 +256,6 @@ TextLayoutCacheKey::TextLayoutCacheKey(const TextLayoutCacheKey& other) :
259256
hinting(other.hinting),
260257
variant(other.variant),
261258
language(other.language) {
262-
if (other.text) {
263-
textCopy.setTo(other.text, other.contextCount);
264-
}
265259
}
266260

267261
int TextLayoutCacheKey::compare(const TextLayoutCacheKey& lhs, const TextLayoutCacheKey& rhs) {
@@ -304,11 +298,6 @@ int TextLayoutCacheKey::compare(const TextLayoutCacheKey& lhs, const TextLayoutC
304298
return memcmp(lhs.getText(), rhs.getText(), lhs.contextCount * sizeof(UChar));
305299
}
306300

307-
void TextLayoutCacheKey::internalTextCopy() {
308-
textCopy.setTo(text, contextCount);
309-
text = NULL;
310-
}
311-
312301
size_t TextLayoutCacheKey::getSize() const {
313302
return sizeof(TextLayoutCacheKey) + sizeof(UChar) * contextCount;
314303
}

core/jni/android/graphics/TextLayoutCache.h

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,21 +76,16 @@ class TextLayoutCacheKey {
7676

7777
TextLayoutCacheKey(const TextLayoutCacheKey& other);
7878

79-
/**
80-
* We need to copy the text when we insert the key into the cache itself.
81-
* We don't need to copy the text when we are only comparing keys.
82-
*/
83-
void internalTextCopy();
84-
8579
/**
8680
* Get the size of the Cache key.
8781
*/
8882
size_t getSize() const;
8983

9084
static int compare(const TextLayoutCacheKey& lhs, const TextLayoutCacheKey& rhs);
9185

86+
inline const UChar* getText() const { return textCopy.string(); }
87+
9288
private:
93-
const UChar* text; // if text is NULL, use textCopy
9489
String16 textCopy;
9590
size_t start;
9691
size_t count;
@@ -105,8 +100,6 @@ class TextLayoutCacheKey {
105100
SkPaint::FontVariant variant;
106101
SkLanguage language;
107102

108-
inline const UChar* getText() const { return text ? text : textCopy.string(); }
109-
110103
}; // TextLayoutCacheKey
111104

112105
inline int strictly_order_type(const TextLayoutCacheKey& lhs, const TextLayoutCacheKey& rhs) {
@@ -316,6 +309,12 @@ class TextLayoutEngine : public Singleton<TextLayoutEngine> {
316309
TextLayoutEngine();
317310
virtual ~TextLayoutEngine();
318311

312+
/**
313+
* Note: this method currently does a defensive copy of the text argument, in case
314+
* there is concurrent mutation of it. The contract may change, and may in the
315+
* future require the caller to guarantee that the contents will not change during
316+
* the call. Be careful of this when doing optimization.
317+
**/
319318
sp<TextLayoutValue> getValue(const SkPaint* paint, const jchar* text, jint start,
320319
jint count, jint contextCount, jint dirFlags);
321320

0 commit comments

Comments
 (0)