Skip to content

Conversation

@HeikoKlare
Copy link
Contributor

@HeikoKlare HeikoKlare commented Jan 31, 2025

The WebView environment used for Edge manages a list of all currently used Edge instances inside an application. This data structure is modified at different places, which may be executed by different threads, such introducing a risk of race conditions on the unsynchronized data structure. In addition, when disposing a Display (such as at application shutdown), Edge instances are disposed in an according dispose listener. This can cause ConcurrentModificationExceptions as the listener iterates through the list of Edge instances and calls their disposal, which in turn removes the Edge instance from that list.

This change makes the data structure thread-safe and copy-on-write to avoid that ConcurrentModificationException occur when iterating over it while removing elements from it.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 31, 2025

Test Results

   494 files  ±0     494 suites  ±0   10m 57s ⏱️ + 1m 2s
 4 333 tests ±0   4 320 ✅ ±0   13 💤 ±0  0 ❌ ±0 
16 574 runs  ±0  16 466 ✅ ±0  108 💤 ±0  0 ❌ ±0 

Results for commit 395eed5. ± Comparison against base commit b0a1fb7.

♻️ This comment has been updated with latest results.

private record WebViewEnvironment(ICoreWebView2Environment environment, List<Edge> instances) {
public WebViewEnvironment(ICoreWebView2Environment environment) {
this (environment, new ArrayList<>());
this (environment, Collections.synchronizedList(new ArrayList<>()));
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
this (environment, Collections.synchronizedList(new ArrayList<>()));
this (environment, new CopyOnWriteArrayList<>());

@laeubi
Copy link
Contributor

laeubi commented Jan 31, 2025

This change makes the data structure thread-safe and avoids that ConcurrentModificationException occur by iterating through a copy of the list of Edge instances.

I don'tsee a copy in the PR... but if you use CopyOnWriteArrayList you can safely iterate the list while it is modified.

The WebView environment used for Edge manages a list of all currently
used Edge instances inside an application. This data structure is
modified at different places, which may be executed by different
threads, such introducing a risk of race conditions on the
unsynchronized data structure. In addition, when disposing a Display
(such as at application shutdown), Edge instances are disposed in an
according dispose listener. This can cause
ConcurrentModificationExceptions as the listener iterates through the
list of Edge instances and calls their disposal, which in turn removes
the Edge instance from that list.

This change makes the data structure thread-safe and copy-on-write to
avoid that ConcurrentModificationException occur when iterating over it
while removing elements from it.
@HeikoKlare
Copy link
Contributor Author

Good point, thank you! I pushed an incomplete state where the copy operatoin was missing. But using an copy-on-write data structure makes more sense anyway, as it does not require a local avoidance of concurrent modification when accessing the data structure but just solves that by the type of data structure itself, also making it easier to understand (as it's not immediately clear why there is a need for making a copy of the data structure).

I updated the PR accordingly.

@HeikoKlare
Copy link
Contributor Author

Additional info: this is a stack trace of an exception that is to be avoided by this PR:

java.util.ConcurrentModificationException
	at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1095)
	at java.base/java.util.ArrayList$Itr.next(ArrayList.java:1049)
	at org.eclipse.swt.browser.Edge.lambda$12(Edge.java:554)
	at org.eclipse.swt.widgets.Display.release(Display.java:3779)
	at org.eclipse.swt.graphics.Device.dispose(Device.java:339)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:194)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:208)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:143)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:109)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:439)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:271)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:668)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:605)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1481)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1454)

private static final Duration MAXIMUM_OPERATION_TIME = Duration.ofMillis(Integer.getInteger(WEB_VIEW_OPERATION_TIMEOUT, 5_000));

private record WebViewEnvironment(ICoreWebView2Environment environment, ArrayList<Edge> instances) {
private record WebViewEnvironment(ICoreWebView2Environment environment, List<Edge> instances) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if a record is really useful here. can the instances probably be part of ICoreWebView2Environment?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ICoreWebView2Environment is a native interface (https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2environment?view=webview2-1.0.2957.106), so I guess nothing we could (or at least should) extend.

Copy link
Contributor

Choose a reason for hiding this comment

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

okay than its not an option of course ...

@HeikoKlare
Copy link
Contributor Author

Jenkins failures are known and unrelated (in particular since the change only affects Edge, which is not executed in the Jenkins build).

@HeikoKlare HeikoKlare merged commit cfbd282 into eclipse-platform:master Feb 1, 2025
11 of 14 checks passed
@HeikoKlare HeikoKlare deleted the edge_disposal_cme branch February 1, 2025 11:07
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.

Edge: ConcurrentModificationException during shutdown

2 participants