/* * JBoss, Home of Professional Open Source * Copyright 2011, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This 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 2.1 of * the License, or (at your option) any later version. * * This software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.restcomm.media.scheduler; import java.lang.InterruptedException; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.LockSupport; import org.apache.log4j.Logger; import org.restcomm.media.concurrent.ConcurrentCyclicFIFO; /** * Implements scheduler with multi-level priority queue. * * This scheduler implementation follows to uniprocessor model with "super" thread. * The "super" thread includes IO bound thread and one or more CPU bound threads * with equal priorities. * * The actual priority is assigned to task instead of process and can be * changed dynamically at runtime using the initial priority level, feedback * and other parameters. * * * @author Oifa Yulian */ public class PriorityQueueScheduler { //SS7 QUEUES public static final Integer RECEIVER_QUEUE=0; public static final Integer SENDER_QUEUE=1; //MANAGEMENT QUEUE FOR CONTROL PROCESSING public static final Integer MANAGEMENT_QUEUE=2; //UDP MANAGER QUEUE FOR POOLING CHANNELS public static final Integer UDP_MANAGER_QUEUE=3; //CORE QUEUES public static final Integer INPUT_QUEUE=4; public static final Integer MIXER_MIX_QUEUE=5; public static final Integer OUTPUT_QUEUE=6; //HEARTBEAT QUEUE public static final Integer HEARTBEAT_QUEUE=-1; //The clock for time measurement private Clock clock; //priority queue protected OrderedTaskQueue[] taskQueues = new OrderedTaskQueue[7]; protected OrderedTaskQueue[] heartBeatQueue = new OrderedTaskQueue[5]; //CPU bound threads private CoreThread coreThread; private CriticalThread criticalThread; //flag indicating state of the scheduler private boolean isActive; private Logger logger = Logger.getLogger(PriorityQueueScheduler.class) ; private ConcurrentCyclicFIFO<Task> waitingTasks=new ConcurrentCyclicFIFO<Task>(); private ConcurrentCyclicFIFO<Task> criticalTasks=new ConcurrentCyclicFIFO<Task>(); private WorkerThread[] workerThreads; private CriticalWorkerThread[] criticalWorkerThreads; /** * Creates new instance of scheduler. */ public PriorityQueueScheduler(Clock clock) { this.clock = clock; for(int i=0;i<taskQueues.length;i++) { taskQueues[i]=new OrderedTaskQueue(); } for(int i=0;i<heartBeatQueue.length;i++) { heartBeatQueue[i]=new OrderedTaskQueue(); } coreThread = new CoreThread("scheduler-core"); criticalThread = new CriticalThread("scheduler-critical"); workerThreads=new WorkerThread[Runtime.getRuntime().availableProcessors()*2]; criticalWorkerThreads=new CriticalWorkerThread[Runtime.getRuntime().availableProcessors() / 2]; for(int i=0;i<workerThreads.length;i++) { workerThreads[i] = new WorkerThread("scheduler-worker-" + i); } for(int i=0;i<criticalWorkerThreads.length;i++) { criticalWorkerThreads[i] = new CriticalWorkerThread("scheduler-critical-worker-" + i); } } public PriorityQueueScheduler() { this(null); } public int getPoolSize() { return workerThreads.length; } /** * Sets clock. * * @param clock the clock used for time measurement. */ public void setClock(Clock clock) { this.clock = clock; } /** * Gets the clock used by this scheduler. * * @return the clock object. */ public Clock getClock() { return clock; } /** * Queues task for execution according to its priority. * * @param task the task to be executed. */ public void submit(Task task,Integer index) { task.activate(false); taskQueues[index].accept(task); } /** * Queues task for execution according to its priority. * * @param task the task to be executed. */ public void submitHeatbeat(Task task) { task.activate(true); heartBeatQueue[coreThread.runIndex].accept(task); } /** * Queues chain of the tasks for execution. * * @param taskChanin the chain of the tasks */ public void submit(TaskChain taskChain) { taskChain.start(); } /** * Starts scheduler. */ public void start() { if(this.isActive) return; if (clock == null) { throw new IllegalStateException("Clock is not set"); } this.isActive = true; logger.info("Starting "); coreThread.activate(); criticalThread.activate(); for(int i=0;i<workerThreads.length;i++) workerThreads[i].activate(); for(int i=0;i<criticalWorkerThreads.length;i++) criticalWorkerThreads[i].activate(); logger.info("Started "); } /** * Stops scheduler. */ public void stop() { if (!this.isActive) { return; } coreThread.shutdown(); criticalThread.shutdown(); for(int i=0;i<workerThreads.length;i++) workerThreads[i].shutdown(); for(int i=0;i<criticalWorkerThreads.length;i++) criticalWorkerThreads[i].shutdown(); try { Thread.sleep(40); } catch(InterruptedException e) { } for(int i=0;i<taskQueues.length;i++) taskQueues[i].clear(); for(int i=0;i<heartBeatQueue.length;i++) heartBeatQueue[i].clear(); } /** * Shows the miss rate. * * @return the miss rate value; */ public double getMissRate() { return 0; } public long getWorstExecutionTime() { return 0; } /** * Executor thread. */ private class CoreThread extends Thread { private volatile boolean active; private int currQueue=UDP_MANAGER_QUEUE; private AtomicInteger activeTasksCount=new AtomicInteger(); private long cycleStart=0; private int runIndex=0; private Object LOCK=new Object(); public CoreThread(String name) { super(name); } public void activate() { this.active = true; this.start(); } public void notifyCompletion() { if(activeTasksCount.decrementAndGet()==0) LockSupport.unpark(coreThread); } @Override public void run() { long cycleDuration; cycleStart = clock.getTime(); while(active) { long taskStart=cycleStart; currQueue=MANAGEMENT_QUEUE; while(currQueue<=OUTPUT_QUEUE) { executeQueue(taskQueues[currQueue]); while(activeTasksCount.get()!=0) LockSupport.park(); currQueue++; } executeQueue(taskQueues[MANAGEMENT_QUEUE]); while(activeTasksCount.get()!=0) LockSupport.park(); runIndex=(runIndex+1)%5; executeQueue(heartBeatQueue[runIndex]); while(activeTasksCount.get()!=0) LockSupport.park(); executeQueue(taskQueues[MANAGEMENT_QUEUE]); while(activeTasksCount.get()!=0) LockSupport.park(); //sleep till next cycle cycleDuration=clock.getTime() - cycleStart; if(cycleDuration<20000000L) try { sleep(20L-cycleDuration/1000000L,(int)((20000000L-cycleDuration)%1000000L)); } catch(InterruptedException e) { //lets continue } //new cycle starts , updating cycle start time by 4ms cycleStart = cycleStart + 20000000L; } } private void executeQueue(OrderedTaskQueue currQueue) { Task t; currQueue.changePool(); t = currQueue.poll(); //submit all tasks in current queue while(t!=null) { activeTasksCount.incrementAndGet(); waitingTasks.offer(t); t = currQueue.poll(); } } /** * Terminates thread. */ private void shutdown() { this.active = false; } } /** * Executor thread. */ private class CriticalThread extends Thread { private volatile boolean active; private AtomicInteger activeTasksCount=new AtomicInteger(); private long cycleStart=0; private Object LOCK=new Object(); public CriticalThread(String name) { super(name); } public void activate() { this.active = true; cycleStart = clock.getTime(); this.start(); } public void notifyCompletion() { if(activeTasksCount.decrementAndGet()==0) LockSupport.unpark(criticalThread); } @Override public void run() { long cycleDuration; while(active) { executeQueue(taskQueues[RECEIVER_QUEUE]); while(activeTasksCount.get()!=0) LockSupport.park(); executeQueue(taskQueues[SENDER_QUEUE]); while(activeTasksCount.get()!=0) LockSupport.park(); //sleep till next cycle cycleDuration=clock.getTime() - cycleStart; if(cycleDuration<4000000L) try { sleep(4L-cycleDuration/1000000L,(int)((4000000L-cycleDuration)%1000000L)); } catch(InterruptedException e) { //lets continue } //new cycle starts , updating cycle start time by 4ms cycleStart = cycleStart + 4000000L; } } private void executeQueue(OrderedTaskQueue currQueue) { Task t; currQueue.changePool(); t = currQueue.poll(); //submit all tasks in current queue while(t!=null) { activeTasksCount.incrementAndGet(); criticalTasks.offer(t); t = currQueue.poll(); } } /** * Terminates thread. */ private void shutdown() { this.active = false; } } private class WorkerThread extends Thread { private volatile boolean active; private Task current; public WorkerThread(String name) { super(name); } public void run() { while(active) { current=null; while(current==null) { try { current=waitingTasks.take(); } catch(Exception ex) { logger.warn("Could not poll waiting task in timely fashion. Will keep trying."); } } current.run(); coreThread.notifyCompletion(); } } public void activate() { this.active = true; this.start(); } /** * Terminates thread. */ private void shutdown() { this.active = false; } } private class CriticalWorkerThread extends Thread { private volatile boolean active; private Task current; public CriticalWorkerThread(String name) { super(name); } public void run() { while(active) { current=null; while(current==null) { try { current=criticalTasks.take(); } catch(Exception ex) { } } current.run(); criticalThread.notifyCompletion(); } } public void activate() { this.active = true; this.start(); } /** * Terminates thread. */ private void shutdown() { this.active = false; } } }