-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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 race condition with layout -> unlayout #30426
Conversation
src/custom-element.js
Outdated
@@ -1168,6 +1169,9 @@ function createBaseCustomElementClass(win) { | |||
|
|||
return promise.then( | |||
() => { | |||
if (abortSignal.aborted) { | |||
return; |
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.
Two things:
- Do we need to reset
LOAD_START
from above? And the same in thecatch
clause? - It looks like spec for
AbortController
says that a supporting promise should yield theAbortError
rejection?
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 we need to reset LOAD_START from above? And the same in the catch clause?
That is taken care of when calling the unlayoutCallback
. By the time we get here, it's already reset.
It looks like spec for AbortController says that a supporting promise should yield the AbortError rejection?
That's specific to fetch
. We could reject with an error, but that I didn't want to polyfill AbortError
, too. As long as it gets handled, it should be fine though either the success or failure cases.
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 still have a concern here. The promise realistically fails by API and I think we should say so as well. We could even just use our cancellation()
error? Or we can even stick code = 20
on it, which is how, I think, AbortError
is usually tested for.
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 switched to the cancellation error.
// We hit a race condition, where `layoutCallback` -> `unlayoutCallback` | ||
// was called in quick succession. Since the unlayout was called before | ||
// the layout completed, we want to remain in the unlayout state. | ||
const err = dev().createError('layoutComplete race'); |
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 logging? This seems like a very expected condition.
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.
It's going through expectedError
, but I could remove if you'd like.
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 temporarily - it's ok. But in the long term, I don't think we need it. Up to you. But if you keep it, I recommend we file an issue to cleanup.
I'm confused about what the introduction of |
When an unlayout (eg, when carousel swipes away) occurs before an unlayout, we can get into a broken state where we _think_ we're fully laid out but in reality we've performed the unlayout. There's still race conditions in each component, since they need to track their own layout/unlayout state. We should quickly migrate to Bento where this isn't an issue. Fixes ampproject#27036 Partial ampproject#13838
a82715c
to
ab3cabb
Compare
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.
Just two more questions. Otherwise this looks good.
src/custom-element.js
Outdated
assertNotTemplate(this); | ||
devAssert(this.isBuilt(), 'Must be built to receive viewport events'); | ||
if (signal.aborted) { | ||
throw cancellation(); |
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 instead to Promise.reject(cancellation())
?
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.
Done.
) { | ||
return; | ||
} | ||
if (this.abortController_) { | ||
this.abortController_.abort(); |
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 we also need to set this.abortController_ = null
here or if layout completes successfuly?
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.
Done.
36b04dd
to
46ac2e9
Compare
Since we're clearing it out after layout completes, it won't exist when we eventually unlayout
7986509
to
4cb6a5c
Compare
8ed3304
to
6154204
Compare
Small prettier change required, but otherwise looks good.
|
* Fix race condition with layout -> unlayout When an unlayout (eg, when carousel swipes away) occurs before an unlayout, we can get into a broken state where we _think_ we're fully laid out but in reality we've performed the unlayout. There's still race conditions in each component, since they need to track their own layout/unlayout state. We should quickly migrate to Bento where this isn't an issue. Fixes ampproject#27036 Partial ampproject#13838 * Rename variable * Throw cancellation errors where appropriate * Review fixes * Fix dep requirement * Fix sourcemap check * Fix tests * Only allow missing signal param in test mode * Do not report missing abortController Since we're clearing it out after layout completes, it won't exist when we eventually unlayout * Toggle amp-analytics visibility _after_ layout * Fix method call * Fix test * lint * Fix test
When an unlayout (eg, when carousel swipes away) occurs before an unlayout, we can get into a broken state where we think we're fully laid out but in reality we've performed the unlayout.
There's still race conditions in each component, since they need to track their own layout/unlayout state. We should quickly migrate to Bento where this isn't an issue.
Fixes #27036
Partial #13838