IJPL-189372 Assertion for detecting early access to Eel FS, especially WSL

Benchmarking results:

MacBook, M2 Max, JBR 21.0.7+9-b968.13 aarch64:

```
Benchmark                                           Mode  Cnt      Score      Error  Units
EelEarlyAccessFilterBenchmark.checkOnePathOneTime   avgt    5    133,187 ±   53,482  ns/op
EelEarlyAccessFilterBenchmark.checkOnePathTenTimes  avgt    5   1402,100 ±   61,042  ns/op
EelEarlyAccessFilterBenchmark.checkTenPathsOneTime  avgt    5  13621,454 ± 5928,938  ns/op
```

Windows, Intel i9-9900, JBR 21.0.7+6-b1020.35 amd64

```
Benchmark                                           Mode  Cnt     Score    Error  Units
EelEarlyAccessFilterBenchmark.checkOnePathOneTime   avgt    5    84,058 ±  4,160  ns/op
EelEarlyAccessFilterBenchmark.checkOnePathTenTimes  avgt    5   862,695 ± 22,599  ns/op
EelEarlyAccessFilterBenchmark.checkTenPathsOneTime  avgt    5  8921,268 ± 86,135  ns/op
```

GitOrigin-RevId: 95c5a8fda5259a53433d20ca46400299f7017c3f
This commit is contained in:
Vladimir Lagunov
2025-05-27 16:51:39 +00:00
committed by intellij-monorepo-bot
parent d9f09c472b
commit c27c65f5bb
10 changed files with 199 additions and 2 deletions
+1
View File
@@ -743,6 +743,7 @@
<module fileurl="file://$PROJECT_DIR$/platform/eel/intellij.platform.eel.iml" filepath="$PROJECT_DIR$/platform/eel/intellij.platform.eel.iml" />
<module fileurl="file://$PROJECT_DIR$/platform/eel/intellij.platform.eel.codegen.iml" filepath="$PROJECT_DIR$/platform/eel/intellij.platform.eel.codegen.iml" />
<module fileurl="file://$PROJECT_DIR$/platform/eel-impl/intellij.platform.eel.impl.iml" filepath="$PROJECT_DIR$/platform/eel-impl/intellij.platform.eel.impl.iml" />
<module fileurl="file://$PROJECT_DIR$/platform/eel-impl/intellij.platform.eel.impl.benchmarks.iml" filepath="$PROJECT_DIR$/platform/eel-impl/intellij.platform.eel.impl.benchmarks.iml" />
<module fileurl="file://$PROJECT_DIR$/platform/eel-provider/intellij.platform.eel.provider.iml" filepath="$PROJECT_DIR$/platform/eel-provider/intellij.platform.eel.provider.iml" />
<module fileurl="file://$PROJECT_DIR$/platform/eel/intellij.platform.eel.tests.iml" filepath="$PROJECT_DIR$/platform/eel/intellij.platform.eel.tests.iml" />
<module fileurl="file://$PROJECT_DIR$/platform/platform-tests/eel-helper/intellij.platform.eelHelper.iml" filepath="$PROJECT_DIR$/platform/platform-tests/eel-helper/intellij.platform.eelHelper.iml" />
@@ -147,7 +147,12 @@ public final class MultiRoutingFileSystem extends DelegatingFileSystem<MultiRout
@Override
protected @NotNull FileSystem getDelegate(@NotNull String root) {
return MultiRoutingFileSystemProvider.ourForceDefaultFs ? myLocalFS : getBackend(root);
if (MultiRoutingFileSystemProvider.ourForceDefaultFs) {
return myLocalFS;
}
FileSystem result = getBackend(root);
myProvider.myPathSpy.accept(root, result != myLocalFS);
return result;
}
@Override
@@ -17,7 +17,9 @@ import java.nio.file.Path;
import java.nio.file.spi.FileSystemProvider;
import java.nio.file.spi.FileTypeDetector;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
/**
* A file system that can delegate specific paths to other file systems.
@@ -53,6 +55,8 @@ public final class MultiRoutingFileSystemProvider
private final MultiRoutingFileSystem myFileSystem;
volatile BiConsumer<String, Boolean> myPathSpy = (ignored, ignored2) -> {};
/**
* Adds a new backend filesystem that handles requests to specific roots.
* <p>
@@ -100,6 +104,27 @@ public final class MultiRoutingFileSystemProvider
}
}
public static void setPathSpy(
@NotNull FileSystemProvider provider,
@NotNull BiConsumer<@NotNull String, @NotNull Boolean> pathSpy
) {
if (provider.getClass().getName().equals(MultiRoutingFileSystemProvider.class.getName())) {
try {
provider.getClass().getMethod("setPathSpy", BiConsumer.class).invoke(provider, pathSpy);
}
catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
else {
throw new IllegalArgumentException(String.format("%s is not an instance of %s", provider, MultiRoutingFileSystemProvider.class));
}
}
public void setPathSpy(@NotNull BiConsumer<String, Boolean> pathSpy) {
myPathSpy = pathSpy;
}
public MultiRoutingFileSystemProvider(FileSystemProvider localFSProvider) {
myLocalProvider = localFSProvider;
myFileSystem = new MultiRoutingFileSystem(this, myLocalProvider.getFileSystem(URI.create("file:///")));
@@ -176,6 +201,7 @@ public final class MultiRoutingFileSystemProvider
FileSystem backend1 = myFileSystem.getBackend(path1String);
FileSystemProvider provider1 = backend1.provider();
if (path2 == null) {
myPathSpy.accept(path1String, provider1 != this);
return provider1;
}
@@ -188,14 +214,17 @@ public final class MultiRoutingFileSystemProvider
FileSystemProvider provider2 = backend2.provider();
if (provider1.equals(provider2)) {
myPathSpy.accept(path1String, provider1 != this);
return provider1;
}
if (canHandleRouting(provider1, backend2.getPath(path2String))) {
myPathSpy.accept(path1String, provider1 != this);
return provider1;
}
if (canHandleRouting(provider2, backend1.getPath(path1String))) {
myPathSpy.accept(path2String, provider2 != this);
return provider2;
}
+22 -1
View File
@@ -30,4 +30,25 @@ jvm_library(
],
runtime_deps = [":eel-impl_resources"]
)
### auto-generated section `build intellij.platform.eel.impl` end
### auto-generated section `build intellij.platform.eel.impl` end
### auto-generated section `build intellij.platform.eel.impl.benchmarks` start
load("@rules_java//java:defs.bzl", "java_library")
load("@rules_jvm//:jvm.bzl", "jvm_library", "jvm_test")
jvm_library(
name = "eel-impl-benchmarks_test_lib",
visibility = ["//visibility:public"],
srcs = glob(["benchmarks/**/*.kt", "benchmarks/**/*.java"], allow_empty = True),
deps = [
"@lib//:jmh-core",
"@lib//:jmh-generator-annprocess",
"//platform/eel-impl",
]
)
jvm_test(
name = "eel-impl-benchmarks_test",
runtime_deps = [":eel-impl-benchmarks_test_lib"]
)
### auto-generated section `build intellij.platform.eel.impl.benchmarks` end
@@ -0,0 +1,12 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.eel.impl.fs;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.OptionsBuilder;
public final class BenchmarkLauncher {
public static void main(String[] args) throws RunnerException {
new Runner(new OptionsBuilder().build()).run();
}
}
@@ -0,0 +1,52 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.eel.impl.fs;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import java.math.BigInteger;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(1)
@Threads(4)
@Warmup(iterations = 0) // This code affects users since the beginning of the application. Users don't warm up our products.
public class EelEarlyAccessFilterBenchmark {
@State(Scope.Benchmark)
public static class BenchmarkState {
final EelEarlyAccessFilter filter = new EelEarlyAccessFilter();
final String[] pathFixtures;
public BenchmarkState() {
pathFixtures = new String[10];
Random random = new Random(31337);
for (int i = 0; i < pathFixtures.length; i++) {
byte[] bytes = new byte[random.nextInt(1023) + 1];
pathFixtures[i] = new BigInteger(bytes).toString(64);
}
}
}
@Benchmark
public void checkOnePathOneTime(BenchmarkState state, Blackhole blackhole) {
blackhole.consume(state.filter.check(state.pathFixtures[0]));
}
@Benchmark
public void checkOnePathTenTimes(BenchmarkState state, Blackhole blackhole) {
for (int ignored = 0; ignored < 10; ignored++) {
blackhole.consume(state.filter.check(state.pathFixtures[0]));
}
}
@Benchmark
public void checkTenPathsOneTime(BenchmarkState state, Blackhole blackhole) {
for (int ignored = 0; ignored < 10; ignored++) {
for (int i = 0; i < 10; i++) {
blackhole.consume(state.filter.check(state.pathFixtures[i]));
}
}
}
}
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$/benchmarks">
<sourceFolder url="file://$MODULE_DIR$/benchmarks" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" scope="TEST" name="jmh-core" level="project" />
<orderEntry type="library" scope="TEST" name="jmh-generator-annprocess" level="project" />
<orderEntry type="module" module-name="intellij.platform.eel.impl" scope="TEST" />
</component>
</module>
@@ -6,5 +6,7 @@
serviceImplementation="com.intellij.platform.eel.impl.local.LocalWindowsEelApiImpl"/>
<applicationService serviceInterface="com.intellij.platform.eel.provider.LocalPosixEelApi"
serviceImplementation="com.intellij.platform.eel.impl.local.LocalPosixEelApiImpl"/>
<applicationActivity implementation="com.intellij.platform.eel.impl.fs.EelEarlyAccessApplicationActivity"/>
</extensions>
</idea-plugin>
@@ -0,0 +1,60 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.eel.impl.fs
import com.intellij.ide.ApplicationActivity
import com.intellij.openapi.diagnostic.Attachment
import com.intellij.openapi.diagnostic.logger
import com.intellij.platform.core.nio.fs.MultiRoutingFileSystem
import com.intellij.platform.core.nio.fs.MultiRoutingFileSystemProvider
import org.jetbrains.annotations.VisibleForTesting
import java.lang.invoke.MethodHandles
import java.nio.file.FileSystems
import java.util.function.BiConsumer
internal class EelEarlyAccessApplicationActivity : ApplicationActivity {
override suspend fun execute() {
val fs = FileSystems.getDefault()
if (fs.javaClass.name != MultiRoutingFileSystem::class.java.name) return
val logger = logger<EelEarlyAccessApplicationActivity>()
val filter = EelEarlyAccessFilter()
MultiRoutingFileSystemProvider.setPathSpy(fs.provider(), BiConsumer<String, Boolean> { strPath, isDefaultProvider ->
if (isDefaultProvider && strPath.startsWith("\\\\wsl")) {
if (filter.check(strPath)) {
// Q: What happened?
// A: Some code tried to access \\wsl.localhost before the initialization of the IJent file system.
// Therefore, the code will access the original filesystem of WSL brought by Microsoft,
// and the code won't access our polished file system with various workarounds, optimizations, etc.
// Also, the code that triggered this error is an obstacle for implementing DevContainers over Eel.
//
// Q: How to fix it?
// A: Call `com.intellij.platform.eel.provider.EelInitialization.runEelInitialization` in advance.
// Sometimes it's easy, sometimes you have to rework the UI/UX, sorry for that.
//
// Q: Why not initialize the IJent filesystem lazily, right here, at this moment?
// A: Although this initialization usually takes a second, sometimes it can be significantly slower.
// It depends not only on the I/O speed of the machine, but also on personal configurations of the user.
// Being called from EDT, this lazy initialization would lead to yet another freeze.
logger.error(
"Remote file system accessed before Eel initialization. The description is in the source code.",
Attachment("path", strPath.toString()),
)
}
}
})
}
}
@VisibleForTesting
class EelEarlyAccessFilter {
private val poorMansBloomFilter = Array(1 shl 16) { true }
private val varHandle = MethodHandles.arrayElementVarHandle(poorMansBloomFilter::class.java)
fun check(strPath: String): Boolean {
val hash = strPath.hashCode().let {
(it and 0xFFFF) xor (it ushr 16 and 0xFFFF)
}
return varHandle.getAndSet(poorMansBloomFilter, hash, false) as Boolean
}
}
@@ -281,6 +281,7 @@ private val asyncAppListenerAllowListForNonCorePlugin = java.util.Set.of(
"com.intellij.internal.statistic.updater.StatisticsJobsScheduler",
"com.intellij.internal.statistic.updater.StatisticsStateCollectorsScheduler",
"org.jetbrains.kotlin.idea.base.plugin.K2UnsupportedPluginsNotificationActivity",
"com.intellij.platform.eel.impl.fs.EelEarlyAccessApplicationActivity",
)
private fun CoroutineScope.executeAsyncAppInitListeners() {