-
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
Stricter locking assertions #1024
Stricter locking assertions #1024
Conversation
Source/ASDisplayNode+Layout.mm
Outdated
|
||
{ | ||
ASDN::MutexUnlocker u(__instanceLock__); | ||
[self _u_setNeedsLayoutFromAbove]; |
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.
Apply the same fix in #1022 here just to be safe, i.e if _u_setNeedsLayoutFromAbove
causes an assertion, it'll be correctly shown.
Source/ASDisplayNode+Layout.mm
Outdated
ASDN::MutexLocker l(__instanceLock__); | ||
// Check if we are a subnode in a layout transition. | ||
// In this case no measurement is needed as it's part of the layout transition | ||
if ([self _isLayoutTransitionInvalid]) { | ||
if ([self _locked_isLayoutTransitionInvalid]) { |
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.
While having the lock, call the _locked_
method.
Source/ASDisplayNode+Layout.mm
Outdated
|
||
CGSize boundsSizeForLayout = ASCeilSizeValues(self.threadSafeBounds.size); | ||
std::shared_ptr<ASDisplayNodeLayout> pendingLayout = _pendingDisplayNodeLayout; | ||
std::shared_ptr<ASDisplayNodeLayout> calculatedLayout = _calculatedDisplayNodeLayout; |
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.
Cache these properties to improve readability of the following lines
#notAPerfOptimization
} | ||
|
||
[self transitionLayoutWithSizeRange:sizeRange | ||
[self transitionLayoutWithSizeRange:[self _constrainedSizeForLayoutPass] |
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.
Easier to just call the "unlocked" variant.
Source/ASDisplayNode.mm
Outdated
@@ -584,14 +590,20 @@ - (BOOL)isNodeLoaded | |||
if (ASDisplayNodeThreadIsMain()) { | |||
// Because the view and layer can only be created and destroyed on Main, that is also the only thread | |||
// where the state of this property can change. As an optimization, we can avoid locking. | |||
return [self _locked_isNodeLoaded]; | |||
return [self _isNodeLoaded]; |
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.
Can't call the _locked_
method 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.
Good catch!
@@ -360,6 +361,8 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N | |||
if (self.playerState == ASVideoNodePlayerStateLoading && _delegateFlags.delegateVideoNodeDidRecoverFromStall) { | |||
[self.delegate videoNodeDidRecoverFromStall:self]; | |||
} | |||
|
|||
ASUnlockScope(self); |
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.
As mentions in the description, play
shouldn't be called with the lock.
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 worth adding a comment in code?
🚫 CI failed with log |
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.
If we're renaming the macro, how about ASAssertLocked
and ASAssertUnlocked
? Over time I'd like to move away from ASDisplayNode
as a prefix, since the framework now encompasses so much more.
Uhh, I like that. Will update! |
4a0ebd2
to
465be36
Compare
- Rename `ASDisplayNodeAssertLockUnownedByCurrentThread` to `ASDisplayNodeAssertLockNotHeld`, and `ASDisplayNodeAssertLockOwnedByCurrentThread` to `ASDisplayNodeAssertLockHeld` -> shorter and hopefully easier to distinguish between the two. - Add assertions to `_locked_` and `_u_` (i.e "unlocked") methods. - Turn `CHECK_LOCKING_SAFETY` flag on by default. After TextureGroup#1022 and TextureGroup#1023, we're in a good shape to actually enforce locked/unlocked requirements of internal methods. Our test suite passed, and we'll test more at Pinterest after the sync this week. - Fix ASVideoNode to avoid calling `play` while holding the lock. That method inserts a subnode and must be called lock free. - Other minor changes.
465be36
to
c037799
Compare
Source/ASDisplayNode+Layout.mm
Outdated
CGSize boundsSizeForLayout = ASCeilSizeValues(self.threadSafeBounds.size); | ||
std::shared_ptr<ASDisplayNodeLayout> pendingLayout = _pendingDisplayNodeLayout; |
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.
Do you mind adding a comment explaining the shared_ptr's here?
Source/ASDisplayNode.mm
Outdated
return (_view != nil || (_layer != nil && _flags.layerBacked)); | ||
} | ||
|
||
- (BOOL)_isNodeLoaded | ||
{ | ||
return (_view != nil || (_layer != nil && _flags.layerBacked)); |
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.
Does this need to be updated with a lock?
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.
This method is called unlocked, and the client is responsible for its safety. Renamed to -_unsafe_unlocked_isNodeLoaded
to reflect that.
Source/ASDisplayNode.mm
Outdated
} | ||
|
||
/// Called without the lock. Client is responsible for locking safety. | ||
- (BOOL)_unsafe_unlocked_isNodeLoaded |
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.
Can we simplify all of this by just using _layer != nil
inline when we want a fast loaded check?
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.
great point!
Source/ASDisplayNode.mm
Outdated
{ | ||
return (_view != nil || (_layer != nil && _flags.layerBacked)); | ||
return (node->_view != nil || (node->_layer != nil && node->_flags.layerBacked)); |
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.
What if we get rid of this function and just put _layer != nil
inside the places where we need it?
…huy/Texture into HN-Stricter-Locking-Safety-Checks
…layer backed, the layer should always be set if loaded
- Rename `ASDisplayNodeAssertLockUnownedByCurrentThread` to `ASAssertUnlocked`, and `ASDisplayNodeAssertLockOwnedByCurrentThread` to `ASAssertLocked` -> shorter and hopefully easier to distinguish between the two. - Add assertions to `_locked_` and `_u_` (i.e "unlocked") methods. - Turn `CHECK_LOCKING_SAFETY` flag on by default. After TextureGroup#1022 and TextureGroup#1023, we're in a good shape to actually enforce locked/unlocked requirements of internal methods. Our test suite passed, and we'll test more at Pinterest after the sync this week. - Fix ASVideoNode to avoid calling `play` while holding the lock. That method inserts a subnode and must be called lock free. - Simplify `_loaded(node)` to only nil-check `_layer` because regardless of whether the node is view or layer backed, the layer should always be set if loaded. Use it throughout. - Other minor changes.
ASDisplayNodeAssertLockUnownedByCurrentThread
toASAssertUnlocked
, andASDisplayNodeAssertLockOwnedByCurrentThread
toASAssertLocked
-> shorter and hopefully easier to distinguish between the two._locked_
and_u_
(i.e "unlocked") methods.CHECK_LOCKING_SAFETY
on by default. After Fix misleading/scary stack trace shown when an assertion occurs during node measurement #1022 and Make sure -_completePendingLayoutTransition is called without the node's instance lock #trivial #1023, we're in a good shape to actually enforce locked/unlocked requirements of internal methods. Our test suite passed, and we'll test more at Pinterest after the sync this week.play
while holding the lock. That method inserts a subnode and must be called lock free.