-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix crash setting attributed text on multiple threads #1141
Conversation
@Adlai-Holler Any reason we are not using |
#endif | ||
NSAttributedString *cleanedAttributedString = ASCleanseAttributedStringOfCoreTextAttributes(attributedText); | ||
|
||
[self _locked_invalidateTruncationText]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, so invalidating the truncation text must be done while we still hold the lock. Because after we release it, another thread may set a new truncation text that will then be cleared by this thread, or may draw this soon-to-be-invalidated text.
Let's leave a comment/explaination here.
if (length > 0) { | ||
ASLayoutElementStyle *style = [self _locked_style]; | ||
style.ascender = [[self class] ascenderWithAttributedString:cleanedAttributedString]; | ||
style.descender = [[attributedText attribute:NSFontAttributeName atIndex:cleanedAttributedString.length - 1 effectiveRange:NULL] descender]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same deal with ascender
and descender
here. We basically use the lock as a transactional one.
Source/ASTextNode.mm
Outdated
// Accessiblity | ||
self.accessibilityLabel = _attributedText.string; | ||
self.isAccessibilityElement = (length != 0); // We're an accessibility element by default if there is a string. | ||
self.isAccessibilityElement = (_attributedText.length != 0); // We're an accessibility element by default if there is a string. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_attributedText
is accessed without the lock here. Can we do these operations while having the locks as well?
At least, we need to access the text via its setter (self.attributedText
) so that if it's changed by another thread right before this, we'll end up grabbing the updated text (and its length) here. Also, it's no longer safe to use the attributedText
param of this method.
Source/ASTextNode.mm
Outdated
@@ -1233,12 +1234,17 @@ - (void)_invalidateTruncationText | |||
{ | |||
{ | |||
ASLockScopeSelf(); | |||
_composedTruncationText = nil; | |||
[self _locked_invalidateTruncationText]; | |||
} | |||
|
|||
[self setNeedsDisplay]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like now is the time to let callers of this (unlocked) method to call -setNeedsDisplay
themselves. It's a bit confusing to only automatically do it with the unlock version.
@@ -1061,10 +1062,15 @@ - (NSUInteger)lineCount | |||
- (void)_invalidateTruncationText | |||
{ | |||
ASLockScopeSelf(); | |||
_textContainer.truncationToken = nil; | |||
[self _locked_invalidateTruncationText]; | |||
[self setNeedsDisplay]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it actually ok to call -setNeedsDisplay
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I asked that myself too, but I didn't want to change it within this diff. In theory the code in ASTextNode2
should be adjusted to the same as in ASTextNode
after this lands.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
b394f45
to
75d5d0e
Compare
d950b99
to
515e8b9
Compare
@nguyenhuy Would appreciate if you could give that another look today. Thanks! |
@@ -1061,10 +1062,15 @@ - (NSUInteger)lineCount | |||
- (void)_invalidateTruncationText | |||
{ | |||
ASLockScopeSelf(); | |||
_textContainer.truncationToken = nil; | |||
[self _locked_invalidateTruncationText]; | |||
[self setNeedsDisplay]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Currently some race condition can happen within
-[ASTextNode setAttributedText:]
:setAttributedText:
You can repro it if you simulate a thread switch and the crash on the descender line via a semaphore like:
And in the frontend with something like: