From a200e0097fec02b53bb3f17a52cef92fafa8f1e8 Mon Sep 17 00:00:00 2001 From: Svata Dedic Date: Mon, 11 Dec 2023 12:19:45 +0100 Subject: [PATCH] Use future and do not block if reloading. --- .../modules/maven/NbMavenProjectImpl.java | 13 +++-- .../problems/MavenModelProblemsProvider.java | 57 +++++++++++++++---- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/java/maven/src/org/netbeans/modules/maven/NbMavenProjectImpl.java b/java/maven/src/org/netbeans/modules/maven/NbMavenProjectImpl.java index 4fa076bc0c37..298b3505d1ab 100644 --- a/java/maven/src/org/netbeans/modules/maven/NbMavenProjectImpl.java +++ b/java/maven/src/org/netbeans/modules/maven/NbMavenProjectImpl.java @@ -44,6 +44,7 @@ import java.util.Properties; import java.util.Set; import java.util.WeakHashMap; +import java.util.concurrent.CompletableFuture; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -472,13 +473,17 @@ public MavenProject getOriginalMavenProjectOrNull() { * the method blocks until the project reload eventually finishes in the reload thread / RP. * @return possibly reloaded Maven project. */ - public MavenProject getFreshOriginalMavenProject() { + public CompletableFuture getFreshOriginalMavenProject() { if (reloadTask.isFinished()) { - return getOriginalMavenProject(); + return CompletableFuture.completedFuture(getOriginalMavenProject()); } else { LOG.log(Level.FINE, "Asked for project {0} being updated, waiting for the refresh to complete.", projectFile); - reloadTask.waitFinished(); - return getOriginalMavenProject(); + CompletableFuture f = new CompletableFuture<>(); + reloadTask.addTaskListener((e) -> { + LOG.log(Level.FINE, "Project {0} update done.", projectFile); + f.complete(getOriginalMavenProject()); + }); + return f; } } diff --git a/java/maven/src/org/netbeans/modules/maven/problems/MavenModelProblemsProvider.java b/java/maven/src/org/netbeans/modules/maven/problems/MavenModelProblemsProvider.java index b842285ff812..0d6512a3d731 100644 --- a/java/maven/src/org/netbeans/modules/maven/problems/MavenModelProblemsProvider.java +++ b/java/maven/src/org/netbeans/modules/maven/problems/MavenModelProblemsProvider.java @@ -32,6 +32,7 @@ import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; @@ -162,7 +163,7 @@ Collection doGetProblems(boolean sync) { * @param sync if the call should complete synchronously */ private Pair, Boolean> doGetProblems1(boolean sync) { - final MavenProject updatedPrj = ((NbMavenProjectImpl)project).getFreshOriginalMavenProject(); + final CompletableFuture pending = ((NbMavenProjectImpl)project).getFreshOriginalMavenProject(); Callable, Boolean>> c; synchronized (this) { @@ -176,25 +177,50 @@ private Pair, Boolean> doGetProblems1(boolean sync) { } MavenProject o = analysedProject.get(); - LOG.log(Level.FINER, "Called getProblems for {0}, analysed = {1}, current = {2}", - new Object[] { project, o == null ? 0 : System.identityHashCode(o), System.identityHashCode(updatedPrj) }); //for non changed project models, no need to recalculate, always return the cached value - Object wasprocessed = updatedPrj.getContextValue(MavenModelProblemsProvider.class.getName()); - if (o == updatedPrj && wasprocessed != null) { - Pair, Boolean> cached = problemsCache; - LOG.log(Level.FINER, "getProblems: Project was processed, cached is: {0}", cached); - if (cached != null) { - return cached; + if (pending.isDone()) { + try { + // cannot block, if .isDone(). + MavenProject updatedPrj = pending.get(); + LOG.log(Level.FINER, "Called getProblems for {0}, analysed = {1}, current = {2}", + new Object[] { project, o == null ? 0 : System.identityHashCode(o), System.identityHashCode(updatedPrj) }); + Object wasprocessed = updatedPrj.getContextValue(MavenModelProblemsProvider.class.getName()); + if (o == updatedPrj && wasprocessed != null) { + Pair, Boolean> cached = problemsCache; + LOG.log(Level.FINER, "getProblems: Project was processed, cached is: {0}", cached); + if (cached != null) { + return cached; + } + } + } catch (ExecutionException | InterruptedException ex) { + LOG.log(Level.FINER, "Project load for {0} threw exception {1}", new Object[] { project, ex.getMessage() }); + LOG.log(Level.FINER, "Stacktrace:", ex); } - } + } else { + LOG.log(Level.FINER, "Called getProblems for {0}, analysed = {1}, current = PENDING", + new Object[] { project, o == null ? 0 : System.identityHashCode(o) }); + } SanityBuildAction sba = cachedSanityBuild.get(); if (sba != null && sba.getPendingResult() == null) { cachedSanityBuild.clear(); } + + // PENDING: think if .thenApplyAsync would be more useful. c = () -> { // double check, the project may be invalidated during the time. - MavenProject prj = ((NbMavenProjectImpl)project).getFreshOriginalMavenProject(); + MavenProject prj; + + try { + prj = ((NbMavenProjectImpl)project).getFreshOriginalMavenProject().get(); + LOG.log(Level.FINER, "Evaluating getProblems for {0}, analysed = {1}, current = {2}", + new Object[] { project, o == null ? 0 : System.identityHashCode(o), System.identityHashCode(prj) }); + } catch (ExecutionException | InterruptedException ex) { + // should not happen + LOG.log(Level.FINER, "Project load for {0} threw exception {1}", new Object[] { project, ex.getMessage() }); + LOG.log(Level.FINER, "Stacktrace:", ex); + return Pair.of( new ArrayList<>(), sanityBuildStatus); + } Object wasprocessed2 = prj.getContextValue(MavenModelProblemsProvider.class.getName()); synchronized (MavenModelProblemsProvider.this) { if (wasprocessed2 != null) { @@ -244,7 +270,14 @@ private Pair, Boolean> doGetProblems1(boolean sync) { new Object[] { round, prj }); // force reload, then wait for the reload to complete NbMavenProject.fireMavenProjectReload(project); - prj = ((NbMavenProjectImpl)project).getFreshOriginalMavenProject(); + try { + prj = ((NbMavenProjectImpl)project).getFreshOriginalMavenProject().get(); + } catch (ExecutionException | InterruptedException ex2) { + // should not happen + LOG.log(Level.FINER, "Project load for {0} threw exception {1}", new Object[] { project, ex2.getMessage() }); + LOG.log(Level.FINER, "Stacktrace:", ex2); + break; + } } } if (prj != null && !sanityBuildStatus) {