Add Polyglot Support in Runtime Tests (#4016)

`runtime-with-instruments` project sets `-Dgraalvm.locatorDisabled=true` that disables the discovery of available polyglot languages (installed with `gu`). On the other hand, enabling locator makes polyglot languages available, but also makes the program classes and the test classes loaded with different classloaders. This way we're unable to use `EnsoContext` in tests to observe internal context state (there is an exception when you try to cast to `EnsoContext`).

The solution is to move tests with enabled polyglot support, but disabled `EnsoContext` introspection to a separate project.
This commit is contained in:
Dmitry Bushev 2023-01-03 17:36:26 +03:00 committed by GitHub
parent 1e5e2327ab
commit 9df6448d85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 104 additions and 128 deletions

View File

@ -285,6 +285,7 @@ lazy val enso = (project in file("."))
`runtime-instrument-repl-debugger`,
`runtime-instrument-runtime-server`,
`runtime-with-instruments`,
`runtime-with-polyglot`,
`runtime-version-manager`,
`runtime-version-manager-test`,
editions,
@ -1143,6 +1144,7 @@ lazy val `polyglot-api` = project
},
libraryDependencies ++= Seq(
"org.graalvm.sdk" % "polyglot-tck" % graalVersion % "provided",
"org.graalvm.truffle" % "truffle-api" % graalVersion % "provided",
"com.google.flatbuffers" % "flatbuffers-java" % flatbuffersVersion,
"org.scalatest" %% "scalatest" % scalatestVersion % Test,
"org.scalacheck" %% "scalacheck" % scalacheckVersion % Test
@ -1569,6 +1571,53 @@ lazy val `runtime-with-instruments` =
.dependsOn(`runtime-instrument-repl-debugger`)
.dependsOn(`runtime-instrument-runtime-server`)
/* runtime-with-polyglot
* ~~~~~~~~~~~~~~~~~~~~~
* A project that allows loading polyglot languages by not disabling
* the GraalVM locator explicitly with `-Dgraalvm.locatorDisabled=true` like the
* `runtime-with-instruments` project does. It means that the test classes and
* runtime classes are loaded using different classloaders, and the test code
* cannot access the `EnsoContext`.
*/
lazy val `runtime-with-polyglot` =
(project in file("engine/runtime-with-polyglot"))
.configs(Benchmark)
.settings(
frgaalJavaCompilerSetting,
inConfig(Compile)(truffleRunOptionsSettings),
inConfig(Benchmark)(Defaults.testSettings),
commands += WithDebugCommand.withDebug,
Benchmark / javacOptions --= Seq(
"-source",
frgaalSourceLevel,
"--enable-preview"
),
Test / javaOptions ++= {
// Note [Classpath Separation]
val runtimeClasspath =
(LocalProject("runtime") / Compile / fullClasspath).value
val runtimeInstrumentsClasspath =
(LocalProject("runtime-with-instruments") / Compile / fullClasspath).value
val appendClasspath =
(runtimeClasspath ++ runtimeInstrumentsClasspath)
.map(_.data)
.mkString(File.pathSeparator)
Seq(
s"-Dtruffle.class.path.append=$appendClasspath"
)
},
Test / fork := true,
Test / envVars ++= distributionEnvironmentOverrides ++ Map(
"ENSO_TEST_DISABLE_IR_CACHE" -> "false"
),
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % scalatestVersion % Test
)
)
.dependsOn(runtime % "compile->compile;test->test;runtime->runtime")
.dependsOn(`runtime-with-instruments`)
/* Note [Unmanaged Classpath]
* ~~~~~~~~~~~~~~~~~~~~~~~~~~
* As the definition of the core primitives in `core_definition` is achieved

View File

@ -1,10 +1,9 @@
package org.enso.polyglot;
import java.util.Arrays;
import java.util.logging.Level;
import org.graalvm.options.OptionDescriptor;
import org.graalvm.options.OptionDescriptors;
import org.graalvm.options.OptionKey;
import org.graalvm.options.*;
import com.oracle.truffle.api.Option;
/** Class representing runtime options supported by the Enso engine. */
public class RuntimeOptions {
@ -91,6 +90,16 @@ public class RuntimeOptions {
OptionDescriptor.newBuilder(USE_GLOBAL_IR_CACHE_LOCATION_KEY, USE_GLOBAL_IR_CACHE_LOCATION)
.build();
public static final String ENABLE_EXECUTION_TIMER = optionName("enableExecutionTimer");
@Option(
help = "Enables timer that counts down the execution time of expressions.",
category = OptionCategory.INTERNAL)
public static final OptionKey<Boolean> ENABLE_EXECUTION_TIMER_KEY = new OptionKey<>(true);
private static final OptionDescriptor ENABLE_EXECUTION_TIMER_DESCRIPTOR =
OptionDescriptor.newBuilder(ENABLE_EXECUTION_TIMER_KEY, ENABLE_EXECUTION_TIMER).build();
public static final OptionDescriptors OPTION_DESCRIPTORS =
OptionDescriptors.create(
Arrays.asList(
@ -107,7 +116,8 @@ public class RuntimeOptions {
INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION_DESCRIPTOR,
DISABLE_IR_CACHES_DESCRIPTOR,
WAIT_FOR_PENDING_SERIALIZATION_JOBS_DESCRIPTOR,
USE_GLOBAL_IR_CACHE_LOCATION_DESCRIPTOR));
USE_GLOBAL_IR_CACHE_LOCATION_DESCRIPTOR,
ENABLE_EXECUTION_TIMER_DESCRIPTOR));
/**
* Canonicalizes the option name by prefixing it with the language name.

View File

@ -37,7 +37,7 @@ import org.enso.interpreter.runtime.tag.AvoidIdInstrumentationTag;
id = IdExecutionService.INSTRUMENT_ID,
services = IdExecutionService.class)
public class IdExecutionInstrument extends TruffleInstrument implements IdExecutionService {
private Timer timer;
private Env env;
/**
@ -48,20 +48,9 @@ public class IdExecutionInstrument extends TruffleInstrument implements IdExecut
@Override
protected void onCreate(Env env) {
env.registerService(this);
this.timer = new Timer.Nanosecond();
this.env = env;
}
/**
* Override the default nanosecond timer with the specified {@code timer}.
*
* @param timer the timer to override with
*/
@Override
public void overrideTimer(Timer timer) {
this.timer = timer;
}
/** The listener class used by this instrument. */
private static class IdExecutionEventListener implements ExecutionEventListener {
private final CallTarget entryCallTarget;
@ -305,6 +294,7 @@ public class IdExecutionInstrument extends TruffleInstrument implements IdExecut
* @param cache the precomputed expression values.
* @param methodCallsCache the storage tracking the executed method calls.
* @param syncState the synchronization state of runtime updates.
* @param timer the execution timer.
* @param nextExecutionItem the next item scheduled for execution.
* @param functionCallCallback the consumer of function call events.
* @param onComputedCallback the consumer of the computed value events.
@ -319,6 +309,7 @@ public class IdExecutionInstrument extends TruffleInstrument implements IdExecut
RuntimeCache cache,
MethodCallsCache methodCallsCache,
UpdatesSynchronizationState syncState,
Timer timer,
UUID nextExecutionItem,
Consumer<IdExecutionInstrument.ExpressionCall> functionCallCallback,
Consumer<IdExecutionInstrument.ExpressionValue> onComputedCallback,

View File

@ -2,9 +2,7 @@ package org.enso.interpreter.test.instrument
import org.enso.distribution.FileSystem
import org.enso.distribution.locking.ThreadSafeFileLockManager
import org.enso.interpreter.instrument.execution.Timer
import org.enso.interpreter.runtime.`type`.ConstantsGen
import org.enso.interpreter.runtime.EnsoContext
import org.enso.interpreter.test.Metadata
import org.enso.pkg.{Package, PackageManager}
import org.enso.polyglot._
@ -24,12 +22,6 @@ class BuiltinTypesTest
with Matchers
with BeforeAndAfterEach {
// === Test Timer ===========================================================
class TestTimer extends Timer {
override def getTime(): Long = 0
}
// === Test Utilities =======================================================
var context: TestContext = _
@ -56,6 +48,7 @@ class BuiltinTypesTest
.option(RuntimeOptions.INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION, "true")
.option(RuntimeOptions.ENABLE_PROJECT_SUGGESTIONS, "false")
.option(RuntimeOptions.ENABLE_GLOBAL_SUGGESTIONS, "false")
.option(RuntimeOptions.ENABLE_EXECUTION_TIMER, "false")
.option(
RuntimeOptions.DISABLE_IR_CACHES,
InstrumentTestContext.DISABLE_IR_CACHE
@ -77,14 +70,6 @@ class BuiltinTypesTest
)
executionContext.context.initialize(LanguageInfo.ID)
val languageContext = executionContext.context
.getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject[EnsoContext]
languageContext.getLanguage.getIdExecutionService.ifPresent(
_.overrideTimer(new TestTimer)
);
def writeMain(contents: String): File =
Files.write(pkg.mainFile.toPath, contents.getBytes).toFile

View File

@ -2,7 +2,6 @@ package org.enso.interpreter.test.instrument
import org.enso.distribution.FileSystem
import org.enso.distribution.locking.ThreadSafeFileLockManager
import org.enso.interpreter.instrument.execution.Timer
import org.enso.interpreter.test.Metadata
import org.enso.pkg.{Package, PackageManager}
import org.enso.polyglot._
@ -23,12 +22,6 @@ class RuntimeAsyncCommandsTest
with Matchers
with BeforeAndAfterEach {
// === Test Timer ===========================================================
class TestTimer extends Timer {
override def getTime(): Long = 0
}
// === Test Utilities =======================================================
var context: TestContext = _
@ -56,6 +49,7 @@ class RuntimeAsyncCommandsTest
)
.option(RuntimeOptions.ENABLE_PROJECT_SUGGESTIONS, "false")
.option(RuntimeOptions.ENABLE_GLOBAL_SUGGESTIONS, "false")
.option(RuntimeOptions.ENABLE_EXECUTION_TIMER, "false")
.option(
RuntimeOptions.DISABLE_IR_CACHES,
InstrumentTestContext.DISABLE_IR_CACHE
@ -76,14 +70,6 @@ class RuntimeAsyncCommandsTest
)
executionContext.context.initialize(LanguageInfo.ID)
val languageContext = executionContext.context
.getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject[org.enso.interpreter.runtime.EnsoContext]
languageContext.getLanguage.getIdExecutionService.ifPresent(
_.overrideTimer(new TestTimer)
)
def writeMain(contents: String): File =
Files.write(pkg.mainFile.toPath, contents.getBytes).toFile

View File

@ -2,7 +2,6 @@ package org.enso.interpreter.test.instrument
import org.enso.distribution.FileSystem
import org.enso.distribution.locking.ThreadSafeFileLockManager
import org.enso.interpreter.instrument.execution.Timer
import org.enso.interpreter.runtime.`type`.ConstantsGen
import org.enso.interpreter.test.Metadata
import org.enso.pkg.{Package, PackageManager}
@ -26,12 +25,6 @@ class RuntimeErrorsTest
with Matchers
with BeforeAndAfterEach {
// === Test Timer ===========================================================
class TestTimer extends Timer {
override def getTime(): Long = 0
}
// === Test Utilities =======================================================
var context: TestContext = _
@ -61,6 +54,7 @@ class RuntimeErrorsTest
.option(RuntimeOptions.INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION, "true")
.option(RuntimeOptions.ENABLE_PROJECT_SUGGESTIONS, "false")
.option(RuntimeOptions.ENABLE_GLOBAL_SUGGESTIONS, "false")
.option(RuntimeOptions.ENABLE_EXECUTION_TIMER, "false")
.option(
RuntimeOptions.DISABLE_IR_CACHES,
InstrumentTestContext.DISABLE_IR_CACHE
@ -81,14 +75,6 @@ class RuntimeErrorsTest
)
executionContext.context.initialize(LanguageInfo.ID)
val languageContext = executionContext.context
.getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject[org.enso.interpreter.runtime.EnsoContext]
languageContext.getLanguage.getIdExecutionService.ifPresent(
_.overrideTimer(new TestTimer)
)
def writeMain(contents: String): File =
Files.write(pkg.mainFile.toPath, contents.getBytes).toFile

View File

@ -2,7 +2,6 @@ package org.enso.interpreter.test.instrument
import org.enso.distribution.FileSystem
import org.enso.distribution.locking.ThreadSafeFileLockManager
import org.enso.interpreter.instrument.execution.Timer
import org.enso.interpreter.runtime.`type`.{Constants, ConstantsGen}
import org.enso.interpreter.test.Metadata
import org.enso.pkg.{Package, PackageManager}
@ -24,12 +23,6 @@ class RuntimeInstrumentTest
with Matchers
with BeforeAndAfterEach {
// === Test Timer ===========================================================
class TestTimer extends Timer {
override def getTime(): Long = 0
}
// === Test Utilities =======================================================
var context: TestContext = _
@ -54,6 +47,7 @@ class RuntimeInstrumentTest
.option(RuntimeOptions.INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION, "true")
.option(RuntimeOptions.ENABLE_PROJECT_SUGGESTIONS, "false")
.option(RuntimeOptions.ENABLE_GLOBAL_SUGGESTIONS, "false")
.option(RuntimeOptions.ENABLE_EXECUTION_TIMER, "false")
.option(
RuntimeOptions.DISABLE_IR_CACHES,
InstrumentTestContext.DISABLE_IR_CACHE
@ -74,14 +68,6 @@ class RuntimeInstrumentTest
)
executionContext.context.initialize(LanguageInfo.ID)
val languageContext = executionContext.context
.getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject[org.enso.interpreter.runtime.EnsoContext]
languageContext.getLanguage.getIdExecutionService.ifPresent(
_.overrideTimer(new TestTimer)
);
def writeMain(contents: String): File =
Files.write(pkg.mainFile.toPath, contents.getBytes).toFile

View File

@ -2,7 +2,6 @@ package org.enso.interpreter.test.instrument
import org.enso.distribution.FileSystem
import org.enso.distribution.locking.ThreadSafeFileLockManager
import org.enso.interpreter.instrument.execution.Timer
import org.enso.interpreter.runtime.`type`.{ConstantsGen, Types}
import org.enso.interpreter.runtime.EnsoContext
import org.enso.interpreter.test.Metadata
@ -27,12 +26,6 @@ class RuntimeServerTest
with Matchers
with BeforeAndAfterEach {
// === Test Timer ===========================================================
class TestTimer extends Timer {
override def getTime(): Long = 0
}
// === Test Utilities =======================================================
var context: TestContext = _
@ -59,6 +52,7 @@ class RuntimeServerTest
.option(RuntimeOptions.INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION, "true")
.option(RuntimeOptions.ENABLE_PROJECT_SUGGESTIONS, "false")
.option(RuntimeOptions.ENABLE_GLOBAL_SUGGESTIONS, "false")
.option(RuntimeOptions.ENABLE_EXECUTION_TIMER, "false")
.option(
RuntimeOptions.DISABLE_IR_CACHES,
InstrumentTestContext.DISABLE_IR_CACHE
@ -84,11 +78,6 @@ class RuntimeServerTest
.getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject[EnsoContext]
val info =
languageContext.getEnvironment.getPublicLanguages.get(LanguageInfo.ID)
languageContext.getLanguage.getIdExecutionService.ifPresent(
_.overrideTimer(new TestTimer)
)
def writeMain(contents: String): File =
Files.write(pkg.mainFile.toPath, contents.getBytes).toFile

View File

@ -1,10 +1,8 @@
package org.enso.interpreter.test.instrument
import org.enso.distribution.FileSystem
import org.apache.commons.io.FileUtils
import org.enso.distribution.locking.ThreadSafeFileLockManager
import org.enso.interpreter.instrument.execution.Timer
import org.enso.interpreter.runtime.`type`.ConstantsGen
import org.enso.interpreter.runtime.EnsoContext
import org.enso.interpreter.test.Metadata
import org.enso.pkg.{Package, PackageManager, QualifiedName}
import org.enso.polyglot._
@ -27,12 +25,6 @@ class RuntimeVisualizationsTest
with Matchers
with BeforeAndAfterEach {
// === Test Timer ===========================================================
class TestTimer extends Timer {
override def getTime(): Long = 0
}
// === Test Utilities =======================================================
var context: TestContext = _
@ -40,7 +32,7 @@ class RuntimeVisualizationsTest
class TestContext(packageName: String) extends InstrumentTestContext {
val tmpDir: Path = Files.createTempDirectory("enso-test-packages")
sys.addShutdownHook(FileSystem.removeDirectoryIfExists(tmpDir))
sys.addShutdownHook(FileUtils.deleteQuietly(tmpDir.toFile))
val lockManager = new ThreadSafeFileLockManager(tmpDir.resolve("locks"))
val runtimeServerEmulator =
new RuntimeServerEmulator(messageQueue, lockManager)
@ -59,6 +51,7 @@ class RuntimeVisualizationsTest
.option(RuntimeOptions.INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION, "true")
.option(RuntimeOptions.ENABLE_PROJECT_SUGGESTIONS, "false")
.option(RuntimeOptions.ENABLE_GLOBAL_SUGGESTIONS, "false")
.option(RuntimeOptions.ENABLE_EXECUTION_TIMER, "false")
.option(RuntimeServerInfo.ENABLE_OPTION, "true")
.option(RuntimeOptions.INTERACTIVE_MODE, "true")
.option(
@ -76,14 +69,6 @@ class RuntimeVisualizationsTest
)
executionContext.context.initialize(LanguageInfo.ID)
val languageContext = executionContext.context
.getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject[EnsoContext]
languageContext.getLanguage.getIdExecutionService.ifPresent(
_.overrideTimer(new TestTimer)
)
def writeMain(contents: String): File =
Files.write(pkg.mainFile.toPath, contents.getBytes).toFile
@ -2160,10 +2145,6 @@ class RuntimeVisualizationsTest
}
it should "run internal IDE visualisation preprocessor catching error" in {
pending
// TODO [JD]: Disabled due to issue with context not allowing JS functions.
// https://www.pivotaltracker.com/story/show/184064564
val contextId = UUID.randomUUID()
val requestId = UUID.randomUUID()
val visualisationId = UUID.randomUUID()
@ -2208,8 +2189,8 @@ class RuntimeVisualizationsTest
context.send(
Api.Request(requestId, Api.PushContextRequest(contextId, item1))
)
val pushContextResponses = context.receiveNIgnorePendingExpressionUpdates(3)
pushContextResponses should contain allOf (
val pushContextResponses = context.receiveNIgnoreStdLib(3)
pushContextResponses should contain theSameElementsAs Seq(
Api.Response(requestId, Api.PushContextResponse(contextId)),
TestMessages.error(
contextId,
@ -2261,7 +2242,7 @@ class RuntimeVisualizationsTest
data
}
val stringified = new String(data)
stringified shouldEqual """{ "kind": "Dataflow", "message": "The List is empty. (at <enso> Main.main(Enso_Test.Test.Main:6:5-32)"}"""
stringified shouldEqual """{"kind":"Dataflow","message":"The List is empty. (at <enso> Main.main(Enso_Test.Test.Main:6:5-32)"}"""
}
it should "attach method pointer visualisation without arguments" in {

View File

@ -30,6 +30,7 @@ import org.enso.interpreter.epb.EpbLanguage;
import org.enso.interpreter.instrument.IdExecutionService;
import org.enso.interpreter.instrument.NotificationHandler.Forwarder;
import org.enso.interpreter.instrument.NotificationHandler.TextMode$;
import org.enso.interpreter.instrument.execution.Timer;
import org.enso.interpreter.node.EnsoRootNode;
import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.node.ProgramRootNode;
@ -130,6 +131,10 @@ public final class EnsoLanguage extends TruffleLanguage<EnsoContext> {
lockManager = new ThreadSafeFileLockManager(distributionManager.paths().locks());
}
boolean isExecutionTimerEnabled =
env.getOptions().get(RuntimeOptions.ENABLE_EXECUTION_TIMER_KEY);
Timer timer = isExecutionTimerEnabled ? new Timer.Nanosecond() : new Timer.Disabled();
EnsoContext context =
new EnsoContext(
this, getLanguageHome(), env, notificationHandler, lockManager, distributionManager);
@ -140,7 +145,7 @@ public final class EnsoLanguage extends TruffleLanguage<EnsoContext> {
env.lookup(idValueListenerInstrument, IdExecutionService.class));
env.registerService(
new ExecutionService(
context, idExecutionInstrument, notificationHandler, connectedLockManager));
context, idExecutionInstrument, notificationHandler, connectedLockManager, timer));
return context;
}

View File

@ -28,6 +28,7 @@ public interface IdExecutionService {
* @param cache the precomputed expression values.
* @param methodCallsCache the storage tracking the executed method calls.
* @param syncState the synchronization state of runtime updates.
* @param timer the execution timer.
* @param nextExecutionItem the next item scheduled for execution.
* @param functionCallCallback the consumer of function call events.
* @param onComputedCallback the consumer of the computed value events.
@ -41,19 +42,13 @@ public interface IdExecutionService {
RuntimeCache cache,
MethodCallsCache methodCallsCache,
UpdatesSynchronizationState syncState,
Timer timer,
UUID nextExecutionItem,
Consumer<ExpressionCall> functionCallCallback,
Consumer<ExpressionValue> onComputedCallback,
Consumer<ExpressionValue> onCachedCallback,
Consumer<Exception> onExceptionalCallback);
/**
* Override the default nanosecond timer with the specified {@code timer}.
*
* @param timer the timer to override with
*/
void overrideTimer(Timer timer);
/** A class for notifications about functions being called in the course of execution. */
public static class ExpressionCall {
private final UUID expressionId;

View File

@ -18,6 +18,7 @@ import org.enso.interpreter.instrument.MethodCallsCache;
import org.enso.interpreter.instrument.NotificationHandler;
import org.enso.interpreter.instrument.RuntimeCache;
import org.enso.interpreter.instrument.UpdatesSynchronizationState;
import org.enso.interpreter.instrument.execution.Timer;
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode;
import org.enso.interpreter.node.expression.builtin.text.util.TypeToDisplayTextNodeGen;
import org.enso.interpreter.runtime.EnsoContext;
@ -58,25 +59,30 @@ public class ExecutionService {
private final TruffleLogger logger = TruffleLogger.getLogger(LanguageInfo.ID);
private final ConnectedLockManager connectedLockManager;
private final Timer timer;
/**
* Creates a new instance of this service.
*
* @param context the language context to use.
* @param idExecutionInstrument optional instance of the {@link IdExecutionService} to use in the
* course of executions
* @param notificationForwarder a forwarder of notifications, used to communicate with the user
* course of executions.
* @param notificationForwarder a forwarder of notifications, used to communicate with the user.
* @param connectedLockManager a connected lock manager (if it is in use) that should be connected
* to the language server, or null
* to the language server, or null.
* @param timer an execution timer.
*/
public ExecutionService(
EnsoContext context,
Optional<IdExecutionService> idExecutionInstrument,
NotificationHandler.Forwarder notificationForwarder,
ConnectedLockManager connectedLockManager) {
ConnectedLockManager connectedLockManager,
Timer timer) {
this.idExecutionInstrument = idExecutionInstrument;
this.context = context;
this.notificationForwarder = notificationForwarder;
this.connectedLockManager = connectedLockManager;
this.timer = timer;
}
/** @return the language context. */
@ -159,6 +165,7 @@ public class ExecutionService {
cache,
methodCallsCache,
syncState,
this.timer,
nextExecutionItem,
funCallCallback,
onComputedCallback,
@ -289,6 +296,7 @@ public class ExecutionService {
cache,
methodCallsCache,
syncState,
this.timer,
nextExecutionItem,
funCallCallback,
onComputedCallback,

View File

@ -7,7 +7,12 @@ trait Timer {
object Timer {
/** A nanosecond precision timer. */
class Nanosecond extends Timer {
final class Nanosecond extends Timer {
override def getTime(): Long = System.nanoTime()
}
/** A timer returns the same zero time. */
final class Disabled extends Timer {
override def getTime(): Long = 0
}
}