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

Make webview2 disposal safer #14888

Merged
merged 45 commits into from
Feb 8, 2024
Merged

Make webview2 disposal safer #14888

merged 45 commits into from
Feb 8, 2024

Conversation

pinzart90
Copy link
Contributor

@pinzart90 pinzart90 commented Jan 26, 2024

net6 cleanup
MLPipeline cleanup
DynamoWebView2 disposal now waits for Async Initialize to finish
InitializeAsync throws Disposed exception if webview is disposed before initialize is finished.

Copy link

github-actions bot commented Jan 26, 2024

UI Smoke Tests

Test: success. 2 passed, 0 failed.
TestComplete Test Result
Workflow Run: UI Smoke Tests
Check: UI Smoke Tests - net8.0

@pinzart90 pinzart90 marked this pull request as ready for review February 6, 2024 19:27
@@ -40,15 +40,15 @@ jobs:
Write-Error "DynamoCLI.exe was not found!"
}
- name: Install dependencies for linux runtime
run: dotnet restore ${{ github.workspace }}\Dynamo\src\DynamoCore.sln -p:Platform=NET60_Linux --runtime=linux-x64
run: dotnet restore ${{ github.workspace }}\Dynamo\src\DynamoCore.sln -p:Platform=NET_Linux --runtime=linux-x64
Copy link
Member

@mjkkirschner mjkkirschner Feb 7, 2024

Choose a reason for hiding this comment

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

why does this job that is called dynamo core windows built for platform linux?

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 think it tries to build DynamoCore for Linux on a windows machine. Maybe we thought it was a valuable scenario (build on windows but target linux)


initState = AsyncMethodState.Done;
}
catch(ObjectDisposedException ex)
Copy link
Member

Choose a reason for hiding this comment

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

should we be catching other exceptions?

Copy link
Contributor Author

@pinzart90 pinzart90 Feb 7, 2024

Choose a reason for hiding this comment

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

ObjectDisposedException is the only exception type that we should expect to happen, and mostly in tests. It should not trigger crashes and Dynamo should be able to go on running.
For other types of exceptions, we should let Dynamo handle it (crash or not...) Not sure yet though...

/// <returns></returns>
internal async Task Initialize(Action<string> logFn = null)
{
logger ??= logFn;
Copy link
Member

Choose a reason for hiding this comment

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

whats the point of this?

Copy link
Contributor Author

@pinzart90 pinzart90 Feb 7, 2024

Choose a reason for hiding this comment

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

Oops. Should be logger = logFn ?? logger
Was trying to use logFn if it is valid.

Reason: This is to handle cases when we do not have access to the Dynamo Logger (we should try to make this a static so we have access to it everywhere).
logger should point to logFn (a DynamoLogger function call - when it is available) or Console.Writeline

initTask = EnsureCoreWebView2Async();
await initTask;

ObjectDisposedException.ThrowIf(diposeCalled, this);
Copy link
Member

Choose a reason for hiding this comment

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

so this is helpful to trigger early failure?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it forces all callers to handle the exception.
Ex

await webview2.Initialize(); \\ - will throw Disposed exception if it was disposed before execution resumes
\\ - webview2.Dispose could be called before this function resumes execution
webview2.DoStuff(); \\ - Do not execute any more code if Initialize throws an exception

Copy link
Member

@mjkkirschner mjkkirschner left a comment

Choose a reason for hiding this comment

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

left some comments.

webBrowserComponent.CoreWebView2.Settings.IsZoomControlEnabled = false;
webBrowserComponent.CoreWebView2.Settings.IsPinchZoomEnabled = false;
}
catch (ObjectDisposedException ex)
Copy link
Contributor

Choose a reason for hiding this comment

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

How can this exception be thrown now that we are waiting for initialize to happen?

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 can be thrown here https://github.com/DynamoDS/Dynamo/pull/14888/files#diff-405bba4d4ce51bef98ee2ea6e37e396b6834800587417bd4ccfa855590099ef3R43

we throw that exception so that callers of Initialize() can short-circuit their initialization code in case the object was disposed before Initialize finished

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess I'm trying to understand what will be the case when it's disposed while it's being initialized at the same time. Are you saying these things can happen concurrently in different threads?

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 can happen on the same thread, but asynchronously
For example a test:

using (webview2View = new Webview2View){
   webview2View.Initialize();// async method might take a couple of seconds
} // might exit block before webview2View.Initialize finishes (dispose will be called here)

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah ok, so these are additional guardrails for sloppy testing code.

// Dispose can be called from the Finalizer (which can run on a non UI thread)
if (Dispatcher != null)

if (initTask != null && !initTask.IsCompleted)
Copy link
Contributor

Choose a reason for hiding this comment

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

In line 41, you're awaiting initTask, so what will be the case where initTask is not complete?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

await initTask means that the execution of the function is put on hold until it finishes some internal awaits. Meanwhile the thread will continue to execute other function calls.
ex

DoStuff() {
      webview2 = new DynamoWebvView2();
      webview2.Initialize();// call but do not wait for it to finish
      webview2.Dispose();
}
``` This is pretty much what happens in some tests

@pinzart90 pinzart90 merged commit 8775f64 into master Feb 8, 2024
21 checks passed
@pinzart90 pinzart90 deleted the test_crash_logs branch February 8, 2024 03:51
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

Successfully merging this pull request may close these issues.

4 participants