mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
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:
committed by
intellij-monorepo-bot
parent
d9f09c472b
commit
c27c65f5bb
Generated
+1
@@ -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" />
|
||||
|
||||
+6
-1
@@ -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
|
||||
|
||||
+29
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
+52
@@ -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>
|
||||
+60
@@ -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
|
||||
}
|
||||
}
|
||||
+1
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user