Skip to content

Commit 35e5ed5

Browse files
authored
Don't cancel aborted jobs immediately (#11375)
* Don't cancel aborted jobs immediately Rather than cancelling Futures that capture jobs' logic, this change introduces a two-level system: - interrupt all jobs softly via ThreadInterrupted at safepoints - if safepoint is not executed within some time period or it is but the job is still not cancelled, trigger a hard-interrupt by cancelling the job explicitly, if possible Closes #11084. * Only cancel Future when you mean it Soft-cancelling a future only to later call it with `mayInterrupt` set to `true` has no effect in the latter case. Changed the logic so that interrupting a Future will really enforce it. Ocassionally some commands should not attempt to run soft cancellations - we know they will re-execute the program. * Replace Thread.sleep with Future.get No while loops etc, it's much easier to reason about what is soft and hard interrupt supposed to do. * Better comments/logs * nit * PR review * Make test more robust
1 parent 988316f commit 35e5ed5

File tree

26 files changed

+257
-63
lines changed

26 files changed

+257
-63
lines changed

engine/language-server/src/main/scala/org/enso/languageserver/requesthandler/visualization/ModifyVisualizationHandler.scala

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import org.enso.languageserver.runtime.{
1212
}
1313
import org.enso.languageserver.util.UnhandledLogging
1414

15+
import java.util.UUID
1516
import scala.concurrent.duration.FiniteDuration
1617

1718
/** A request handler for `executionContext/modifyVisualization` commands.
@@ -41,16 +42,23 @@ class ModifyVisualizationHandler(
4142
)
4243
val cancellable =
4344
context.system.scheduler.scheduleOnce(timeout, self, RequestTimeout)
44-
context.become(responseStage(id, sender(), cancellable))
45+
context.become(
46+
responseStage(id, params.visualizationId, sender(), cancellable)
47+
)
4548
}
4649

4750
private def responseStage(
4851
id: Id,
52+
visualizationID: UUID,
4953
replyTo: ActorRef,
5054
cancellable: Cancellable
5155
): Receive = {
5256
case RequestTimeout =>
53-
logger.error("Request [{}] timed out.", id)
57+
logger.error(
58+
"Request [{}] timed out for visualization {}.",
59+
id,
60+
visualizationID
61+
)
5462
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
5563
context.stop(self)
5664

engine/runtime-instrument-common/src/main/java/org/enso/interpreter/instrument/command/SetExecutionEnvironmentCommand.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ private void setExecutionEnvironment(
5252
if (!oldEnvironmentName.equals(executionEnvironment.name())) {
5353
ctx.jobControlPlane()
5454
.abortJobs(
55-
contextId, "set execution environment to " + executionEnvironment.name());
55+
contextId,
56+
"set execution environment to " + executionEnvironment.name(),
57+
false);
5658
ctx.locking()
5759
.withWriteCompilationLock(
5860
this.getClass(),

engine/runtime-instrument-common/src/main/java/org/enso/interpreter/instrument/execution/JobControlPlane.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,15 @@ public interface JobControlPlane {
2121
* Aborts jobs that relates to the specified execution context.
2222
*
2323
* @param contextId an identifier of a context
24+
* @param reason reason for aborting job(s)
25+
* @param softAbortFirst true if ongoing jobs should be aborted with safepoints first, even if
26+
* marked as interruptible
2427
* @param classOf abort jobs of a given class only. If empty all jobs for the given context are
2528
* aborted
2629
*/
2730
@SuppressWarnings("unchecked")
28-
void abortJobs(UUID contextId, String reason, Class<? extends Job<?>>... classOf);
31+
void abortJobs(
32+
UUID contextId, String reason, boolean softAbortFirst, Class<? extends Job<?>>... classOf);
2933

3034
/**
3135
* Aborts jobs that relate to the specified execution context.

engine/runtime-instrument-common/src/main/java/org/enso/interpreter/instrument/job/ExecuteExpressionJob.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public ExecuteExpressionJob(
3232
}
3333

3434
@Override
35-
public Executable run(RuntimeContext ctx) {
35+
public Executable runImpl(RuntimeContext ctx) {
3636
return ctx.locking()
3737
.withContextLock(
3838
ctx.locking().getOrCreateContextLock(contextId),

engine/runtime-instrument-common/src/main/java/org/enso/interpreter/instrument/job/SerializeModuleJob.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public SerializeModuleJob(QualifiedName moduleName) {
1818
}
1919

2020
@Override
21-
public Void run(RuntimeContext ctx) {
21+
public Void runImpl(RuntimeContext ctx) {
2222
var ensoContext = ctx.executionService().getContext();
2323
var compiler = ensoContext.getCompiler();
2424
boolean useGlobalCacheLocations = ensoContext.isUseGlobalCache();

engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/command/DestroyContextCmd.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class DestroyContextCmd(
3434
}
3535

3636
private def removeContext()(implicit ctx: RuntimeContext): Unit = {
37-
ctx.jobControlPlane.abortJobs(request.contextId, "destroy context")
37+
ctx.jobControlPlane.abortJobs(request.contextId, "destroy context", false)
3838
val contextLock = ctx.locking.getOrCreateContextLock(request.contextId)
3939
try {
4040
ctx.locking.withContextLock(

engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/command/InterruptContextCmd.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ class InterruptContextCmd(
2323
): Future[Unit] =
2424
if (doesContextExist) {
2525
Future {
26-
ctx.jobControlPlane.abortJobs(request.contextId, "interrupt context")
26+
ctx.jobControlPlane.abortJobs(
27+
request.contextId,
28+
"interrupt context",
29+
false
30+
)
2731
reply(Api.InterruptContextResponse(request.contextId))
2832
}
2933
} else {

engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/command/PopContextCmd.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class PopContextCmd(
4343
ec: ExecutionContext
4444
): Future[Unit] =
4545
Future {
46-
ctx.jobControlPlane.abortJobs(request.contextId, "pop context")
46+
ctx.jobControlPlane.abortJobs(request.contextId, "pop context", false)
4747
val maybeTopItem = ctx.contextManager.pop(request.contextId)
4848
if (maybeTopItem.isDefined) {
4949
reply(Api.PopContextResponse(request.contextId))

engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/command/PushContextCmd.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class PushContextCmd(
4646
ec: ExecutionContext
4747
): Future[Boolean] =
4848
Future {
49-
ctx.jobControlPlane.abortJobs(request.contextId, "push context")
49+
ctx.jobControlPlane.abortJobs(request.contextId, "push context", false)
5050
val stack = ctx.contextManager.getStack(request.contextId)
5151
val pushed = request.stackItem match {
5252
case _: Api.StackItem.ExplicitCall if stack.isEmpty =>

engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/command/RecomputeContextCmd.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ class RecomputeContextCmd(
4242
ec: ExecutionContext
4343
): Future[Boolean] = {
4444
Future {
45-
ctx.jobControlPlane.abortJobs(request.contextId, "recompute context")
45+
ctx.jobControlPlane.abortJobs(
46+
request.contextId,
47+
"recompute context",
48+
false
49+
)
4650
val stack = ctx.contextManager.getStack(request.contextId)
4751
if (stack.isEmpty) {
4852
reply(Api.EmptyStackError(request.contextId))

0 commit comments

Comments
 (0)