/* * 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.mobicents.protocols.ss7.scheduler; import org.apache.log4j.Logger; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * 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 Scheduler implements SchedulerMBean { // MANAGEMENT QUEUE SHOULD CONTAIN ONLY TASKS THAT ARE NOT TIME DEPENDENT public static final Integer MANAGEMENT_QUEUE = 0; // MTP2/SCTP READ public static final Integer L2READ_QUEUE = 1; // MTP3/M3UA READ public static final Integer L3READ_QUEUE = 2; // ISUP / SCCP READ public static final Integer L4READ_QUEUE = 3; // TCAP READ public static final Integer TCAPREAD_QUEUE = 4; // MAP/INUP and other APP LAYER READ public static final Integer APPREAD_QUEUE = 5; // MAP/INUP and other APP LAYER WRITE public static final Integer APPWRITE_QUEUE = 6; // TCAP WRITE public static final Integer TCAPWRITE_QUEUE = 7; // ISUP / SCCP WRITE public static final Integer L4WRITE_QUEUE = 8; // MTP3/M3UA WRITE public static final Integer L3WRITE_QUEUE = 9; // MTP2/SCTP WRITE public static final Integer L2WRITE_QUEUE = 10; // INTERNETWORKING OCCURES OVER L3 NO HIGHER LAYERS ARE USED // BASICALLY DOEST NOT MATTER WHAT QUEUE WE CHOOSE , IT SHOULD BE BETWEEN L3READ_QUEUE AND L3WRITE_QUEUE public static final Integer INTERNETWORKING_QUEUE = 3; public static final Integer HEARTBEAT_QUEUE = -1; // The clock for time measurement private Clock clock; // priority queue protected OrderedTaskQueue[] taskQueues = new OrderedTaskQueue[11]; protected OrderedTaskQueue heartBeatQueue; // CPU bound threads private CpuThread cpuThread; // flag indicating state of the scheduler private boolean isActive; private Logger logger = Logger.getLogger(Scheduler.class); /** * Creates new instance of scheduler. */ public Scheduler() { for (int i = 0; i < taskQueues.length; i++) taskQueues[i] = new OrderedTaskQueue(); heartBeatQueue = new OrderedTaskQueue(); cpuThread = new CpuThread(String.format("Scheduler")); } /** * 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.accept(task); } /** * 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 "); cpuThread.activate(); logger.info("Started "); } /** * Stops scheduler. */ public void stop() { if (!this.isActive) { return; } cpuThread.shutdown(); try { Thread.sleep(40); } catch (InterruptedException e) { } for (int i = 0; i < taskQueues.length; i++) taskQueues[i].clear(); heartBeatQueue.clear(); } // removed statistics to increase perfomance /** * Shows the miss rate. * * @return the miss rate value; */ public double getMissRate() { return 0; } public long getWorstExecutionTime() { return 0; } public void notifyCompletion() { cpuThread.notifyCompletion(); } /** * Executor thread. */ private class CpuThread extends Thread { private volatile boolean active; private int currQueue = MANAGEMENT_QUEUE; private AtomicInteger activeTasksCount = new AtomicInteger(); private long cycleStart = 0; private int runIndex = 0; private ExecutorService eservice; private Object LOCK = new Object(); public CpuThread(String name) { super(name); int size = Runtime.getRuntime().availableProcessors() * 2; eservice = new ThreadPoolExecutor(size, size, 0L, TimeUnit.MILLISECONDS, new ConcurrentLinkedList<Runnable>()); } public void activate() { this.active = true; this.start(); } public void notifyCompletion() { int newValue = activeTasksCount.decrementAndGet(); if (newValue == 0 && this.active) synchronized (LOCK) { LOCK.notify(); } } @Override public void run() { long cycleDuration, cycleDuration2; cycleStart = System.nanoTime(); while (active) { while (currQueue <= L2WRITE_QUEUE) { synchronized (LOCK) { if (executeQueue(taskQueues[currQueue])) try { LOCK.wait(); } catch (InterruptedException e) { // lets continue } } currQueue++; } runIndex = (runIndex + 1) % 25; if (runIndex == 0) { synchronized (LOCK) { if (executeQueue(heartBeatQueue)) try { LOCK.wait(); } catch (InterruptedException e) { // lets continue } } } // sleep till next cycle cycleDuration = System.nanoTime() - 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 // cycleDuration2=System.nanoTime() - cycleStart; cycleStart = cycleStart + 4000000L; currQueue = MANAGEMENT_QUEUE; // if(cycleDuration2>4100000L) // System.out.println("TIME LONGER THEN 4.1MS,DURATION:" + cycleDuration); // else if(cycleDuration2<3900000L) // System.out.println("TIME SHORTER THEN 3.9MS,DURATION:" + cycleDuration); } } private boolean executeQueue(OrderedTaskQueue currQueue) { Task t; currQueue.changePool(); int currQueueSize = currQueue.size(); activeTasksCount.set(currQueueSize); t = currQueue.poll(); // submit all tasks in current queue while (t != null) { eservice.execute(t); t = currQueue.poll(); } return currQueueSize != 0; } /** * Terminates thread. */ private void shutdown() { this.active = false; } } }