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

Throwing a thenable object in locks.requests callback will implicitly invoke its .then() #77

Closed
ryanwang522 opened this issue Sep 14, 2021 · 13 comments

Comments

@ryanwang522
Copy link

Hi @inexorabletash , I have a question about throwing a "thenable" object (or returning a rejected promise with thenable object) in the web-locks api callback. "thenable" means that the object defines its .then() method.

Consider the following code,

var thenable = { 
    then: (...args) => { console.log("In thenable.then"); } 
} 

navigator.locks.request("lock-test", {mode: "exclusive"}, async () => {
    throw thenable; 
    // or return Promise.reject(thenable);
}).then((res) => console.log(res), (e) => console.log(e));

I am wondering is this an expected behavior?

Didn't find any spec mentioning this behavior. One might related is that if we return(resolve) a "thenable" object inside promise chain, it will unpack it by calling .then. It will call all .then's until it gets raw value.

I'm using Chrome 91.0.4472.106 (Official Build) (64-bit) on ubuntu 18.04.

@bathos
Copy link

bathos commented Sep 14, 2021

I suspect you’ve found a Chromium bug. Neither Web IDL nor the internal ECMA-262 promise operations that Web IDL delegates to would access properties / invoke methods of a rejected Promise’s “reason” value — as you said, only fulfilling should lead to “unpacking”. I don’t think Web Locks is supposed to be doing anything unique in this regard.

@inexorabletash
Copy link
Member

Agreed, looks like a Chrome bug. Can you file one via new.crbug.com ?

@ryanwang522
Copy link
Author

@bathos Thanks for your clarification.
I've filed a ticket at https://bugs.chromium.org/p/chromium/issues/detail?id=1250253.

I am investigating chromium source code which might relates to the issue, will update on the above ticket if I found something suspicious, thanks guys :)

@inexorabletash
Copy link
Member

Thanks. I made a note in the crbug about where to look in the code.

I'll leave this open for a bit; we should have a WPT for this.

@ryanwang522

This comment has been minimized.

@ryanwang522
Copy link
Author

Hi @inexorabletash,

Sorry if I didn't express well in the above comment.
I think the issue comes from that Lock::ThenFunction is used as the onRejected handler of the lock's waiting promise (described in WICG spec, which is the promise returns from async callback).

When Lock::ThenFunction::Call() get invoked (to handle the promise rejection from the callback), it will return the thenable object and further spawn a resolve-thenable-job mircotask. That's why the .then() get called when throwing thenable object from the async callback.

I think the easiest approach to solve this is that we shouldn't return the thenable value in Lock::ThenFunction::Call(value) when the waiting promise has been rejected. How about returning a dummy ScriptValue() here if the promise is rejected?

I also left a comment (with the proposed WPT testcase for this) on crbug as well.

@inexorabletash
Copy link
Member

Hey, let's keep the Chromium discussions on the Chromium bug. It's not a high priority issue so hasn't been given a lot of attention.

@saschanaz
Copy link
Member

Can this be closed then?

@inexorabletash
Copy link
Member

I think I left this open since we should add a WPT for this case.

@ryanwang522
Copy link
Author

May I know what's the expected behavior of the to-do WPT from your perspective?
IIUC, the WPT should make sure that the then method of a thenable object should not be called when throwing/rejecting it from the async lock callback. For example:

  const test_thenable = { 
    then: (...args) => { 
      assert_unreached('Unexpected resolution of thenable object');
    }
  };
  const p = navigator.locks.request(res, async lock => { 
    throw test_thenable;
  });

However, this issue need to be fixed in Lock::ThenFunction first to meet the above behavior.

@saschanaz
Copy link
Member

That test makes sense to me.

@inexorabletash
Copy link
Member

Chromium CL up at https://chromium-review.googlesource.com/4064307 that includes a WPT

chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Nov 30, 2022
An edge case first reported on github[1] - the result from the lock
acquisition callback should be the resolution of the call's promise.
If the result is itself a promise (or a thenable - i.e. has a then()
method), then it should be "unpacked" per normal promise handling.
This works! Also, if the result is an "abrupt completion" (i.e the
callback throws), that means the resolution should be an exception. So
far, so good! But if that exception is a thenable, it should not be
"unpacked" per normal promise handling. But this is what was happening
in Chrome!

The spec is fine here, it was just an implementation bug. Fix the
implementation and add a WPT to cover this case.

[1] w3c/web-locks#77

Bug: 1250253
Change-Id: Ib38cd1ff48d5f25e1939f50e614ce623c2d10a35
@inexorabletash
Copy link
Member

The test case should be upstreamed soon, so closing this.

aarongable pushed a commit to chromium/chromium that referenced this issue Nov 30, 2022
An edge case first reported on github[1] - the result from the lock
acquisition callback should be the resolution of the call's promise.
If the result is itself a promise (or a thenable - i.e. has a then()
method), then it should be "unpacked" per normal promise handling.
This works! Also, if the result is an "abrupt completion" (i.e the
callback throws), that means the resolution should be an exception. So
far, so good! But if that exception is a thenable, it should not be
"unpacked" per normal promise handling. But this is what was happening
in Chrome!

The spec is fine here, it was just an implementation bug. Fix the
implementation and add a WPT to cover this case.

[1] w3c/web-locks#77

Bug: 1250253
Change-Id: Ib38cd1ff48d5f25e1939f50e614ce623c2d10a35
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4064307
Commit-Queue: Joshua Bell <jsbell@chromium.org>
Reviewed-by: Ayu Ishii <ayui@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1077650}
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Nov 30, 2022
An edge case first reported on github[1] - the result from the lock
acquisition callback should be the resolution of the call's promise.
If the result is itself a promise (or a thenable - i.e. has a then()
method), then it should be "unpacked" per normal promise handling.
This works! Also, if the result is an "abrupt completion" (i.e the
callback throws), that means the resolution should be an exception. So
far, so good! But if that exception is a thenable, it should not be
"unpacked" per normal promise handling. But this is what was happening
in Chrome!

The spec is fine here, it was just an implementation bug. Fix the
implementation and add a WPT to cover this case.

[1] w3c/web-locks#77

Bug: 1250253
Change-Id: Ib38cd1ff48d5f25e1939f50e614ce623c2d10a35
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4064307
Commit-Queue: Joshua Bell <jsbell@chromium.org>
Reviewed-by: Ayu Ishii <ayui@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1077650}
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Nov 30, 2022
An edge case first reported on github[1] - the result from the lock
acquisition callback should be the resolution of the call's promise.
If the result is itself a promise (or a thenable - i.e. has a then()
method), then it should be "unpacked" per normal promise handling.
This works! Also, if the result is an "abrupt completion" (i.e the
callback throws), that means the resolution should be an exception. So
far, so good! But if that exception is a thenable, it should not be
"unpacked" per normal promise handling. But this is what was happening
in Chrome!

The spec is fine here, it was just an implementation bug. Fix the
implementation and add a WPT to cover this case.

[1] w3c/web-locks#77

Bug: 1250253
Change-Id: Ib38cd1ff48d5f25e1939f50e614ce623c2d10a35
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4064307
Commit-Queue: Joshua Bell <jsbell@chromium.org>
Reviewed-by: Ayu Ishii <ayui@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1077650}
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this issue Dec 11, 2022
…ck shouldn't invoke it, a=testonly

Automatic update from web-platform-tests
Web Locks: Throwing a thenable in callback shouldn't invoke it

An edge case first reported on github[1] - the result from the lock
acquisition callback should be the resolution of the call's promise.
If the result is itself a promise (or a thenable - i.e. has a then()
method), then it should be "unpacked" per normal promise handling.
This works! Also, if the result is an "abrupt completion" (i.e the
callback throws), that means the resolution should be an exception. So
far, so good! But if that exception is a thenable, it should not be
"unpacked" per normal promise handling. But this is what was happening
in Chrome!

The spec is fine here, it was just an implementation bug. Fix the
implementation and add a WPT to cover this case.

[1] w3c/web-locks#77

Bug: 1250253
Change-Id: Ib38cd1ff48d5f25e1939f50e614ce623c2d10a35
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4064307
Commit-Queue: Joshua Bell <jsbell@chromium.org>
Reviewed-by: Ayu Ishii <ayui@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1077650}

--

wpt-commits: 3428426669666600429687c1cfb708cefd8cc79b
wpt-pr: 37259
BruceDai pushed a commit to BruceDai/wpt that referenced this issue Dec 13, 2022
An edge case first reported on github[1] - the result from the lock
acquisition callback should be the resolution of the call's promise.
If the result is itself a promise (or a thenable - i.e. has a then()
method), then it should be "unpacked" per normal promise handling.
This works! Also, if the result is an "abrupt completion" (i.e the
callback throws), that means the resolution should be an exception. So
far, so good! But if that exception is a thenable, it should not be
"unpacked" per normal promise handling. But this is what was happening
in Chrome!

The spec is fine here, it was just an implementation bug. Fix the
implementation and add a WPT to cover this case.

[1] w3c/web-locks#77

Bug: 1250253
Change-Id: Ib38cd1ff48d5f25e1939f50e614ce623c2d10a35
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4064307
Commit-Queue: Joshua Bell <jsbell@chromium.org>
Reviewed-by: Ayu Ishii <ayui@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1077650}
jamienicol pushed a commit to jamienicol/gecko that referenced this issue Dec 14, 2022
…ck shouldn't invoke it, a=testonly

Automatic update from web-platform-tests
Web Locks: Throwing a thenable in callback shouldn't invoke it

An edge case first reported on github[1] - the result from the lock
acquisition callback should be the resolution of the call's promise.
If the result is itself a promise (or a thenable - i.e. has a then()
method), then it should be "unpacked" per normal promise handling.
This works! Also, if the result is an "abrupt completion" (i.e the
callback throws), that means the resolution should be an exception. So
far, so good! But if that exception is a thenable, it should not be
"unpacked" per normal promise handling. But this is what was happening
in Chrome!

The spec is fine here, it was just an implementation bug. Fix the
implementation and add a WPT to cover this case.

[1] w3c/web-locks#77

Bug: 1250253
Change-Id: Ib38cd1ff48d5f25e1939f50e614ce623c2d10a35
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4064307
Commit-Queue: Joshua Bell <jsbell@chromium.org>
Reviewed-by: Ayu Ishii <ayui@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1077650}

--

wpt-commits: 3428426669666600429687c1cfb708cefd8cc79b
wpt-pr: 37259
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

No branches or pull requests

4 participants