cleanup
[idea/community.git] / platform / platform-impl / src / com / intellij / openapi / vfs / DiskQueryRelay.java
1 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.openapi.vfs;
3
4 import com.intellij.openapi.application.ApplicationManager;
5 import com.intellij.openapi.progress.ProgressIndicatorProvider;
6 import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
7 import org.jetbrains.annotations.ApiStatus;
8 import org.jetbrains.annotations.NotNull;
9
10 import java.util.Map;
11 import java.util.concurrent.ConcurrentHashMap;
12 import java.util.concurrent.Future;
13 import java.util.function.Function;
14
15 /**
16  * A utility to run a potentially long function on a pooled thread, wait for it in an interruptible way and reuse that computation if it's
17  * needed again if it's still running. Function results should be ready for concurrent access, preferably thread-safe.
18  */
19 @ApiStatus.Internal
20 public final class DiskQueryRelay<Param, Result> {
21   private final @NotNull Function<? super Param, ? extends Result> myFunction;
22
23   /**
24    * We remember the submitted tasks in "myTasks" until they're finished, to avoid creating many-many similar threads
25    * in case the callee is interrupted by "checkCanceled", restarted, comes again with the same query, is interrupted again, and so on.
26    */
27   private final Map<Param, Future<Result>> myTasks = new ConcurrentHashMap<>();
28
29   public DiskQueryRelay(@NotNull Function<? super Param, ? extends Result> function) {
30     myFunction = function;
31   }
32
33   public Result accessDiskWithCheckCanceled(@NotNull Param arg) {
34     if (ProgressIndicatorProvider.getGlobalProgressIndicator() == null) {
35       return myFunction.apply(arg);
36     }
37
38     Future<Result> future = myTasks.computeIfAbsent(arg, eachArg -> ApplicationManager.getApplication().executeOnPooledThread(() -> {
39       try {
40         return myFunction.apply(eachArg);
41       }
42       finally {
43         myTasks.remove(eachArg);
44       }
45     }));
46     if (future.isDone()) {
47       // maybe it was very fast and completed before being put into a map
48       myTasks.remove(arg, future);
49     }
50     return ProgressIndicatorUtils.awaitWithCheckCanceled(future);
51   }
52 }