-
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
Unlock before cleanup and calling out to subclass hooks for animated images. #1087
Conversation
Source/ASImageNode+AnimatedImage.mm
Outdated
[self animatedImageSet:_animatedImage previousAnimatedImage:previousAnimatedImage]; | ||
{ | ||
// Unlock for cleanup and calling subclass hook | ||
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.
Is this actually safe? We don't necessarily know what the calling scope is doing and therefore unlocking here could be dangerous? I know it's very unlikely (I can only think of convoluted examples)…
I'm not sure of a better solution though… If we do end up deciding this, we should at least add a comment that unlocking a lock someone else locked might cause us a bad time in the future.
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.
Agree. Another thing is that with a recursive lock, the lock may have been acquired multiple times and so unlocking once here is not enought.
Ideally, we have to make this method _u_
but it can be painful and may not be possible.
Another idea is dispatching before calling -animatedImageSet:previousAnimatedImage:
and deallocating the previous image.
Source/ASImageNode+AnimatedImage.mm
Outdated
|
||
self.contents = nil; | ||
[self setCoverImage:nil]; |
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.
Shouldn't we wrap the above lines in a shouldCleanup
block?
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.
Hm, can you explain why -setCoverImage:
needs to be called unlocked?
Somewhat unrelated: that method acquires the lock right away, so shouldn't we just call -_locked_setCoverImage:
directly here?
@@ -173,7 +173,6 @@ typedef UIImage * _Nullable (^asimagenode_modification_block_t)(UIImage *image); | |||
* | |||
* @discussion This method is for subclasses to override so they can know if an animated image | |||
* has been set on the node. | |||
* @warning this method is called with the node's lock held. |
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.
Overall, I think it's great to remove this caveat.
Source/ASImageNode+AnimatedImage.mm
Outdated
[self animatedImageSet:_animatedImage previousAnimatedImage:previousAnimatedImage]; | ||
{ | ||
// Unlock for cleanup and calling subclass hook | ||
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.
Agree. Another thing is that with a recursive lock, the lock may have been acquired multiple times and so unlocking once here is not enought.
Ideally, we have to make this method _u_
but it can be painful and may not be possible.
Another idea is dispatching before calling -animatedImageSet:previousAnimatedImage:
and deallocating the previous image.
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.
Opps, didn't mean to approve.
9918f20
to
0be1bc5
Compare
Unfortunately this is all not so trivial to solve. If we would like to solve this without dispatching to the next runloop it will take a lot of effort to refactor every (AS) image node and the fact we have to call a delegate subclass hook needs to be considered every time we change the code. After looking into it more deeply I think there are a couple of ways we could approach this:
I eventually went with version 6 for now you can see the history too for also implementing 5. Happy for any thoughts or better ideas cc @nguyenhuy @garrettmoon @appleguy @Adlai-Holler |
Source/ASImageNode+AnimatedImage.mm
Outdated
}); | ||
// Don't need to wakeup the runloop as the current is already running | ||
// CFRunLoopWakeUp(runLoop); // Should not be necessary | ||
|
||
// Animated image can take while to dealloc, do it off the main queue | ||
if (previousAnimatedImage != nil) { |
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 think we need to deallocate the previous image after calling out to subclass, otherwise it will not be deallocated in background if the deallocation occurs before the next run loop.
@nguyenhuy You wanna give that another look please |
3858dda
to
75da400
Compare
75da400
to
cde0eba
Compare
animatedImageSet:previousAnimatedImage:
to prevent a deadlock.