Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions eo-runtime/src/main/java/org/eolang/ExInterrupted.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2016-2026 Objectionary.com
* SPDX-License-Identifier: MIT
*/
package org.eolang;

/**
* Error that stops a computation running on an interrupted thread.
*
* <p>Dataization is a long chain of attribute lookups, where nothing ever
* blocks. A thread that has to be stopped from the outside — a test that
* outlived its deadline, a program that must shut down — therefore never
* notices the interrupt and keeps computing forever, holding a CPU core
* until the JVM dies. {@link PhDefault#take(String)} throws this error on
* the very next lookup instead.</p>
*
* <p>The interrupt flag stays up when this error is thrown, so every next
* lookup fails the same way and no {@code try} can resurrect the
* computation.</p>
*
* @since 0.74.0
*/
public final class ExInterrupted extends ExAbstract {

/**
* Serialization identifier.
*/
private static final long serialVersionUID = 597749420437007616L;

/**
* Ctor.
* @param cause Exception cause
*/
public ExInterrupted(final String cause) {
super(cause);
}
Comment on lines +30 to +36
}
8 changes: 8 additions & 0 deletions eo-runtime/src/main/java/org/eolang/PhDefault.java
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,14 @@ public void put(final String name, final Phi object) {

@Override
public Phi take(final String name) {
if (Thread.currentThread().isInterrupted()) {
throw new ExInterrupted(
String.format(
"Can't take \"%s\" from %s, because the thread was interrupted",
name, this.forma()
)
);
}
PhDefault.NESTING.set(PhDefault.NESTING.get() + 1);
try {
final Phi resolved;
Expand Down
42 changes: 42 additions & 0 deletions eo-runtime/src/test/java/org/eolang/ExInterruptedTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2016-2026 Objectionary.com
* SPDX-License-Identifier: MIT
*/
package org.eolang;

import java.time.Duration;
import java.util.concurrent.atomic.AtomicReference;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;

/**
* Test case for {@link ExInterrupted}.
* @since 0.74.0
*/
final class ExInterruptedTest {

@Test
void stopsTakingInInterruptedThread() throws InterruptedException {
final PhDefault phi = new PhDefault();
phi.add("stopsTakingInInterruptedThread", new AtComposite(phi, rho -> new Data.ToPhi(42L)));
final AtomicReference<Throwable> thrown = new AtomicReference<>();
final Thread thread = new Thread(
() -> {
while (true) {
phi.take("stopsTakingInInterruptedThread");
}
}
);
thread.setDaemon(true);
thread.setUncaughtExceptionHandler((ignore, error) -> thrown.set(error));
thread.start();
thread.interrupt();
thread.join(Duration.ofSeconds(10L).toMillis());
MatcherAssert.assertThat(
"Interrupted thread must abandon its computation, but it kept taking attributes",
thrown.get(),
Matchers.instanceOf(ExInterrupted.class)
);
}
}
Loading