xeij/SimpleTask.java
//========================================================================================
// SimpleTask.java
// en:Simple task
// ja:シンプルタスク
// Copyright (C) 2003-2026 Makoto Kamada
//
// This file is part of the XEiJ (X68000 Emulator in Java).
// You can use, modify and redistribute the XEiJ if the conditions are met.
// Read the XEiJ License for more details.
// https://stdkmd.net/xeij/
//========================================================================================
package xeij;
import java.util.concurrent.*; //ScheduledExecutorService
public class SimpleTask implements Runnable {
//interface SimpleTask.Runner
// タスクの本体
public interface Runner {
//n = runN (n)
// nは初回は1、次回からは前回返した値。
// 0を返すと終了、0以外を返すと継続。
// runN(n)の内側からstop()を使って停止させようとしないこと。
// スレッドを占有しないように適度に細切れにすること。
public int runN (int n);
} //interface Runner
//interface SimpleTask.FinalizableRunner
// 後始末付きタスクの本体
public interface FinalizableRunner extends Runner {
//finalizeN (n)
// 後始末をする
// nはstop()で停止したときは次にrunN(n)に渡す予定だったn、
// 最後まで実行したときは0。
// 最後のrunN(n)の後に1回だけfinalizeN(n)が呼び出される。
// シャットダウンでは呼び出されない(ことがある)ので、
// 後始末が必要なときは必ずstop()すること。
public void finalizeN (int n);
} //interface FinalizableRunner
//状態
public static final int STOPPED = 0; //停止中
public static final int ONE_SHOT = 1; //1回実行中
public static final int FIXED_DELAY = 2; //固定間隔実行中
public static final int FIXED_PERIOD = 3; //固定周期実行中
//引数
// https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/concurrent/ScheduledExecutorService.html
private ScheduledExecutorService executor; //スレッド
private Runner runner; //タスクの本体
//変数
private volatile int status; //状態。STOPPED、ONE_SHOT、FIXED_DELAY、FIXED_PERIODのいずれか
private volatile int n; //(実行中のとき)次回のrunN(n)の引数
private volatile boolean lastRun; //(実行中のとき)次回のrun()が最後
private volatile long delayOrPeriod; //(実行中のとき)現在の間隔または周期
private volatile long nextDelayOrPeriod; //(実行中のとき)次回の間隔または周期
private final Object lock = new Object (); //futureが確定するまでrun()に読ませない
private volatile ScheduledFuture<?> future; //(実行中のとき)scheduleが返したfuture
//task = new SimpleTask (executor, runner)
// コンストラクタ
public SimpleTask (ScheduledExecutorService executor, Runner runner) {
this.executor = executor;
this.runner = runner;
status = STOPPED; //停止中
} //new SimpleTask
//task = task.fixedDelay (initialDelay, delay)
// 固定間隔実行を予約する。
public SimpleTask fixedDelay (long initialDelay, long delay) {
if (status != STOPPED) { //実行中
throw new IllegalStateException ("task is running");
}
n = 1; //初回は1
status = FIXED_DELAY; //固定間隔実行中
lastRun = false; //次回のrun()が最後ではない
delayOrPeriod =
nextDelayOrPeriod = delay;
synchronized (lock) {
future = executor.scheduleWithFixedDelay (
this, initialDelay, delay, TimeUnit.MILLISECONDS); //固定間隔実行
}
return this;
} //fixedDelay
//task = task.fixedPeriod (initialDelay, period)
// 固定周期実行を予約する。
public SimpleTask fixedPeriod (long initialDelay, long period) {
if (status != STOPPED) { //実行中
throw new IllegalStateException ("task is running");
}
status = FIXED_PERIOD; //固定周期実行中
n = 1; //初回は1
lastRun = false; //次回のrun()が最後ではない
delayOrPeriod =
nextDelayOrPeriod = period;
synchronized (lock) {
future = executor.scheduleAtFixedRate (
this, initialDelay, period, TimeUnit.MILLISECONDS); //固定頻度実行
}
return this;
} //fixedPeriod
//task.gaze ()
// 終了するまで待つ。
public void gaze () {
if (status != STOPPED) { //実行中
try {
future.get ();
} catch (Exception e) {
}
}
} //gaze
//task.gaze (timeout)
// 終了するかタイムアウトまで待つ。
public void gaze (long timeout) {
if (status != STOPPED) { //実行中
try {
future.get (timeout, TimeUnit.MILLISECONDS);
} catch (Exception e) {
}
}
} //gaze
//status = task.getStatus ()
// 状態を得る。
// STOPPED、ONE_SHOT、FIXED_DELAY、FIXED_PERIODのいずれかを返す
public int getStatus () {
return status;
} //getStatus
//task = task.oneShot (initialDelay)
// 1回実行を予約する。
public SimpleTask oneShot (long initialDelay) {
if (status == STOPPED) { //実行中
throw new IllegalStateException ("task is running");
}
status = ONE_SHOT; //1回実行中
n = 1; //初回は1
lastRun = true; //次回のrun()が最後
synchronized (lock) {
future = executor.schedule (
this, initialDelay, TimeUnit.MILLISECONDS); //1回実行
}
return this;
} //oneShot
//run ()
// タスク
@Override public void run () {
n = runner.runN (n); //タスクの本体
if (n == 0 || //終了
lastRun) { //次回のrun()が最後
synchronized (lock) {
future.cancel (false); //次回以降のrun()をキャンセルする
}
if (runner instanceof FinalizableRunner) {
((FinalizableRunner) runner).finalizeN (n); //後始末をする
}
status = STOPPED; //停止中
return;
}
if (delayOrPeriod != nextDelayOrPeriod) { //間隔または周期が変更された
delayOrPeriod = nextDelayOrPeriod;
if (status == FIXED_DELAY) {
System.out.println("new delay is "+delayOrPeriod);
synchronized (lock) {
future.cancel (false);
future = executor.scheduleWithFixedDelay (
this, delayOrPeriod, delayOrPeriod, TimeUnit.MILLISECONDS); //固定間隔実行
}
} else if (status == FIXED_PERIOD) {
synchronized (lock) {
future.cancel (false);
future = executor.scheduleAtFixedRate (
this, delayOrPeriod, delayOrPeriod, TimeUnit.MILLISECONDS); //固定頻度実行
}
}
}
} //run
//task.setDelay (delay)
// 固定間隔実行の間隔を変更する。
public void setDelay (long delay) {
nextDelayOrPeriod = delay;
} //setDelay
//task.setPeriod (period)
// 固定周期実行の周期を変更する。
public void setPeriod (long period) {
nextDelayOrPeriod = period;
} //setPeriod
//sleep (timeout)
// タイムアウトまで待つ。
// InterruptedExceptionがあるとタイムアウトを待たない。
public static void sleep (long timeout) {
try {
Thread.sleep (timeout);
} catch (InterruptedException ie) {
}
} //sleep
//task = task.stop ()
// 実行中のとき次回以降のrun()をキャンセルして終了するまで待つ。
// runN(n)の内側からstop()を使って停止させようとしないこと。
public SimpleTask stop () {
if (status != STOPPED) { //実行中
lastRun = true; //次回のrun()が最後
future.cancel (false); //次回以降のrun()をキャンセルする
try {
future.get (); //run()が終わるまで待つ
} catch (Exception e) {
}
if (status != STOPPED) { //実行中。最後のrun()が後始末をしなかった
if (runner instanceof FinalizableRunner) {
((FinalizableRunner) runner).finalizeN (n); //後始末をする
}
status = STOPPED; //停止中
}
}
return this;
} //stop
} //class SimpleTask