All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgroups.util.TimeScheduler3 Maven / Gradle / Ivy

There is a newer version: 9.1.7.Final
Show newest version

package org.jgroups.util;


import org.jgroups.Global;
import org.jgroups.logging.Log;
import org.jgroups.logging.LogFactory;

import java.util.List;
import java.util.concurrent.*;
import java.util.function.Consumer;
import java.util.function.Function;


/**
 * Implementation of {@link TimeScheduler}. Uses a {@link DelayQueue} to order tasks according to execution times
 * @author Bela Ban
 * @since  3.3
 */
public class TimeScheduler3 implements TimeScheduler, Runnable {
    /** Thread pool used to execute the tasks */
    protected Executor                    pool;

    /** DelayQueue with tasks being sorted according to execution times (next execution first) */
    protected final BlockingQueue   queue=new DelayQueue<>();

    /** Thread which removes tasks ready to be executed from the queue and submits them to the pool for execution */
    protected volatile Thread             runner;

    protected static final Log            log=LogFactory.getLog(TimeScheduler3.class);

    protected ThreadFactory               timer_thread_factory;

    // if true, non-blocking timer tasks are run directly by the runner thread and not submitted to the thread pool
    protected boolean                     non_blocking_task_handling=true;

    protected enum TaskType               {dynamic, fixed_rate, fixed_delay}


    /**
     * Create a scheduler that executes tasks in dynamically adjustable intervals
     */
    public TimeScheduler3() {
        pool=new ThreadPoolExecutor(4, 10,
                                    30000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100),
                                    Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
        start();
    }


    public TimeScheduler3(ThreadFactory factory, int min_threads, int max_threads, long keep_alive_time, int max_queue_size,
                          String rejection_policy) {
        this(factory, min_threads, max_threads, keep_alive_time, new ArrayBlockingQueue<>(max_queue_size), rejection_policy, true);
    }

    public TimeScheduler3(ThreadFactory factory, int min_threads, int max_threads, long keep_alive_time,
                          BlockingQueue queue, String rejection_policy, boolean thread_pool_enabled) {
        timer_thread_factory=factory;
        pool=thread_pool_enabled?
          new ThreadPoolExecutor(min_threads, max_threads,keep_alive_time, TimeUnit.MILLISECONDS,
                                 queue, factory, Util.parseRejectionPolicy(rejection_policy))
          : new DirectExecutor();
        start();
    }

    public TimeScheduler3(Executor thread_pool, ThreadFactory factory) {
        timer_thread_factory=factory;
        pool=thread_pool;
        start();
    }

    public void    setThreadFactory(ThreadFactory f)     {condSet((p) -> p.setThreadFactory(f));}
    public void    setThreadPool(Executor new_pool)      {pool=new_pool;}
    public int     getMinThreads()                       {return condGet(ThreadPoolExecutor::getCorePoolSize, 0);}
    public void    setMinThreads(int size)               {condSet(p -> p.setCorePoolSize(size));}
    public int     getMaxThreads()                       {return condGet(ThreadPoolExecutor::getMaximumPoolSize, 0);}
    public void    setMaxThreads(int size)               {condSet(p -> p.setMaximumPoolSize(size));}
    public long    getKeepAliveTime()                    {return condGet(p -> p.getKeepAliveTime(TimeUnit.MILLISECONDS), 0L);}
    public void    setKeepAliveTime(long time)           {condSet(p -> p.setKeepAliveTime(time, TimeUnit.MILLISECONDS));}
    public int     getCurrentThreads()                   {return condGet(ThreadPoolExecutor::getPoolSize, 0);}
    public int     getQueueSize()                        {return condGet(p -> p.getQueue().size(), 0);}
    public int     size()                                {return queue.size();}
    public String  toString()                            {return getClass().getSimpleName();}
    public boolean isShutdown()                          {return condGet(ThreadPoolExecutor::isShutdown, false);}
    public boolean getNonBlockingTaskHandling()          {return non_blocking_task_handling;}
    public void    setNonBlockingTaskHandling(boolean b) {this.non_blocking_task_handling=b;}


    public String dumpTimerTasks() {
        StringBuilder sb=new StringBuilder();
        for(Task task: queue) {
            sb.append(task);
            if(task.isCancelled())
                sb.append(" (cancelled)");
            sb.append("\n");
        }
        return sb.toString();
    }



    public void execute(Runnable task, boolean can_block) {
        submitToPool(task instanceof TimeScheduler.Task?
                       new RecurringTask(task, TaskType.dynamic, 0, ((TimeScheduler.Task)task).nextInterval(), TimeUnit.MILLISECONDS, can_block)
                       : new Task(task, can_block)); // we'll execute the task directly
    }


    public Future schedule(Runnable work, long initial_delay, TimeUnit unit, boolean can_block) {
        return doSchedule(new Task(work, initial_delay, unit, can_block), initial_delay);
    }



    public Future scheduleWithFixedDelay(Runnable work, long initial_delay, long delay, TimeUnit unit, boolean can_block) {
        return scheduleRecurring(work, TaskType.fixed_delay, initial_delay, delay, unit, can_block);
    }


    public Future scheduleAtFixedRate(Runnable work, long initial_delay, long delay, TimeUnit unit, boolean can_block) {
        return scheduleRecurring(work,TaskType.fixed_rate,initial_delay,delay,unit, can_block);
    }


    /**
     * Schedule a task for execution at varying intervals. After execution, the task will get rescheduled after
     * {@link org.jgroups.util.TimeScheduler.Task#nextInterval()} milliseconds. The task is never done until
     * nextInterval() returns a value <= 0 or the task is cancelled.

* Note that the task is rescheduled relative to the last time is actually executed. This is similar to * {@link #scheduleWithFixedDelay(Runnable,long,long,java.util.concurrent.TimeUnit)}. * @param work the task to execute */ public Future scheduleWithDynamicInterval(TimeScheduler.Task work, boolean can_block) { return scheduleRecurring(work, TaskType.dynamic, work.nextInterval(), 0, TimeUnit.MILLISECONDS, can_block); } protected void start() { startRunner(); } /** * Stops the timer, cancelling all tasks */ public void stop() { stopRunner(); // we may need to do multiple iterations as the iterator works on a copy and tasks might have been added just // after the iterator() call returned while(!queue.isEmpty()) for(Task entry: queue) { entry.cancel(true); queue.remove(entry); } queue.clear(); if(pool instanceof ThreadPoolExecutor) { ThreadPoolExecutor p=(ThreadPoolExecutor)pool; List remaining_tasks=p.shutdownNow(); remaining_tasks.stream().filter(task -> task instanceof Future).forEach(task -> ((Future)task).cancel(true)); p.getQueue().clear(); try { p.awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME, TimeUnit.MILLISECONDS); } catch(InterruptedException e) { } } // clears the threads list (https://issues.jboss.org/browse/JGRP-1971) if(timer_thread_factory instanceof LazyThreadFactory) ((LazyThreadFactory)timer_thread_factory).destroy(); } public void run() { while(Thread.currentThread() == runner) { try { Task task=queue.take(); submitToPool(task); } catch(InterruptedException interrupted) { // flag is cleared and we check if the loop should be terminated at the top of the loop } catch(Throwable t) { log.error(Util.getMessage("FailedSubmittingTaskToThreadPool"), t); } } } protected Future scheduleRecurring(Runnable work, TaskType type, long initial_delay, long delay, TimeUnit unit, boolean can_block) { return doSchedule(new RecurringTask(work, type, initial_delay, delay, unit, can_block), initial_delay); } protected Future doSchedule(Task task, long initial_delay) { if(task.getRunnable() == null) throw new NullPointerException(); if (isShutdown()) return null; if(initial_delay <= 0) { submitToPool(task); return task; } return add(task); } protected void condSet(Consumer setter) { if(pool instanceof ThreadPoolExecutor) setter.accept((ThreadPoolExecutor)pool); } protected T condGet(Function getter, T default_value) { if(pool instanceof ThreadPoolExecutor) return getter.apply((ThreadPoolExecutor)pool); return default_value; } protected void submitToPool(Task task) { if(non_blocking_task_handling && !task.canBlock()) { task.run(); return; } try { pool.execute(task); } catch(RejectedExecutionException rejected) { // only thrown if rejection policy is "abort" Thread thread=timer_thread_factory != null? timer_thread_factory.newThread(task, "Timer temp thread") : new Thread(task, "Timer temp thread"); thread.start(); } } protected Task add(Task task) { if(!isRunning()) return null; queue.add(task); return task; } protected boolean isRunning() { Thread tmp=runner; return tmp != null && tmp.isAlive(); } protected synchronized void startRunner() { stopRunner(); runner=timer_thread_factory != null? timer_thread_factory.newThread(this, "Timer runner") : new Thread(this, "Timer runner"); runner.start(); } protected synchronized void stopRunner() { Thread tmp=runner; runner=null; if(tmp != null) { tmp.interrupt(); try {tmp.join(500);} catch(InterruptedException e) {} } queue.clear(); } public static class Task implements Runnable, Delayed, Future { protected final Runnable runnable; // the task to execute protected long creation_time; // time (in ns) at which the task was created protected long delay; // time (in ns) after which the task should execute protected volatile boolean cancelled; protected volatile boolean done; protected final boolean can_block; public Task(Runnable runnable, boolean can_block) { this.runnable=runnable; this.can_block=can_block; } public Task(Runnable runnable, long initial_delay, TimeUnit unit, boolean can_block) { this.can_block=can_block; this.creation_time=System.nanoTime(); this.delay=TimeUnit.NANOSECONDS.convert(initial_delay, unit); this.runnable=runnable; if(runnable == null) throw new IllegalArgumentException("runnable cannot be null"); } public Runnable getRunnable() {return runnable;} public boolean canBlock() {return can_block;} public int compareTo(Delayed o) { long my_delay=getDelay(TimeUnit.NANOSECONDS), other_delay=o.getDelay(TimeUnit.NANOSECONDS); // return Long.compare(my_delay, other_delay); // JDK 7 only return (my_delay < other_delay) ? -1 : ((my_delay == other_delay) ? 0 : 1); } public long getDelay(TimeUnit unit) { // time (in ns) until execution, can be negative when already elapsed long remaining_time=delay - (System.nanoTime() - creation_time); return unit.convert(remaining_time, TimeUnit.NANOSECONDS); } public boolean cancel(boolean mayInterruptIfRunning) { boolean retval=!isDone(); cancelled=true; return retval; } public boolean isCancelled() {return cancelled;} public boolean isDone() {return done || cancelled;} public Object get() throws InterruptedException, ExecutionException {return null;} public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return null; } public void run() { if(isDone()) return; try { runnable.run(); } catch(Throwable t) { log.error(Util.getMessage("FailedExecutingTask") + runnable, t); } finally { done=true; } } public String toString() { return String.format("%s (can block=%b)", runnable.toString(), can_block); } } /** Tasks which runs more than once, either dynamic, fixed-rate or fixed-delay, until cancelled */ protected class RecurringTask extends Task { protected final TaskType type; protected final long period; // ns protected final long initial_delay; // ns protected int cnt=1; // number of invocations (for fixed rate invocations) public RecurringTask(Runnable runnable, TaskType type, long initial_delay, long delay, TimeUnit unit, boolean can_block) { super(runnable, initial_delay, unit, can_block); this.initial_delay=TimeUnit.NANOSECONDS.convert(initial_delay, TimeUnit.MILLISECONDS); this.type=type; period=TimeUnit.NANOSECONDS.convert(delay, unit); if(type == TaskType.dynamic && !(runnable instanceof TimeScheduler.Task)) throw new IllegalArgumentException("Need to provide a TimeScheduler.Task as runnable when type is dynamic"); } public void run() { if(isDone()) return; super.run(); if(cancelled) return; done=false; // run again switch(type) { case dynamic: long next_interval=TimeUnit.NANOSECONDS.convert(((TimeScheduler.Task)runnable).nextInterval(), TimeUnit.MILLISECONDS); if(next_interval <= 0) { if(log.isTraceEnabled()) log.trace("task will not get rescheduled as interval is " + next_interval); done=true; return; } creation_time=System.nanoTime(); delay=next_interval; break; case fixed_rate: delay=initial_delay + cnt++ * period; break; case fixed_delay: creation_time=System.nanoTime(); delay=period; break; } add(this); // schedule this task again } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy