/***************************************************************** JADE - Java Agent DEvelopment Framework is a framework to develop multi-agent systems in compliance with the FIPA specifications. Copyright (C) 2000 CSELT S.p.A. GNU Lesser General Public License 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, version 2.1 of the License. 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *****************************************************************/ package jade.core; import java.util.Vector; import jade.util.leap.List; import jade.util.leap.LinkedList; import jade.util.leap.Iterator; import jade.util.leap.EnumIterator; import jade.util.leap.Serializable; import jade.core.behaviours.Behaviour; /** @author Giovanni Rimassa - Universita' di Parma @version $Date: 2005-12-16 16:37:25 +0100 (ven, 16 dic 2005) $ $Revision: 5844 $ */ /************************************************************** Name: Scheduler Responsibility and Collaborations: + Selects the behaviour to execute. (Behaviour) + Holds together all the behaviours of an agent. (Agent, Behaviour) + Manages the resources needed to synchronize and execute agent behaviours, such as thread pools, locks, etc. ****************************************************************/ class Scheduler implements Serializable { //#MIDP_EXCLUDE_BEGIN protected List readyBehaviours = new LinkedList(); protected List blockedBehaviours = new LinkedList(); //#MIDP_EXCLUDE_END /*#MIDP_INCLUDE_BEGIN protected Vector readyBehaviours = new Vector(); protected Vector blockedBehaviours = new Vector(); #MIDP_INCLUDE_END*/ /** @serial */ private Agent owner; /** @serial */ private int currentIndex; public Scheduler(Agent a) { owner = a; currentIndex = 0; } // Add a behaviour at the end of the behaviours queue. // This can never change the index of the current behaviour. // If the behaviours queue was empty notifies the embedded thread of // the owner agent that a behaviour is now available. public synchronized void add(Behaviour b) { //#MIDP_EXCLUDE_BEGIN readyBehaviours.add(b); //#MIDP_EXCLUDE_END /*#MIDP_INCLUDE_BEGIN readyBehaviours.addElement(b); #MIDP_INCLUDE_END*/ notify(); //#MIDP_EXCLUDE_BEGIN owner.notifyAddBehaviour(b); //#MIDP_EXCLUDE_END } // Moves a behaviour from the ready queue to the sleeping queue. public synchronized void block(Behaviour b) { if (removeFromReady(b)) { //#MIDP_EXCLUDE_BEGIN blockedBehaviours.add(b); //#MIDP_EXCLUDE_END /*#MIDP_INCLUDE_BEGIN blockedBehaviours.addElement(b); #MIDP_INCLUDE_END*/ //#MIDP_EXCLUDE_BEGIN owner.notifyChangeBehaviourState(b, Behaviour.STATE_READY, Behaviour.STATE_BLOCKED); //#MIDP_EXCLUDE_END } } // Moves a behaviour from the sleeping queue to the ready queue. public synchronized void restart(Behaviour b) { if (removeFromBlocked(b)) { //#MIDP_EXCLUDE_BEGIN readyBehaviours.add(b); //#MIDP_EXCLUDE_END /*#MIDP_INCLUDE_BEGIN readyBehaviours.addElement(b); #MIDP_INCLUDE_END*/ notify(); //#MIDP_EXCLUDE_BEGIN owner.notifyChangeBehaviourState(b, Behaviour.STATE_BLOCKED, Behaviour.STATE_READY); //#MIDP_EXCLUDE_END } } /** Restarts all behaviours. This method simply calls Behaviour.restart() on every behaviour. The Behaviour.restart() method then notifies the agent (with the Agent.notifyRestarted() method), causing Scheduler.restart() to be called (this also moves behaviours from the blocked queue to the ready queue --> we must copy all behaviours into a temporary buffer to avoid concurrent modification exceptions). Why not restarting only blocked behaviours? Some ready behaviour can be a ParallelBehaviour with some of its children blocked. These children must be restarted too. */ public synchronized void restartAll() { Behaviour[] behaviours = new Behaviour[readyBehaviours.size()]; int counter = 0; //#MIDP_EXCLUDE_BEGIN for(Iterator it = readyBehaviours.iterator(); it.hasNext();) //#MIDP_EXCLUDE_END /*#MIDP_INCLUDE_BEGIN for(Iterator it = new EnumIterator(readyBehaviours.elements()); it.hasNext();) #MIDP_INCLUDE_END*/ behaviours[counter++] = (Behaviour)it.next(); for(int i = 0; i < behaviours.length; i++) { Behaviour b = behaviours[i]; b.restart(); } behaviours = new Behaviour[blockedBehaviours.size()]; counter = 0; //#MIDP_EXCLUDE_BEGIN for(Iterator it = blockedBehaviours.iterator(); it.hasNext();) { //#MIDP_EXCLUDE_END /*#MIDP_INCLUDE_BEGIN for(Iterator it = new EnumIterator(blockedBehaviours.elements()); it.hasNext();) { #MIDP_INCLUDE_END*/ //#DOTNET_EXCLUDE_BEGIN behaviours[counter++] = (Behaviour)it.next(); //#DOTNET_EXCLUDE_END /*#DOTNET_INCLUDE_BEGIN Object tmpB = null; try // Hack: sometimes .NET inserts into this array a non-Behaviour object { tmpB = it.next(); behaviours[counter++] = (Behaviour)tmpB; } catch(ClassCastException cce) { System.out.println("Found an object of type "+tmpB.getClass().getName()+" instead of Behaviour"); cce.printStackTrace(); } #DOTNET_INCLUDE_END*/ } for(int i = 0; i < behaviours.length; i++) { Behaviour b = behaviours[i]; /*#DOTNET_INCLUDE_BEGIN if (b != null) #DOTNET_INCLUDE_END*/ b.restart(); } } /** Removes a specified behaviour from the scheduler */ public synchronized void remove(Behaviour b) { boolean found = removeFromBlocked(b); if(!found) { found = removeFromReady(b); } if (found) { //#MIDP_EXCLUDE_BEGIN owner.notifyRemoveBehaviour(b); //#MIDP_EXCLUDE_END } } /** Selects the appropriate behaviour for execution, with a trivial round-robin algorithm. */ public synchronized Behaviour schedule() throws InterruptedException { while(readyBehaviours.isEmpty()) { owner.idle(); } //#MIDP_EXCLUDE_BEGIN Behaviour b = (Behaviour)readyBehaviours.get(currentIndex); //#MIDP_EXCLUDE_END /*#MIDP_INCLUDE_BEGIN Behaviour b = (Behaviour)readyBehaviours.elementAt(currentIndex); #MIDP_INCLUDE_END*/ currentIndex = (currentIndex + 1) % readyBehaviours.size(); return b; } //#MIDP_EXCLUDE_BEGIN // Helper method for persistence service public synchronized Behaviour[] getBehaviours() { Behaviour[] result = new Behaviour[blockedBehaviours.size() + readyBehaviours.size()]; Iterator itReady = readyBehaviours.iterator(); Iterator itBlocked = blockedBehaviours.iterator(); for(int i = 0; i < result.length; i++) { Behaviour b = null; if(itReady.hasNext()) { b = (Behaviour)itReady.next(); } else { b = (Behaviour)itBlocked.next(); } result[i] = b; } return result; } // Helper method for persistence service public void setBehaviours(Behaviour[] behaviours) { readyBehaviours.clear(); blockedBehaviours.clear(); for(int i = 0; i < behaviours.length; i++) { Behaviour b = behaviours[i]; if(b.isRunnable()) { readyBehaviours.add(b); } else { blockedBehaviours.add(b); } } // The current index is not saved when persisting an agent currentIndex = 0; } //#MIDP_EXCLUDE_END // Removes a specified behaviour from the blocked queue. private boolean removeFromBlocked(Behaviour b) { //#MIDP_EXCLUDE_BEGIN return blockedBehaviours.remove(b); //#MIDP_EXCLUDE_END /*#MIDP_INCLUDE_BEGIN return blockedBehaviours.removeElement(b); #MIDP_INCLUDE_END*/ } // Removes a specified behaviour from the ready queue. // This can change the index of the current behaviour, so a check is // made: if the just removed behaviour has an index lesser than the // current one, then the current index must be decremented. private boolean removeFromReady(Behaviour b) { int index = readyBehaviours.indexOf(b); if(index != -1) { //#MIDP_EXCLUDE_BEGIN readyBehaviours.remove(b); //#MIDP_EXCLUDE_END /*#MIDP_INCLUDE_BEGIN readyBehaviours.removeElement(b); #MIDP_INCLUDE_END*/ if(index < currentIndex) --currentIndex; //if(currentIndex < 0) // currentIndex = 0; else if (index == currentIndex && currentIndex == readyBehaviours.size()) currentIndex = 0; } return index != -1; } }