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

com.github.libxjava.concurrent.ScheduledTaskExecutor Maven / Gradle / Ivy

The newest version!
/*
 * libxjava -- utility library for cross-Java-platform development
 *             Lib-Cross-Java CDC
 *
 * Copyright (c) 2010 Marcel Patzlaff ([email protected])
 *
 * This library is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library.  If not, see .
 */

package com.github.libxjava.concurrent;


/**
 *
 * @author Marcel Patzlaff
 * @version libxjava-cdc - 0.3
 */
public class ScheduledTaskExecutor {
    private static final class TaskQueue {
        private TaskFuture _head;
        private int _count;
        private int _waitingThreads;
        
        TaskQueue() {
            _head= null;
            _waitingThreads= 0;
            _count= 0;
        }
        
        synchronized void addTask(TaskFuture task) {
            if(task.next != null) {
                throw new IllegalArgumentException("task already in queue");
            }
            
            if(_head == null) {
                _head= task;
                this.notify();
            } else if(_head.start > task.start) {
                task.next= _head;
                _head= task;
                this.notify();
            } else {
                TaskFuture previous= _head;
                for(TaskFuture comp= previous.next; comp != null && comp.start <= task.start; previous= comp, comp= comp.next);
                task.next= previous.next;
                previous.next= task;
            }
            
            _count++;
        }
        
        synchronized void removeTask(TaskFuture task) {
            if(_head == task) {
                _head= task.next;
                task.next= null;
                _count--;
                return;
            }
            TaskFuture previous= _head;
            for(; previous != null && previous.next != task; previous= previous.next);
            
            if(previous != null) {
                previous.next= task.next;
                task.next= null;
                _count--;
            }
        }
        
        synchronized TaskFuture waitForTask(final long deadline) throws InterruptedException {
            _waitingThreads++;
            try {
                for(;;) {
                    long currentTime= System.currentTimeMillis();
                    long waitUntil= deadline;
                    if(_head != null) {
                        if(currentTime >= _head.start) {
                            TaskFuture task= _head;
                            _head= _head.next;
                            task.next= null;
                            _count--;
                            return task;
                        } else {
                            waitUntil= _head.start;
                        }
                    }
                    
                    if(currentTime >= deadline) {
                        return null;
                    }
                    
                    long toWait= waitUntil - currentTime;
                    if(toWait > 0) {
                        this.wait(toWait);
                    }
                }
            } finally {
                _waitingThreads--;
            }
        }
        
        synchronized boolean hasUnassignedTasks() {
            return _waitingThreads <= 0 && _count > 0;
        }
    }
    
    private final class Worker implements Runnable {
        protected Thread thread;
        
        protected Worker() {
        }
        
        public void run() {
            try {
                TaskFuture task= null;
                while((task= getTask()) != null) {
                    executeTask(task);
                    task= null;
                }
            } finally {
                totalThreads.updateAndGet(-1);
            }
        }
        
        private void executeTask(TaskFuture task) {
            beforeExecute(thread, task);
            Throwable t= null;
            try {
                task.doRun();
            } catch (RuntimeException re) {
                t= re;
            }
            afterExecute(task, t);
        }
    }
    
    /*package*/ final AtomicNumber totalThreads;
    
    private final IThreadFactory _threadFactory;
    private final TaskQueue _taskQueue;
    private final int _maxPoolSize;
    private final long _keepAliveTimeInMillis;
    
    public ScheduledTaskExecutor(int initialPoolSize, int maxPoolSize, long keepAliveTimeInMillis, IThreadFactory threadFactory) {
        if(initialPoolSize < 0 || maxPoolSize < 0 || initialPoolSize > maxPoolSize) {
            throw new IllegalArgumentException("pool sizes");
        }
        
        _maxPoolSize= maxPoolSize;
        _threadFactory= threadFactory;
        _taskQueue= new TaskQueue();
        _keepAliveTimeInMillis= keepAliveTimeInMillis;
        this.totalThreads= new AtomicNumber(initialPoolSize);
        
        for(int i= 0; i < initialPoolSize; i++) {
            createAndStartThread();
        }
    }
    
    public TaskFuture submit(Runnable target) {
        return scheduleAtFixedRate(target, 0L, 0L);
    }
    
    public TaskFuture schedule(Runnable target, long delayInMillis) {
        return scheduleAtFixedRate(target, delayInMillis, 0L);
    }
    
    public TaskFuture scheduleAtFixedRate(Runnable target, long delayInMillis, long periodInMillis) {
        if(delayInMillis < 0 || periodInMillis < 0) {
            throw new IllegalArgumentException("delay or period");
        }
        
        TaskFuture task= createAndInitialiseTaskFuture(target, delayInMillis, periodInMillis);
        addTaskForExecution(task);
        return task;
    }
    
    protected void beforeExecute(Thread workThread, TaskFuture task) {
        // do nothing
    }
    
    protected void afterExecute(TaskFuture task, Throwable t) {
        // do nothing
    }
    
    protected TaskFuture createTaskFuture(Object target) {
        return new TaskFuture();
    }
    
    /*package*/ TaskFuture getTask() {
        long deadline= _keepAliveTimeInMillis + System.currentTimeMillis();
        try {
            for(;;) {
                try {
                    return _taskQueue.waitForTask(deadline);
                } catch (InterruptedException e) {
                    // TODO: check for shutdown etc
                }
            }
        } finally {
            ensureEnoughThreadsStarted();
        }
    }
    
    /*package*/ void addTask(TaskFuture task) {
        _taskQueue.addTask(task);
    }
    
    /*package*/ void removeTask(TaskFuture task) {
        _taskQueue.removeTask(task);
    }
    
    private TaskFuture createAndInitialiseTaskFuture(Runnable target, long delay, long period) {
        TaskFuture task= createTaskFuture(target);
        task.executor= this;
        task.target= target;
        task.start= System.currentTimeMillis() + delay;
        task.period= period;
        return task;
    }
    
    private void createAndStartThread() {
        Worker worker= new Worker();
        Thread thr= _threadFactory.newThread(worker);
        worker.thread= thr;
        thr.start();
    }
    
    private void addTaskForExecution(TaskFuture task) {
        _taskQueue.addTask(task);
        ensureEnoughThreadsStarted();
    }
    
    private void ensureEnoughThreadsStarted() {
        while(_taskQueue.hasUnassignedTasks()) {
            int threadCount= totalThreads.get();
            if(threadCount < _maxPoolSize) {
                if(totalThreads.compareAndSet(threadCount, threadCount + 1)) {
                    createAndStartThread();
                    break;
                }
            } else {
                // FIXME: what should we do here?
                System.err.println("WARNING: not enough worker threads");
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy