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