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

Canceling JSHost.ImportAsync fails because dynamic_import in invoke-js.ts is not cancelable #80028

Closed
nikonthethird opened this issue Dec 28, 2022 · 3 comments · Fixed by #80257
Assignees
Milestone

Comments

@nikonthethird
Copy link

Description

TLDR:

The cancellation token given to JSHost.ImportAsync does not work because the underlying JS function called by ImportAsync does not return a Promise that is "controllable" (outfitted with a special symbol).

// Repro here: https://github.com/nikonthethird/ImportAsyncError, or paste this code into an InitializeAsync  of a page in Blazor WASM:
var cts = new CancellationTokenSource();
cts.Cancel();
await JSHost.ImportAsync("hello", "world.js", cts.Token);

The code above fails with an exception because dynamic_import is not controllable (the promise generated by the function is not wrapped using wrap_as_cancelable_promise like for example http_wasm_fetch is).

Description

After upgrading to .NET 7, an application of mine has been throwing the following error messages in the log:

"Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer"]: Unhandled exception rendering component: "Error: Assert failed: Promise is not controllable"
at System.Runtime.InteropServices.JavaScript.CancelablePromise._CancelPromise(IntPtr )
   at System.Runtime.InteropServices.JavaScript.CancelablePromise.CancelPromise(Task )
   at System.Net.Http.BrowserHttpInterop.<>c__DisplayClass13_0`1[[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].<CancelationHelper>b__0()
   at System.Threading.CancellationToken.<>c.<Register>b__12_0(Object )
   at System.Threading.CancellationTokenSource.Invoke(Delegate , Object , CancellationTokenSource )
   at System.Threading.CancellationTokenSource.CallbackNode.<>c.<ExecuteCallback>b__9_0(Object )
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext , ContextCallback , Object )
--- End of stack trace from previous location ---

This is what I discovered:

Since .NET 7 uses the [JSImport] source generator internally, I suspect the exceptions I am experiencing also come from some internal JSHost.ImportAsync calls.

Reproduction Steps

@page "/"
@using System.Runtime.InteropServices.JavaScript

<h1>Hello, world!</h1>

@if (error is not null) {
    <p>@error</p>
}

@code {
    private String? error;

    protected override async Task OnInitializedAsync() {
        var cts = new CancellationTokenSource();
        cts.Cancel();

        try {
            await JSHost.ImportAsync("hello", "world.js", cts.Token);
        } catch (Exception ex) {
            error = ex.ToString();
        }
        
    }
}

A reproduction can also be found here.

Expected behavior

The import should be cancelled.

Actual behavior

Error: Assert failed: Promise is not controllable

Regression?

Yes, no problem with my code in .NET 6.
Note that I do not even use JSHost.ImportAsync in my code directly, but .NET 7 itself uses the new [JSImport] source generated, and this is where I expect the error to occur.

Known Workarounds

None so far.

Configuration

.NET 7.0.1
Ubuntu 20.04 / Firefox
Windows 10 22H2 / Chromium
x64
It happens in all cases, I don't know why I even provided this info...

Other information

No response

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Dec 28, 2022
@teo-tsirpanis teo-tsirpanis added the arch-wasm WebAssembly architecture label Dec 28, 2022
@ghost
Copy link

ghost commented Dec 28, 2022

Tagging subscribers to 'arch-wasm': @lewing
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

TLDR:

The cancellation token given to JSHost.ImportAsync does not work because the underlying JS function called by ImportAsync does not return a Promise that is "controllable" (outfitted with a special symbol).

// Repro here: https://github.com/nikonthethird/ImportAsyncError, or paste this code into an InitializeAsync  of a page in Blazor WASM:
var cts = new CancellationTokenSource();
cts.Cancel();
await JSHost.ImportAsync("hello", "world.js", cts.Token);

The code above fails with an exception because dynamic_import is not controllable (the promise generated by the function is not wrapped using wrap_as_cancelable_promise like for example http_wasm_fetch is).

Description

After upgrading to .NET 7, an application of mine has been throwing the following error messages in the log:

"Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer"]: Unhandled exception rendering component: "Error: Assert failed: Promise is not controllable"
at System.Runtime.InteropServices.JavaScript.CancelablePromise._CancelPromise(IntPtr )
   at System.Runtime.InteropServices.JavaScript.CancelablePromise.CancelPromise(Task )
   at System.Net.Http.BrowserHttpInterop.<>c__DisplayClass13_0`1[[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].<CancelationHelper>b__0()
   at System.Threading.CancellationToken.<>c.<Register>b__12_0(Object )
   at System.Threading.CancellationTokenSource.Invoke(Delegate , Object , CancellationTokenSource )
   at System.Threading.CancellationTokenSource.CallbackNode.<>c.<ExecuteCallback>b__9_0(Object )
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext , ContextCallback , Object )
--- End of stack trace from previous location ---

This is what I discovered:

Since .NET 7 uses the [JSImport] source generator internally, I suspect the exceptions I am experiencing also come from some internal JSHost.ImportAsync calls.

Reproduction Steps

@page "/"
@using System.Runtime.InteropServices.JavaScript

<h1>Hello, world!</h1>

@if (error is not null) {
    <p>@error</p>
}

@code {
    private String? error;

    protected override async Task OnInitializedAsync() {
        var cts = new CancellationTokenSource();
        cts.Cancel();

        try {
            await JSHost.ImportAsync("hello", "world.js", cts.Token);
        } catch (Exception ex) {
            error = ex.ToString();
        }
        
    }
}

A reproduction can also be found here.

Expected behavior

The import should be cancelled.

Actual behavior

Error: Assert failed: Promise is not controllable

Regression?

Yes, no problem with my code in .NET 6.
Note that I do not even use JSHost.ImportAsync in my code directly, but .NET 7 itself uses the new [JSImport] source generated, and this is where I expect the error to occur.

Known Workarounds

None so far.

Configuration

.NET 7.0.1
Ubuntu 20.04 / Firefox
Windows 10 22H2 / Chromium
x64
It happens in all cases, I don't know why I even provided this info...

Other information

No response

Author: nikonthethird
Assignees: -
Labels:

arch-wasm, untriaged, area-System.Runtime.InteropServices.JavaScript

Milestone: -

@radical
Copy link
Member

radical commented Jan 3, 2023

/cc @pavelsavara @maraf

@pavelsavara pavelsavara self-assigned this Jan 3, 2023
@maraf maraf removed the untriaged New issue has not been triaged by the area owner label Jan 5, 2023
@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Jan 5, 2023
@pavelsavara
Copy link
Member

thanks @nikonthethird , your explanation is perfect :)

@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Jan 6, 2023
@pavelsavara pavelsavara added this to the 7.0.x milestone Jan 6, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Feb 6, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants