From a03abe4588931b8ccfcac70dbf532dfb39d4705d Mon Sep 17 00:00:00 2001 From: Nathaniel Bauernfeind Date: Tue, 5 Mar 2024 10:58:34 -0700 Subject: [PATCH] Corey's comments --- .../src/main/java/io/deephaven/base/Lazy.java | 22 -- .../io/deephaven/util/MultiException.java | 2 +- .../util/datastructures/CachingSupplier.java | 12 +- .../engine/context/QueryCompiler.java | 201 +++++++++--------- .../impl/QueryCompilerRequestProcessor.java | 28 ++- .../impl/select/AbstractFormulaColumn.java | 3 +- .../analyzers/SelectAndViewAnalyzer.java | 4 +- open-api/lang-parser/lang-parser.gradle | 1 + .../deephaven/lang/parse/ParsedDocument.java | 6 +- .../lang/completion/CompletionLookups.java | 10 +- 10 files changed, 149 insertions(+), 140 deletions(-) delete mode 100644 Base/src/main/java/io/deephaven/base/Lazy.java diff --git a/Base/src/main/java/io/deephaven/base/Lazy.java b/Base/src/main/java/io/deephaven/base/Lazy.java deleted file mode 100644 index db280fab6e4..00000000000 --- a/Base/src/main/java/io/deephaven/base/Lazy.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending - */ -package io.deephaven.base; - -import java.util.function.Supplier; - -public class Lazy implements Supplier { - private Supplier supplier; - - public Lazy(Supplier x) { - supplier = () -> { - T val = x.get(); - supplier = () -> val; - return val; - }; - } - - public synchronized T get() { - return supplier.get(); - } -}; diff --git a/Util/src/main/java/io/deephaven/util/MultiException.java b/Util/src/main/java/io/deephaven/util/MultiException.java index 5bf7e86649c..6cf51efc62d 100644 --- a/Util/src/main/java/io/deephaven/util/MultiException.java +++ b/Util/src/main/java/io/deephaven/util/MultiException.java @@ -8,7 +8,7 @@ import java.util.List; /** - * An exception to use when a series of operations must all be executed, but may all throw exceptions themselves. This + * An exception to use when a series of operations mus all be executed, but may all throw exceptions themselves. This * allows for retention of all exception data. */ public class MultiException extends Exception { diff --git a/Util/src/main/java/io/deephaven/util/datastructures/CachingSupplier.java b/Util/src/main/java/io/deephaven/util/datastructures/CachingSupplier.java index 43ff488da7e..62a60707d4c 100644 --- a/Util/src/main/java/io/deephaven/util/datastructures/CachingSupplier.java +++ b/Util/src/main/java/io/deephaven/util/datastructures/CachingSupplier.java @@ -17,7 +17,8 @@ public final class CachingSupplier implements Supplier private final Supplier internalSupplier; private volatile boolean hasCachedResult; - private volatile OUTPUT_TYPE cachedResult; + private OUTPUT_TYPE cachedResult; + private RuntimeException errorResult; /** * Construct a {@link Supplier} wrapper. @@ -33,12 +34,19 @@ public OUTPUT_TYPE get() { if (!hasCachedResult) { synchronized (this) { if (!hasCachedResult) { - cachedResult = internalSupplier.get(); + try { + cachedResult = internalSupplier.get(); + } catch (RuntimeException err) { + errorResult = err; + } hasCachedResult = true; } } } + if (errorResult != null) { + throw errorResult; + } return cachedResult; } } diff --git a/engine/context/src/main/java/io/deephaven/engine/context/QueryCompiler.java b/engine/context/src/main/java/io/deephaven/engine/context/QueryCompiler.java index c1c07de9008..f1f824bcd43 100644 --- a/engine/context/src/main/java/io/deephaven/engine/context/QueryCompiler.java +++ b/engine/context/src/main/java/io/deephaven/engine/context/QueryCompiler.java @@ -274,64 +274,33 @@ public void compile( } } - /* - * @formatter:off - * 3. try to resolve CFs without compiling; retain next hash to try - * 4. compile all remaining with a single compilation task - * 5. goto step 3 - * 6. probably need Consumer> to fit DhFormulaColumn pattern? (other select columns don't need this) - * @formatter:on - */ - if (!newResolvers.isEmpty()) { - // It's my job to fulfill the future of these futures. + // It's my job to fulfill these futures. try { compileHelper(newRequests, newResolvers); } catch (RuntimeException e) { // These failures are not applicable to a single request, so we can't just complete the future and // leave the failure in the cache. - for (int ii = 0; ii < newRequests.size(); ++ii) { - if (newResolvers.get(ii).completeExceptionally(e)) { - knownClasses.remove(newRequests.get(ii).classBody()); + synchronized (this) { + for (int ii = 0; ii < newRequests.size(); ++ii) { + if (newResolvers.get(ii).completeExceptionally(e)) { + knownClasses.remove(newRequests.get(ii).classBody()); + } } } throw e; } } - Error firstError = null; - RuntimeException firstException = null; for (int ii = 0; ii < requests.length; ++ii) { try { resolvers[ii].complete(allFutures[ii].get()); - } catch (InterruptedException | ExecutionException e) { - final RuntimeException err; - if (e instanceof InterruptedException) { - err = new UncheckedDeephavenException("Interrupted while waiting for codegen", e); - } else { - Throwable cause = e.getCause(); - if (cause instanceof Error) { - firstError = (Error) cause; - resolvers[ii].completeExceptionally(cause); - continue; - } else if (cause instanceof RuntimeException) { - err = (RuntimeException) cause; - } else { - err = new UncheckedDeephavenException("Error during codegen", e); - } - } - if (firstException == null) { - firstException = err; - } + } catch (ExecutionException err) { + resolvers[ii].completeExceptionally(err.getCause()); + } catch (Throwable err) { resolvers[ii].completeExceptionally(err); } } - if (firstError != null) { - throw firstError; - } - if (firstException != null) { - throw firstException; - } } private static void ensureDirectories(final File file, final Supplier runtimeErrMsg) { @@ -483,6 +452,13 @@ private String getClassPath() { return sb.toString(); } + private static class CompilationState { + int next_pi; + boolean compiled; + String packageName; + String fqClassName; + } + private void compileHelper( @NotNull final List requests, @NotNull final List>> resolvers) { @@ -500,19 +476,28 @@ private void compileHelper( } int numCompiled = 0; - final int[] next_pi = new int[requests.size()]; - final boolean[] compiled = new boolean[requests.size()]; - final String[] packageName = new String[requests.size()]; - final String[] fqClassName = new String[requests.size()]; + final CompilationState[] states = new CompilationState[requests.size()]; + for (int ii = 0; ii < requests.size(); ++ii) { + states[ii] = new CompilationState(); + } + + /* + * @formatter:off + * 1. try to resolve CFs without compiling; retain next hash to try + * 2. compile all remaining with a single compilation task + * 3. goto step 1 if any are unresolved + * @formatter:on + */ while (numCompiled < requests.size()) { for (int ii = 0; ii < requests.size(); ++ii) { - if (compiled[ii]) { + final CompilationState state = states[ii]; + if (state.compiled) { continue; } while (true) { - final int pi = next_pi[ii]++; + final int pi = state.next_pi++; final String packageNameSuffix = "c_" + basicHashText[ii] + (pi == 0 ? "" : ("p" + pi)) + "v" + JAVA_CLASS_VERSION; @@ -523,26 +508,26 @@ private void compileHelper( + request.packageNameRoot() + ", class name=" + request.className() + ", class body " + "hash=" + basicHashText[ii] + " - contact Deephaven support!"); resolvers.get(ii).completeExceptionally(err); - compiled[ii] = true; + state.compiled = true; ++numCompiled; break; } - packageName[ii] = request.getPackageName(packageNameSuffix); - fqClassName[ii] = packageName[ii] + "." + request.className(); + state.packageName = request.getPackageName(packageNameSuffix); + state.fqClassName = state.packageName + "." + request.className(); // Ask the classloader to load an existing class with this name. This might: // 1. Fail to find a class (returning null) // 2. Find a class whose body has the formula we are looking for // 3. Find a class whose body has a different formula (hash collision) - Class result = tryLoadClassByFqName(fqClassName[ii], request.parameterClasses()); + Class result = tryLoadClassByFqName(state.fqClassName, request.parameterClasses()); if (result == null) { break; // we'll try to compile it } - if (completeIfResultMatchesQueryCompilerRequest(packageName[ii], request, resolvers.get(ii), + if (completeIfResultMatchesQueryCompilerRequest(state.packageName, request, resolvers.get(ii), result)) { - compiled[ii] = true; + state.compiled = true; ++numCompiled; break; } @@ -554,16 +539,16 @@ private void compileHelper( } // Couldn't resolve at least one of the requests, so try a round of compilation. - final CompilationRequestAttempt[] compilationRequestAttempts = - new CompilationRequestAttempt[requests.size() - numCompiled]; - for (int ii = 0, jj = 0; ii < requests.size(); ++ii) { - if (!compiled[ii]) { + final List compilationRequestAttempts = new ArrayList<>(); + for (int ii = 0; ii < requests.size(); ++ii) { + final CompilationState state = states[ii]; + if (!state.compiled) { final QueryCompilerRequest request = requests.get(ii); - compilationRequestAttempts[jj++] = new CompilationRequestAttempt( + compilationRequestAttempts.add(new CompilationRequestAttempt( request, - packageName[ii], - fqClassName[ii], - resolvers.get(ii)); + state.packageName, + state.fqClassName, + resolvers.get(ii))); } } @@ -573,14 +558,15 @@ private void compileHelper( // ... then give the filesystem some time. All requests should use the same deadline. final long deadline = System.currentTimeMillis() + CODEGEN_TIMEOUT_MS - CODEGEN_LOOP_DELAY_MS; for (int ii = 0; ii < requests.size(); ++ii) { - if (compiled[ii]) { + final CompilationState state = states[ii]; + if (state.compiled) { continue; } final QueryCompilerRequest request = requests.get(ii); final CompletionStageFuture.Resolver> resolver = resolvers.get(ii); if (resolver.getFuture().isDone()) { - compiled[ii] = true; + state.compiled = true; ++numCompiled; continue; } @@ -590,12 +576,12 @@ private void compileHelper( // B. Lost a race to another process on the same file system which is compiling the identical formula // C. Lost a race to another process on the same file system compiling a different formula that collides - Class clazz = tryLoadClassByFqName(fqClassName[ii], request.parameterClasses()); + Class clazz = tryLoadClassByFqName(state.fqClassName, request.parameterClasses()); try { while (clazz == null && System.currentTimeMillis() < deadline) { // noinspection BusyWait Thread.sleep(CODEGEN_LOOP_DELAY_MS); - clazz = tryLoadClassByFqName(fqClassName[ii], request.parameterClasses()); + clazz = tryLoadClassByFqName(state.fqClassName, request.parameterClasses()); } } catch (final InterruptedException ie) { throw new UncheckedDeephavenException("Interrupted while waiting for codegen", ie); @@ -606,8 +592,8 @@ private void compileHelper( throw new IllegalStateException("Should have been able to load *some* class here"); } - if (completeIfResultMatchesQueryCompilerRequest(packageName[ii], request, resolver, clazz)) { - compiled[ii] = true; + if (completeIfResultMatchesQueryCompilerRequest(state.packageName, request, resolver, clazz)) { + state.compiled = true; ++numCompiled; } } @@ -805,7 +791,7 @@ public JavaSourceFromString makeSource() { } private void maybeCreateClass( - @NotNull final CompilationRequestAttempt[] requests) { + @NotNull final List requests) { // Get the destination root directory (e.g. /tmp/workspace/cache/classes) and populate it with the package // directories (e.g. io/deephaven/test) if they are not already there. This will be useful later. // Also create a temp directory e.g. /tmp/workspace/cache/classes/temporaryCompilationDirectory12345 @@ -848,21 +834,21 @@ private void maybeCreateClass( final OperationInitializer operationInitializer = ExecutionContext.getContext().getOperationInitializer(); int parallelismFactor = operationInitializer.parallelismFactor(); - int requestsPerTask = Math.max(32, (requests.length + parallelismFactor - 1) / parallelismFactor); + int requestsPerTask = Math.max(32, (requests.size() + parallelismFactor - 1) / parallelismFactor); log.info().append("Compiling with parallelismFactor = ").append(parallelismFactor) .append(" requestsPerTask = ").append(requestsPerTask).endl(); - if (parallelismFactor == 1 || requestsPerTask >= requests.length) { + if (parallelismFactor == 1 || requestsPerTask >= requests.size()) { maybeCreateClassHelper(compiler, fileManager, requests, rootPathAsString, tempDirAsString, - 0, requests.length, false); + 0, requests.size()); } else { - int numTasks = (requests.length + requestsPerTask - 1) / requestsPerTask; + int numTasks = (requests.size() + requestsPerTask - 1) / requestsPerTask; final Future[] tasks = new Future[numTasks]; for (int jobId = 0; jobId < numTasks; ++jobId) { final int startInclusive = jobId * requestsPerTask; - final int endExclusive = Math.min(requests.length, (jobId + 1) * requestsPerTask); + final int endExclusive = Math.min(requests.size(), (jobId + 1) * requestsPerTask); tasks[jobId] = operationInitializer.submit(() -> { maybeCreateClassHelper(compiler, fileManager, requests, rootPathAsString, tempDirAsString, - startInclusive, endExclusive, false); + startInclusive, endExclusive); }); } for (int jobId = 0; jobId < numTasks; ++jobId) { @@ -897,12 +883,36 @@ private void maybeCreateClass( private void maybeCreateClassHelper( @NotNull final JavaCompiler compiler, @NotNull final JavaFileManager fileManager, - @NotNull final CompilationRequestAttempt[] requests, + @NotNull final List requests, + @NotNull final String rootPathAsString, + @NotNull final String tempDirAsString, + final int startInclusive, + final int endExclusive) { + final List toRetry = new ArrayList<>(); + final boolean wantRetry = maybeCreateClassHelper2(compiler, + fileManager, requests, rootPathAsString, tempDirAsString, startInclusive, endExclusive, toRetry); + if (!wantRetry) { + return; + } + + final List ignored = new ArrayList<>(); + if (maybeCreateClassHelper2(compiler, + fileManager, toRetry, rootPathAsString, tempDirAsString, 0, toRetry.size(), ignored)) { + // We only retried compilation units that did not fail on the first pass, so we should not have any failures + // on the second pass. + throw new IllegalStateException("Unexpected failure during second pass of compilation"); + } + } + + private boolean maybeCreateClassHelper2( + @NotNull final JavaCompiler compiler, + @NotNull final JavaFileManager fileManager, + @NotNull final List requests, @NotNull final String rootPathAsString, @NotNull final String tempDirAsString, final int startInclusive, final int endExclusive, - final boolean isRetry) { + List toRetry) { final StringWriter compilerOutput = new StringWriter(); final String classPathAsString = getClassPath() + File.pathSeparator + getJavaClassPath(); @@ -930,51 +940,48 @@ private void maybeCreateClassHelper( }, compilerOptions, null, - Arrays.stream(requests, startInclusive, endExclusive) + requests.subList(startInclusive, endExclusive).stream() .map(CompilationRequestAttempt::makeSource) .collect(Collectors.toList())) .call(); - final List shouldRetry; - if (!isRetry && numFailures.intValue() > 0 && numFailures.intValue() != endExclusive - startInclusive) { - // if this is the first attempt, and we had some failures, but not all of them failed, then we should retry - shouldRetry = new ArrayList<>(); - } else { - shouldRetry = null; - } + final boolean wantRetry = numFailures.intValue() > 0 && numFailures.intValue() != endExclusive - startInclusive; // The above has compiled into e.g. // /tmp/workspace/cache/classes/temporaryCompilationDirectory12345/io/deephaven/test/cm12862183232603186v52_0/{various // class files} // We want to atomically move it to e.g. // /tmp/workspace/cache/classes/io/deephaven/test/cm12862183232603186v52_0/{various class files} - Arrays.stream(requests, startInclusive, endExclusive).forEach(request -> { + requests.subList(startInclusive, endExclusive).forEach(request -> { final Path srcDir = Paths.get(tempDirAsString, request.splitPackageName); final Path destDir = Paths.get(rootPathAsString, request.splitPackageName); try { Files.move(srcDir, destDir, StandardCopyOption.ATOMIC_MOVE); } catch (IOException ioe) { - if (shouldRetry != null && !Files.exists(srcDir) && !request.resolver.getFuture().isDone()) { - // This source actually succeeded in compiling, but was not written because some other source failed - // to compile. Let's recursively call ourselves to try again. - shouldRetry.add(request); - return; + // The name "isDone" might be misleading here. We haven't called "complete" on the successful + // futures yet, so the only way they would be "done" at this point is if they completed + // exceptionally. + final boolean hasException = request.resolver.getFuture().isDone(); + + if (wantRetry && !Files.exists(srcDir)) { + // The move failed and the source directory does not exist. + if (!hasException) { + // This source actually succeeded in compiling, but was not written because some other source + // failed to compile. Let's schedule this work to try again. + toRetry.add(request); + } } - if (!Files.exists(destDir) && !request.resolver.getFuture().isDone()) { - // The move might have failed for a variety of bad reasons. However, if the reason was because - // we lost the race to some other process, that's a harmless/desirable outcome, and we can ignore - // it. + if (!Files.exists(destDir) && !hasException) { + // Propagate an error here only if the destination does not exist; ignoring issues related to + // collisions with another process. request.resolver.completeExceptionally(new UncheckedIOException( "Move failed for some reason other than destination already existing", ioe)); } } }); - if (shouldRetry != null && !shouldRetry.isEmpty()) { - maybeCreateClassHelper(compiler, fileManager, shouldRetry.toArray(CompilationRequestAttempt[]::new), - rootPathAsString, tempDirAsString, 0, shouldRetry.size(), true); - } + return wantRetry; } /** diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/QueryCompilerRequestProcessor.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/QueryCompilerRequestProcessor.java index 20518ba64fe..99db140e0f4 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/QueryCompilerRequestProcessor.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/QueryCompilerRequestProcessor.java @@ -3,10 +3,12 @@ */ package io.deephaven.engine.table.impl; +import io.deephaven.UncheckedDeephavenException; import io.deephaven.engine.context.ExecutionContext; import io.deephaven.engine.context.QueryCompiler; import io.deephaven.engine.context.QueryCompilerRequest; import io.deephaven.engine.table.impl.perf.QueryPerformanceRecorder; +import io.deephaven.util.MultiException; import io.deephaven.util.SafeCloseable; import io.deephaven.util.CompletionStageFuture; import io.deephaven.util.CompletionStageFutureImpl; @@ -14,6 +16,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ExecutionException; public interface QueryCompilerRequestProcessor { /** @@ -80,12 +83,25 @@ public void compile() { try (final SafeCloseable ignored = QueryPerformanceRecorder.getInstance().getCompilationNugget(desc)) { final QueryCompiler compiler = ExecutionContext.getContext().getQueryCompiler(); - if (requests.size() == 1) { - compiler.compile(requests.get(0), resolvers.get(0)); - } else { - compiler.compile( - requests.toArray(QueryCompilerRequest[]::new), - resolvers.toArray(CompletionStageFuture.Resolver[]::new)); + // noinspection unchecked + compiler.compile( + requests.toArray(QueryCompilerRequest[]::new), + resolvers.toArray(CompletionStageFuture.Resolver[]::new)); + + final List exceptions = new ArrayList<>(); + for (CompletionStageFuture.Resolver> resolver : resolvers) { + try { + Object ignored2 = resolver.getFuture().get(); + } catch (ExecutionException err) { + exceptions.add(err.getCause()); + } catch (InterruptedException err) { + exceptions.add(err); + } + } + if (!exceptions.isEmpty()) { + throw new UncheckedDeephavenException("Compilation failed", + MultiException.maybeWrapInMultiException("Compilation exceptions for multiple requests", + exceptions)); } } } diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/select/AbstractFormulaColumn.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/select/AbstractFormulaColumn.java index 50a182e1e4e..549d72112d7 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/select/AbstractFormulaColumn.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/select/AbstractFormulaColumn.java @@ -286,8 +286,7 @@ protected Future createKernelFormulaFactory( return formulaKernelFactoryFuture .thenApply(formulaKernelFactory -> (columnName, rowSet, lazy, columnsToData, params) -> { // Maybe warn that we ignore "lazy". By the way, "lazy" is the wrong term anyway. "lazy" doesn't - // mean - // "cached", which is how we are using it. + // mean "cached", which is how we are using it. final Map> netColumnSources = new HashMap<>(); for (final String sourceColumnName : sd.sources) { final ColumnSource columnSourceToUse = columnsToData.get(sourceColumnName); diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/select/analyzers/SelectAndViewAnalyzer.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/select/analyzers/SelectAndViewAnalyzer.java index ef3a891db9d..ef1e9f7e9a5 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/select/analyzers/SelectAndViewAnalyzer.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/select/analyzers/SelectAndViewAnalyzer.java @@ -52,8 +52,8 @@ public enum Mode { public static Supplier> newQueryScopeVariableSupplier() { final QueryScope queryScope = ExecutionContext.getContext().getQueryScope(); - return new CachingSupplier<>( - () -> queryScope.toMap((name, value) -> NameValidator.isValidQueryParameterName(name))); + return new CachingSupplier<>(() -> Collections.unmodifiableMap( + queryScope.toMap((name, value) -> NameValidator.isValidQueryParameterName(name)))); } public static void initializeSelectColumns( diff --git a/open-api/lang-parser/lang-parser.gradle b/open-api/lang-parser/lang-parser.gradle index 8baf8dc5632..d785dc7c187 100644 --- a/open-api/lang-parser/lang-parser.gradle +++ b/open-api/lang-parser/lang-parser.gradle @@ -10,6 +10,7 @@ plugins { apply from: "$rootDir/gradle/web-common.gradle" dependencies { + implementation project(':Util') api project(':open-api-shared-fu') implementation project(':log-factory') api project(':proto:proto-backplane-grpc') diff --git a/open-api/lang-parser/src/main/java/io/deephaven/lang/parse/ParsedDocument.java b/open-api/lang-parser/src/main/java/io/deephaven/lang/parse/ParsedDocument.java index 7f1166b5b67..e332ab69b26 100644 --- a/open-api/lang-parser/src/main/java/io/deephaven/lang/parse/ParsedDocument.java +++ b/open-api/lang-parser/src/main/java/io/deephaven/lang/parse/ParsedDocument.java @@ -3,7 +3,6 @@ */ package io.deephaven.lang.parse; -import io.deephaven.base.Lazy; import io.deephaven.base.verify.Assert; import io.deephaven.io.logger.Logger; import io.deephaven.lang.generated.*; @@ -11,6 +10,7 @@ import io.deephaven.proto.backplane.script.grpc.DocumentRange; import io.deephaven.proto.backplane.script.grpc.Position; import io.deephaven.proto.backplane.script.grpc.TextEdit; +import io.deephaven.util.datastructures.CachingSupplier; import io.deephaven.web.shared.fu.MappedIterable; import java.util.*; @@ -100,7 +100,7 @@ public boolean containsIndex(int i) { private static final Pattern NEW_LINE_PATTERN = Pattern.compile("\\r?\\n"); private final ChunkerDocument doc; - private final Lazy> statements; + private final CachingSupplier> statements; private final String src; private String errorSource; private ParseException error; @@ -112,7 +112,7 @@ public ParsedDocument(ChunkerDocument doc, String document) { this.src = document; computedPositions = new ConcurrentHashMap<>(4); assignments = new ConcurrentHashMap<>(12); - statements = new Lazy<>(() -> { + statements = new CachingSupplier<>(() -> { final LinkedHashSet stmts = new LinkedHashSet<>(); doc.childrenAccept(new ChunkerDefaultVisitor() { @Override diff --git a/open-api/lang-tools/src/main/java/io/deephaven/lang/completion/CompletionLookups.java b/open-api/lang-tools/src/main/java/io/deephaven/lang/completion/CompletionLookups.java index 4e2625a8c47..457f3db3a34 100644 --- a/open-api/lang-tools/src/main/java/io/deephaven/lang/completion/CompletionLookups.java +++ b/open-api/lang-tools/src/main/java/io/deephaven/lang/completion/CompletionLookups.java @@ -3,11 +3,11 @@ */ package io.deephaven.lang.completion; -import io.deephaven.base.Lazy; import io.deephaven.engine.context.ExecutionContext; import io.deephaven.engine.table.TableDefinition; import io.deephaven.engine.context.QueryLibrary; import io.deephaven.engine.util.ScriptSession; +import io.deephaven.util.datastructures.CachingSupplier; import java.util.Collection; import java.util.Map; @@ -26,15 +26,15 @@ public class CompletionLookups { private static final WeakHashMap lookups = new WeakHashMap<>(); - private final Lazy>> statics; + private final CachingSupplier>> statics; private final Map referencedTables; - private final Lazy customCompletions; + private final CachingSupplier customCompletions; public CompletionLookups(Set customCompletionFactory) { final QueryLibrary ql = ExecutionContext.getContext().getQueryLibrary(); - statics = new Lazy<>(ql::getStaticImports); + statics = new CachingSupplier<>(ql::getStaticImports); referencedTables = new ConcurrentHashMap<>(); - customCompletions = new Lazy<>(() -> new DelegatingCustomCompletion(customCompletionFactory)); + customCompletions = new CachingSupplier<>(() -> new DelegatingCustomCompletion(customCompletionFactory)); // This can be slow, so lets start it on a background thread right away. final ForkJoinPool pool = ForkJoinPool.commonPool();