/** * Copyright (c) 2007-2009 Alysson Bessani, Eduardo Alchieri, Paulo Sousa, and the authors indicated in the @author tags * * This file is part of SMaRt. * * SMaRt is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * SMaRt 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along with SMaRt. If not, see <http://www.gnu.org/licenses/>. */ package bftsmart.paxosatwar.executionmanager; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.TreeMap; import java.util.concurrent.locks.ReentrantLock; import bftsmart.paxosatwar.Consensus; import bftsmart.paxosatwar.messages.MessageFactory; import bftsmart.paxosatwar.messages.PaxosMessage; import bftsmart.paxosatwar.roles.Acceptor; import bftsmart.paxosatwar.roles.Proposer; import bftsmart.reconfiguration.ServerViewManager; import bftsmart.tom.core.TOMLayer; import bftsmart.tom.util.Logger; /** * This classe manages consensus instances. Each execution is a consensus * instance. It can have several rounds if there were problems during consensus. * * @author Alysson */ public final class ExecutionManager { private ServerViewManager reconfManager; private Acceptor acceptor; // Acceptor role of the PaW algorithm private Proposer proposer; // Proposer role of the PaW algorithm //******* EDUARDO BEGIN: now these variables are all concentrated in the Reconfigurationmanager **************// //private int me; // This process ID //private int[] acceptors; // Process ID's of all replicas, including this one //private int[] otherAcceptors; // Process ID's of all replicas, except this one //******* EDUARDO END **************// private Map<Integer, Execution> executions = new TreeMap<Integer, Execution>(); // Executions private ReentrantLock executionsLock = new ReentrantLock(); //lock for executions table // Paxos messages that were out of context (that didn't belong to the execution that was/is is progress private Map<Integer, List<PaxosMessage>> outOfContext = new HashMap<Integer, List<PaxosMessage>>(); // Proposes that were out of context (that belonged to future executions, and not the one running at the time) private Map<Integer, PaxosMessage> outOfContextProposes = new HashMap<Integer, PaxosMessage>(); private ReentrantLock outOfContextLock = new ReentrantLock(); //lock for out of context private boolean stopped = false; // Is the execution manager stopped? // When the execution manager is stopped, incoming paxos messages are stored here private Queue<PaxosMessage> stoppedMsgs = new LinkedList<PaxosMessage>(); private Round stoppedRound = null; // round at which the current execution was stoppped private ReentrantLock stoppedMsgsLock = new ReentrantLock(); //lock for stopped messages private TOMLayer tomLayer; // TOM layer associated with this execution manager private int paxosHighMark; // Paxos high mark for consensus instances /** THIS IS JOAO'S CODE, TO HANDLE THE STATE TRANSFER */ private int revivalHighMark; // Paxos high mark for consensus instances when this replica EID equals 0 private int timeoutHighMark; // Paxos high mark for a timed-out replica /******************************************************************/ /** * Creates a new instance of ExecutionManager * * @param acceptor Acceptor role of the PaW algorithm * @param proposer Proposer role of the PaW algorithm * @param acceptors Process ID's of all replicas, including this one * @param f Maximum number of replicas that can be faulty * @param me This process ID * @param initialTimeout initial timeout for rounds */ public ExecutionManager(ServerViewManager manager, Acceptor acceptor, Proposer proposer, int me) { //******* EDUARDO BEGIN **************// this.reconfManager = manager; this.acceptor = acceptor; this.proposer = proposer; //this.me = me; this.paxosHighMark = reconfManager.getStaticConf().getPaxosHighMark(); /** THIS IS JOAO'S CODE, TO HANDLE THE STATE TRANSFER */ this.revivalHighMark = reconfManager.getStaticConf().getRevivalHighMark(); this.timeoutHighMark = reconfManager.getStaticConf().getTimeoutHighMark(); /******************************************************************/ //******* EDUARDO END **************// } /** * Sets the TOM layer associated with this execution manager * @param tom The TOM layer associated with this execution manager */ public void setTOMLayer(TOMLayer tom) { this.tomLayer = tom; } /** * Returns the TOM layer associated with this execution manager * @return The TOM layer associated with this execution manager */ public TOMLayer getTOMLayer() { return tomLayer; } /** * Returns the acceptor role of the PaW algorithm * @return The acceptor role of the PaW algorithm */ public Acceptor getAcceptor() { return acceptor; } public Proposer getProposer() { return proposer; } public boolean stopped() { return stopped; } public Queue<PaxosMessage> getStoppedMsgs() { return stoppedMsgs; } public void clearStopped() { stoppedMsgs.clear(); } /** * Stops this execution manager */ public void stop() { Logger.println("(ExecutionManager.stoping) Stoping execution manager"); stoppedMsgsLock.lock(); this.stopped = true; if (tomLayer.getInExec() != -1) { stoppedRound = getExecution(tomLayer.getInExec()).getLastRound(); //stoppedRound.getTimeoutTask().cancel(); if (stoppedRound != null) Logger.println("(ExecutionManager.stop) Stoping round " + stoppedRound.getNumber() + " of consensus " + tomLayer.getInExec()); } stoppedMsgsLock.unlock(); } /** * Restarts this execution manager */ public void restart() { Logger.println("(ExecutionManager.restart) Starting execution manager"); stoppedMsgsLock.lock(); this.stopped = false; //process stopped messages while (!stoppedMsgs.isEmpty()) { PaxosMessage pm = stoppedMsgs.remove(); if (pm.getNumber() > tomLayer.getLastExec()) acceptor.processMessage(pm); } stoppedMsgsLock.unlock(); Logger.println("(ExecutionManager.restart) Finished stopped messages processing"); } /** * Checks if this message can execute now. If it is not possible, * it is stored in outOfContextMessages * * @param msg the received message * @return true in case the message can be executed, false otherwise */ public final boolean checkLimits(PaxosMessage msg) { outOfContextLock.lock(); int lastConsId = tomLayer.getLastExec(); int inExec = tomLayer.getInExec(); Logger.println("(ExecutionManager.checkLimits) Received message " + msg); Logger.println("(ExecutionManager.checkLimits) I'm at execution " + inExec + " and my last execution is " + lastConsId); boolean isRetrievingState = tomLayer.isRetrievingState(); if (isRetrievingState) { Logger.println("(ExecutionManager.checkLimits) I'm waiting for a state"); } boolean canProcessTheMessage = false; /** THIS IS JOAO'S CODE, TO HANDLE THE STATE TRANSFER */ // This serves to re-direct the messages to the out of context // while a replica is receiving the state of the others and updating itself if (isRetrievingState || // Is this replica retrieving a state? (!(lastConsId == -1 && msg.getNumber() >= (lastConsId + revivalHighMark)) && //not a recovered replica (msg.getNumber() > lastConsId && (msg.getNumber() < (lastConsId + paxosHighMark))) && // not an ahead of time message !(stopped && msg.getNumber() >= (lastConsId + timeoutHighMark)))) { // not a timed-out replica which needs to fetch the state if (stopped) {//just an optimization to avoid calling the lock in normal case stoppedMsgsLock.lock(); if (stopped) { Logger.println("(ExecutionManager.checkLimits) adding message for execution " + msg.getNumber() + " to stoopped"); //the execution manager was stopped, the messages should be stored //for later processing (when the execution is restarted) stoppedMsgs.add(msg); } stoppedMsgsLock.unlock(); } else { if (isRetrievingState || msg.getNumber() > (lastConsId + 1) || (inExec != -1 && inExec < msg.getNumber()) || (inExec == -1 && msg.getPaxosType() != MessageFactory.PROPOSE)) { //not propose message for the next consensus Logger.println("(ExecutionManager.checkLimits) Message for execution " + msg.getNumber() + " is out of context, adding it to out of context set"); //System.out.println("(ExecutionManager.checkLimits) Message for execution " + // msg.getNumber() + " is out of context, adding it to out of context set; isRetrievingState="+isRetrievingState); addOutOfContextMessage(msg); } else { //can process! Logger.println("(ExecutionManager.checkLimits) message for execution " + msg.getNumber() + " can be processed"); //Logger.debug = false; canProcessTheMessage = true; } } } else if ((lastConsId == -1 && msg.getNumber() >= (lastConsId + revivalHighMark)) || //recovered... (msg.getNumber() >= (lastConsId + paxosHighMark)) || //or too late replica... (stopped && msg.getNumber() >= (lastConsId + timeoutHighMark))) { // or a timed-out replica which needs to fetch the state //System.out.println("---ENTREI NO ELSE IF LINHA 244!!!"); //Start state transfer /** THIS IS JOAO'S CODE, FOR HANLDING THE STATE TRANSFER */ Logger.println("(ExecutionManager.checkLimits) Message for execution " + msg.getNumber() + " is beyond the paxos highmark, adding it to out of context set"); addOutOfContextMessage(msg); if (reconfManager.getStaticConf().isStateTransferEnabled()) { //Logger.debug = true; //System.out.println("---ESTOU NO EXECUTIONMANAGER NA LINHA 253!!!"); tomLayer.getStateManager().analyzeState(msg.getNumber()); } else { System.out.println("##################################################################################"); System.out.println("- Ahead-of-time message discarded"); System.out.println("- If many messages of the same consensus are discarded, the replica can halt!"); System.out.println("- Try to increase the 'system.paxos.highMarc' configuration parameter."); System.out.println("- Last consensus executed: " + lastConsId); System.out.println("##################################################################################"); } /******************************************************************/ } outOfContextLock.unlock(); return canProcessTheMessage; } /** * Informs if there are messages till to be processed associated the specified consensus's execution * @param eid The ID for the consensus execution in question * @return True if there are still messages to be processed, false otherwise */ public boolean receivedOutOfContextPropose(int eid) { outOfContextLock.lock(); /******* BEGIN OUTOFCONTEXT CRITICAL SECTION *******/ boolean result = outOfContextProposes.get(eid) != null; /******* END OUTOFCONTEXT CRITICAL SECTION *******/ outOfContextLock.unlock(); return result; } /** * Removes a consensus's execution from this manager * @param id ID of the consensus's execution to be removed * @return The consensus's execution that was removed */ public Execution removeExecution(int id) { executionsLock.lock(); /******* BEGIN EXECUTIONS CRITICAL SECTION *******/ Execution execution = executions.remove(id); /******* END EXECUTIONS CRITICAL SECTION *******/ executionsLock.unlock(); outOfContextLock.lock(); /******* BEGIN OUTOFCONTEXT CRITICAL SECTION *******/ outOfContextProposes.remove(id); outOfContext.remove(id); /******* END OUTOFCONTEXT CRITICAL SECTION *******/ outOfContextLock.unlock(); return execution; } /** THIS IS JOAO'S CODE, FOR HANDLING THE STATE TRANSFER */ public void removeOutOfContexts(int id) { outOfContextLock.lock(); /******* BEGIN OUTOFCONTEXT CRITICAL SECTION *******/ Integer[] keys = new Integer[outOfContextProposes.keySet().size()]; outOfContextProposes.keySet().toArray(keys); for (int i = 0; i < keys.length; i++) { if (keys[i] <= id) { outOfContextProposes.remove(keys[i]); } } keys = new Integer[outOfContext.keySet().size()]; outOfContext.keySet().toArray(keys); for (int i = 0; i < keys.length; i++) { if (keys[i] <= id) { outOfContext.remove(keys[i]); } } /******* END OUTOFCONTEXT CRITICAL SECTION *******/ outOfContextLock.unlock(); } /********************************************************/ /** * Returns the specified consensus' execution * * @param eid ID of the consensus execution to be returned * @return The consensus execution specified */ public Execution getExecution(int eid) { executionsLock.lock(); /******* BEGIN EXECUTIONS CRITICAL SECTION *******/ Execution execution = executions.get(eid); if (execution == null) {//there is no execution created with the given eid //let's create one... Consensus cons = new Consensus(eid); execution = new Execution(this, cons); //...and add it to the executions table executions.put(eid, execution); } /******* END EXECUTIONS CRITICAL SECTION *******/ executionsLock.unlock(); return execution; } public boolean isDecidable(int eid) { if (receivedOutOfContextPropose(eid)) { Execution exec = getExecution(eid); PaxosMessage prop = outOfContextProposes.get(exec.getId()); Round round = exec.getRound(prop.getRound(), reconfManager); byte[] propHash = tomLayer.computeHash(prop.getValue()); List<PaxosMessage> msgs = outOfContext.get(eid); int countWeaks = 0; int countStrongs = 0; if (msgs != null) { for (PaxosMessage msg : msgs) { if (msg.getRound() == round.getNumber() && Arrays.equals(propHash, msg.getValue())) { if (msg.getPaxosType() == MessageFactory.WEAK) countWeaks++; else if (msg.getPaxosType() == MessageFactory.STRONG) countStrongs++; } } } if(reconfManager.getStaticConf().isBFT()){ return ((countWeaks > (2*reconfManager.getCurrentViewF())) && (countStrongs > (2*reconfManager.getCurrentViewF()))); }else{ return (countStrongs > reconfManager.getQuorumStrong()); } } //System.out.println("ESTOU A RETORNAR FALSE!!!!!!!!!!!!!!!!!!!!"); return false; } public void processOutOfContextPropose(Execution execution) { outOfContextLock.lock(); /******* BEGIN OUTOFCONTEXT CRITICAL SECTION *******/ PaxosMessage prop = outOfContextProposes.remove(execution.getId()); if (prop != null) { Logger.println("(ExecutionManager.createExecution) (" + execution.getId() + ") Processing out of context propose"); acceptor.processMessage(prop); } /******* END OUTOFCONTEXT CRITICAL SECTION *******/ outOfContextLock.unlock(); } public void processOutOfContext(Execution execution) { outOfContextLock.lock(); /******* BEGIN OUTOFCONTEXT CRITICAL SECTION *******/ //then we have to put the pending paxos messages List<PaxosMessage> messages = outOfContext.remove(execution.getId()); if (messages != null) { Logger.println("(createExecution) (" + execution.getId() + ") Processing other " + messages.size() + " out of context messages."); for (Iterator<PaxosMessage> i = messages.iterator(); i.hasNext();) { acceptor.processMessage(i.next()); if (execution.isDecided()) { Logger.println("(ExecutionManager.createExecution) execution " + execution.getId() + " decided."); break; } } Logger.println("(createExecution) (" + execution.getId() + ") Finished out of context processing"); } /******* END OUTOFCONTEXT CRITICAL SECTION *******/ outOfContextLock.unlock(); } /** * Stores a message established as being out of context (a message that * doesn't belong to current executing consensus). * * @param m Out of context message to be stored */ public void addOutOfContextMessage(PaxosMessage m) { outOfContextLock.lock(); /******* BEGIN OUTOFCONTEXT CRITICAL SECTION *******/ if (m.getPaxosType() == MessageFactory.PROPOSE) { Logger.println("(ExecutionManager.addOutOfContextMessage) adding " + m); outOfContextProposes.put(m.getNumber(), m); } else { List<PaxosMessage> messages = outOfContext.get(m.getNumber()); if (messages == null) { messages = new LinkedList<PaxosMessage>(); outOfContext.put(m.getNumber(), messages); } Logger.println("(ExecutionManager.addOutOfContextMessage) adding " + m); messages.add(m); } /******* END OUTOFCONTEXT CRITICAL SECTION *******/ outOfContextLock.unlock(); } @Override public String toString() { return stoppedMsgs.toString(); } }