From e2c571419d263b8e4d1da1629ad0c28094a0ccd9 Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Wed, 3 Apr 2024 00:39:31 +0200 Subject: [PATCH] fixed race conditions, crudely but effectively. There must be a faster solution using a concurrent list implementation --- .../repl/TerminalProgressBarMonitor.java | 28 +++--- ...ascalJUnitParallelRecursiveTestRunner.java | 86 +++++++++++-------- 2 files changed, 65 insertions(+), 49 deletions(-) diff --git a/src/org/rascalmpl/repl/TerminalProgressBarMonitor.java b/src/org/rascalmpl/repl/TerminalProgressBarMonitor.java index 4d1cce9bd5f..700c8af53ba 100644 --- a/src/org/rascalmpl/repl/TerminalProgressBarMonitor.java +++ b/src/org/rascalmpl/repl/TerminalProgressBarMonitor.java @@ -61,12 +61,13 @@ public TerminalProgressBarMonitor(OutputStream out, Terminal tm) { * Represents one currently running progress bar */ private class ProgressBar { + private final long threadId; private final String name; private int max; private int current = 0; private int previousWidth = 0; private int doneWidth = 0; - private int barWidth = lineWidth - "☐ ".length() - " 🕐 00:00:00.000 ".length(); + private final int barWidth = lineWidth - "☐ ".length() - " 🕐 00:00:00.000 ".length(); private final Instant startTime; private Duration duration; private String message = ""; @@ -74,6 +75,7 @@ private class ProgressBar { private final String[] clocks = new String[] {"🕐" , "🕑", "🕒", "🕓", "🕔", "🕕", "🕖", "🕗", "🕘", "🕙", "🕛"}; ProgressBar(String name, int max) { + this.threadId = Thread.currentThread().getId(); this.name = name; this.max = max; this.startTime = Instant.now(); @@ -114,7 +116,6 @@ int newWidth() { void write() { previousWidth = doneWidth; doneWidth = newWidth(); - // var overWidth = barWidth - doneWidth; var done = current >= max ? "☑ " : "☐ "; @@ -156,12 +157,15 @@ else if (barWidth <= 3) { // we can print the clock for good measure @Override public boolean equals(Object obj) { - return obj instanceof ProgressBar && ((ProgressBar) obj).name.equals(name); + return obj instanceof ProgressBar + && ((ProgressBar) obj).name.equals(name) + && ((ProgressBar) obj).threadId == threadId + ; } @Override public int hashCode() { - return name.hashCode(); + return name.hashCode() + 31 * (int) threadId; } public void done() { @@ -243,11 +247,13 @@ private void printBars() { * @return the current instance by that name */ private ProgressBar findBarByName(String name) { - return bars.stream().filter(b -> b.name.equals(name)).findFirst().orElseGet(() -> null); + return bars.stream() + .filter(b -> b.threadId == Thread.currentThread().getId()) + .filter(b -> b.name.equals(name)).findFirst().orElseGet(() -> null); } @Override - public void jobStart(String name, int workShare, int totalWork) { + public synchronized void jobStart(String name, int workShare, int totalWork) { var pb = findBarByName(name); eraseBars(); // to make room for the new bars @@ -264,7 +270,7 @@ public void jobStart(String name, int workShare, int totalWork) { } @Override - public void jobStep(String name, String message, int workShare) { + public synchronized void jobStep(String name, String message, int workShare) { ProgressBar pb = findBarByName(name); if (pb != null) { @@ -274,7 +280,7 @@ public void jobStep(String name, String message, int workShare) { } @Override - public int jobEnd(String name, boolean succeeded) { + public synchronized int jobEnd(String name, boolean succeeded) { var pb = findBarByName(name); if (pb != null) { @@ -292,13 +298,13 @@ public int jobEnd(String name, boolean succeeded) { } @Override - public boolean jobIsCanceled(String name) { + public synchronized boolean jobIsCanceled(String name) { // ? don't know what this should do return false; } @Override - public void jobTodo(String name, int work) { + public synchronized void jobTodo(String name, int work) { ProgressBar pb = findBarByName(name); if (pb != null) { @@ -308,7 +314,7 @@ public void jobTodo(String name, int work) { } @Override - public void warning(String message, ISourceLocation src) { + public synchronized void warning(String message, ISourceLocation src) { eraseBars(); writer.println(("[WARNING] " + src + ": " + message)); printBars(); diff --git a/src/org/rascalmpl/test/infrastructure/RascalJUnitParallelRecursiveTestRunner.java b/src/org/rascalmpl/test/infrastructure/RascalJUnitParallelRecursiveTestRunner.java index f45d313d38c..1316e552cea 100644 --- a/src/org/rascalmpl/test/infrastructure/RascalJUnitParallelRecursiveTestRunner.java +++ b/src/org/rascalmpl/test/infrastructure/RascalJUnitParallelRecursiveTestRunner.java @@ -241,50 +241,60 @@ private boolean waitForRunSignal() { } private void processModules() { - String module; - try { - while ((module = modules.poll()) != null) { - try { - evaluator.doImport(new NullRascalMonitor(), module); - } - catch (Throwable e) { - synchronized(stdout) { - evaluator.warning("Could not import " + module + " for testing...", null); - evaluator.warning(e.getMessage(), null); - e.printStackTrace(evaluator.getOutPrinter()); - } - - // register a failing module to make sure we report failure later on. - - Description testDesc = Description.createTestDescription(getClass(), module, new CompilationFailed() { - @Override - public Class annotationType() { - return getClass(); - } - }); + evaluator.job("Importing test modules", 1, (jn) -> { + try { + String module; + + while ((module = modules.poll()) != null) { + evaluator.jobTodo(jn, 1); + + try { + evaluator.doImport(new NullRascalMonitor(), module); + } + catch (Throwable e) { + synchronized(stdout) { + evaluator.warning("Could not import " + module + " for testing...", null); + evaluator.warning(e.getMessage(), null); + e.printStackTrace(evaluator.getOutPrinter()); + } + + // register a failing module to make sure we report failure later on. + + Description testDesc = Description.createTestDescription(getClass(), module, new CompilationFailed() { + @Override + public Class annotationType() { + return getClass(); + } + }); - testModules.add(testDesc); - descriptions.add(testDesc); + testModules.add(testDesc); + descriptions.add(testDesc); - continue; - } + continue; + } + finally { + evaluator.jobStep(jn, module); + } - ModuleEnvironment moduleEnv = heap.getModule(module.replaceAll("\\\\","")); - if (!moduleEnv.getTests().isEmpty()) { - Description modDesc = Description.createSuiteDescription(module); - for (AbstractFunction f : moduleEnv.getTests()) { - modDesc.addChild(Description.createTestDescription(getClass(), RascalJUnitTestRunner.computeTestName(f.getName(), f.getAst().getLocation()))); + ModuleEnvironment moduleEnv = heap.getModule(module.replaceAll("\\\\","")); + if (!moduleEnv.getTests().isEmpty()) { + Description modDesc = Description.createSuiteDescription(module); + for (AbstractFunction f : moduleEnv.getTests()) { + modDesc.addChild(Description.createTestDescription(getClass(), RascalJUnitTestRunner.computeTestName(f.getName(), f.getAst().getLocation()))); + } + descriptions.add(modDesc); + testModules.add(modDesc); } - descriptions.add(modDesc); - testModules.add(modDesc); } + // let's shuffle them + Collections.shuffle(testModules); } - // let's shuffle them - Collections.shuffle(testModules); - } - finally { - importsCompleted.release(); - } + finally { + importsCompleted.release(); + } + + return true; + }); } private void initializeEvaluator() {