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

🐛 Fix race condition with layout -> unlayout #30426

Merged
merged 14 commits into from
Oct 9, 2020

Conversation

jridgewell
Copy link
Contributor

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

src/custom-element.js Outdated Show resolved Hide resolved
src/custom-element.js Outdated Show resolved Hide resolved
@@ -1168,6 +1169,9 @@ function createBaseCustomElementClass(win) {

return promise.then(
() => {
if (abortSignal.aborted) {
return;
Copy link
Contributor

Choose a reason for hiding this comment

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

Two things:

  1. Do we need to reset LOAD_START from above? And the same in the catch clause?
  2. It looks like spec for AbortController says that a supporting promise should yield the AbortError rejection?

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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.

src/service/resource.js Outdated Show resolved Hide resolved
// 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');
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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.

src/service/resource.js Show resolved Hide resolved
src/service/resource.js Outdated Show resolved Hide resolved
src/service/resources-impl.js Outdated Show resolved Hide resolved
@samouri
Copy link
Member

samouri commented Oct 1, 2020

I'm confused about what the introduction of AbortController buys us

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
Copy link
Contributor

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

assertNotTemplate(this);
devAssert(this.isBuilt(), 'Must be built to receive viewport events');
if (signal.aborted) {
throw cancellation();
Copy link
Contributor

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())?

Copy link
Contributor Author

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();
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@jridgewell jridgewell force-pushed the layout-race branch 2 times, most recently from 7986509 to 4cb6a5c Compare October 8, 2020 04:54
@jridgewell jridgewell force-pushed the layout-race branch 2 times, most recently from 8ed3304 to 6154204 Compare October 8, 2020 18:51
@kristoferbaxter
Copy link
Contributor

Small prettier change required, but otherwise looks good.

extensions/amp-analytics/0.1/test/test-amp-analytics.js
  232:11  error  Insert `;`  prettier/prettier

@CLAassistant
Copy link

CLAassistant commented Oct 8, 2020

CLA assistant check
All committers have signed the CLA.

@jridgewell jridgewell merged commit 93fdf01 into ampproject:master Oct 9, 2020
@jridgewell jridgewell deleted the layout-race branch October 9, 2020 22:21
ed-bird pushed a commit to ed-bird/amphtml that referenced this pull request Dec 10, 2020
* 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

amp-carousel : images load intermittently
6 participants