Skip to content
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

Merged

Conversation

nguyenhuy
Copy link
Member

@nguyenhuy nguyenhuy commented Jul 12, 2018

@nguyenhuy
Copy link
Member Author

Note to reviewers: please review this after #1022 and #1023.


{
ASDN::MutexUnlocker u(__instanceLock__);
[self _u_setNeedsLayoutFromAbove];
Copy link
Member Author

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.

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]) {
Copy link
Member Author

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.


CGSize boundsSizeForLayout = ASCeilSizeValues(self.threadSafeBounds.size);
std::shared_ptr<ASDisplayNodeLayout> pendingLayout = _pendingDisplayNodeLayout;
std::shared_ptr<ASDisplayNodeLayout> calculatedLayout = _calculatedDisplayNodeLayout;
Copy link
Member Author

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]
Copy link
Member Author

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.

@@ -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];
Copy link
Member Author

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

Copy link
Member

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);
Copy link
Member Author

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.

Copy link
Member

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?

@ghost
Copy link

ghost commented Jul 12, 2018

🚫 CI failed with log

Copy link
Member

@Adlai-Holler Adlai-Holler left a 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.

@nguyenhuy
Copy link
Member Author

Uhh, I like that. Will update!

@TextureGroup TextureGroup deleted a comment Jul 12, 2018
@TextureGroup TextureGroup deleted a comment Jul 12, 2018
@TextureGroup TextureGroup deleted a comment Jul 12, 2018
@nguyenhuy nguyenhuy force-pushed the HN-Stricter-Locking-Safety-Checks branch from 4a0ebd2 to 465be36 Compare July 12, 2018 20:43
- 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.
@nguyenhuy nguyenhuy force-pushed the HN-Stricter-Locking-Safety-Checks branch from 465be36 to c037799 Compare July 12, 2018 20:49
CGSize boundsSizeForLayout = ASCeilSizeValues(self.threadSafeBounds.size);
std::shared_ptr<ASDisplayNodeLayout> pendingLayout = _pendingDisplayNodeLayout;
Copy link
Member

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?

return (_view != nil || (_layer != nil && _flags.layerBacked));
}

- (BOOL)_isNodeLoaded
{
return (_view != nil || (_layer != nil && _flags.layerBacked));
Copy link
Member

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?

Copy link
Member Author

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.

}

/// Called without the lock. Client is responsible for locking safety.
- (BOOL)_unsafe_unlocked_isNodeLoaded
Copy link
Member

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great point!

@TextureGroup TextureGroup deleted a comment Jul 12, 2018
{
return (_view != nil || (_layer != nil && _flags.layerBacked));
return (node->_view != nil || (node->_layer != nil && node->_flags.layerBacked));
Copy link
Member

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?

@nguyenhuy nguyenhuy merged commit 0dc97fb into TextureGroup:master Jul 13, 2018
@nguyenhuy nguyenhuy deleted the HN-Stricter-Locking-Safety-Checks branch July 13, 2018 21:58
mikezucc pushed a commit to mikezucc/Texture that referenced this pull request Oct 2, 2018
- 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants