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
4 changes: 3 additions & 1 deletion src/dev/enola/be/task/TaskCallable.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ public T call() throws Exception {
var thread = Thread.currentThread();
var originalThreadName = thread.getName();
thread.setName(task.id().toString());

try {
var output = task.execute();
if (output == null)
throw new NullPointerException("Task.execute() must not return null (Task ID: " + task.id() + ")");
throw new NullPointerException("Task.execute() must not return null: " + task.id());
return output;

} finally {
thread.setName(originalThreadName);
task.endedAt(Instant.now());
Expand Down
32 changes: 23 additions & 9 deletions src/dev/enola/be/task/demo/LongIncrementingTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import dev.enola.be.task.demo.LongIncrementingTask.Output;

import java.time.Duration;
import java.time.Instant;
import java.util.function.Consumer;

public class LongIncrementingTask extends Task<Input, Output> {
Expand All @@ -28,26 +29,39 @@ protected Output execute() throws Exception {
Thread.yield();
if (Thread.currentThread().isInterrupted())
throw new InterruptedException("Task was interrupted");
try {
Thread.sleep(input.sleep.toMillis());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new InterruptedException("Task was interrupted during sleep");
}
Threads.sleep(input.sleep);
}

// TODO Report % progress

return new Output(input.max);
}

public static void main(String[] args) {
// Count to 10000, with 1ms pause between each increment
var input = new Input(10000, Duration.ofMillis(1));
private static void simpleLoop(long max, Duration sleep) throws InterruptedException {
var start = Instant.now();
for (long i = 0; i < max; i++) Threads.sleep(sleep);
Copy link

Copilot AI Oct 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loop calls Threads.sleep() for each iteration without any yield or interrupt check. For large values of max, this could create performance issues and make the method unresponsive to interruption. Consider adding periodic interrupt checks similar to the main execute() method.

Suggested change
for (long i = 0; i < max; i++) Threads.sleep(sleep);
for (long i = 0; i < max; i++) {
Thread.yield();
if (Thread.currentThread().isInterrupted())
throw new InterruptedException("Loop was interrupted");
Threads.sleep(sleep);
}

Copilot uses AI. Check for mistakes.
var duration = Duration.between(start, Instant.now());
System.out.println(
"Looped to "
+ max
+ " with "
+ sleep
+ " sleep, but without output, in "
+ duration);
Comment on lines +44 to +50
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The multi-line string concatenation for logging can be made more readable and efficient. Consider using String.format() or System.out.printf() for a cleaner implementation.

        System.out.printf("Looped to %d with %s sleep, but without output, in %s%n", max, sleep, duration);

}

public static void main(String[] args) throws InterruptedException {
// Count to max, with 1ms pause between each increment
var max = 10000;
var sleep = Duration.ofMillis(0);
Copy link

Copilot AI Oct 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Using a sleep duration of 0 milliseconds may not provide meaningful demonstration of the sleep functionality. Consider using a small positive duration like Duration.ofMillis(1) to better demonstrate the sleep behavior.

Suggested change
var sleep = Duration.ofMillis(0);
var sleep = Duration.ofMillis(1);

Copilot uses AI. Check for mistakes.

var input = new Input(max, sleep);
var task = new LongIncrementingTask(input, System.out::println);
try (var executor = new TaskExecutor()) {
executor.await(task);
System.out.println(task);
}

simpleLoop(max, sleep);
}
}
45 changes: 45 additions & 0 deletions src/dev/enola/be/task/demo/Threads.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package dev.enola.be.task.demo;

import java.time.Duration;

final class Threads {

// from
// https://github.com/enola-dev/enola/blob/main/java/dev/enola/common/concurrent/Threads.java

/**
* Sleep 😴 for a certain duration.
*
* <p>This is a wrapper around {@link Thread#sleep(Duration)} which (correctly) handles its
* {@link InterruptedException} by (re-)interrupting the current thread, and then re-throwing it
* as a checked exception. It also checks for negative duration (which the original method just
* ignores, which could hide bugs), and has an optimizing shortcut for duration 0.
Comment on lines +15 to +16
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The Javadoc states that this wrapper checks for negative duration which the "original method" ignores. This is true for Thread.sleep(long), but Thread.sleep(Duration), which is used here, already throws an IllegalArgumentException for negative durations. The comment could be clarified to avoid confusion, for example by specifying which Thread.sleep overload it refers to.

*
* <p>See <a href="https://www.baeldung.com/java-interrupted-exception">Baeldung's related
* article</a>, or <a
* href="https://www.yegor256.com/2015/10/20/interrupted-exception.html">yegor256.com Blog
* Post</a> and <a href="https://github.com/google/guava/issues/1219">Google Guava Issue
* #1219</a>, as well as Google Guava's Uninterruptibles.sleepUninterruptibly(Duration) (which does
* something different from this).
*
* @param duration Duration to sleep
* @throws InterruptedException if interrupted
* @throws IllegalArgumentException if duration is negative
*/
public static void sleep(Duration duration) throws InterruptedException {
if (duration.isNegative())
throw new IllegalArgumentException(duration + " cannot be negative");

if (duration.isZero()) return;

try {
Thread.sleep(duration);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
// WAS: throw new UncheckedInterruptedException(e);
throw e;
}
}

private Threads() {}
}