/**
* 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 bftsmart.paxosatwar.messages.PaxosMessage;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.TreeSet;
import org.apache.commons.codec.binary.Base64;
import bftsmart.reconfiguration.ServerViewManager;
import bftsmart.reconfiguration.views.View;
import bftsmart.tom.core.messages.TOMMessage;
import java.util.HashSet;
import java.util.Set;
/**
* This class stands for a round of an execution of a consensus
*/
public class Round implements Serializable {
private static final long serialVersionUID = -2891450035863688295L;
private transient Execution execution; // Execution where the round belongs to
private int number; // Round's number
private int me; // Process ID
private boolean[] weakSetted;
private boolean[] strongSetted;
private byte[][] weak; // weakling accepted values from other processes
private byte[][] strong; // strongly accepted values from other processes
private Collection<Integer> freeze = null; // processes where this round was freezed
private boolean frozen = false; // is this round frozen?
private boolean collected = false; // indicates if a collect message for this round was already sent
private boolean alreadyRemoved = false; // indicates if this round was removed from its execution
public byte[] propValue = null; // proposed value
public TOMMessage[] deserializedPropValue = null; //utility var
public byte[] propValueHash = null; // proposed value hash
public HashSet<PaxosMessage> proof; // proof from other processes
private View lastView = null;
private ServerViewManager manager;
/**
* Creates a new instance of Round for acceptors
* @param parent Execution to which this round belongs
* @param number Number of the round
* @param timeout Timeout duration for this round
*/
protected Round(ServerViewManager manager, Execution parent, int number) {
this.execution = parent;
this.number = number;
this.manager = manager;
this.proof = new HashSet<PaxosMessage>();
//ExecutionManager manager = execution.getManager();
this.lastView = manager.getCurrentView();
this.me = manager.getStaticConf().getProcessId();
//int[] acceptors = manager.getAcceptors();
int n = manager.getCurrentViewN();
weakSetted = new boolean[n];
strongSetted = new boolean[n];
Arrays.fill(weakSetted, false);
Arrays.fill(strongSetted, false);
if (number == 0) {
this.weak = new byte[n][];
this.strong = new byte[n][];
Arrays.fill((Object[]) weak, null);
Arrays.fill((Object[]) strong, null);
} else {
Round previousRound = execution.getRound(number - 1, manager);
this.weak = previousRound.getWeak();
this.strong = previousRound.getStrong();
}
}
// If a view change takes place and concurrentely this consensus is still
// receiving messages, the weak and strong arrays must be updated
private void updateArrays() {
if (lastView.getId() != manager.getCurrentViewId()) {
int n = manager.getCurrentViewN();
byte[][] weak = new byte[n][];
byte[][] strong = new byte[n][];
boolean[] weakSetted = new boolean[n];
boolean[] strongSetted = new boolean[n];
Arrays.fill(weakSetted, false);
Arrays.fill(strongSetted, false);
for (int pid : lastView.getProcesses()) {
if (manager.isCurrentViewMember(pid)) {
int currentPos = manager.getCurrentViewPos(pid);
int lastPos = lastView.getPos(pid);
weak[currentPos] = this.weak[lastPos];
strong[currentPos] = this.strong[lastPos];
weakSetted[currentPos] = this.weakSetted[lastPos];
strongSetted[currentPos] = this.strongSetted[lastPos];
}
}
this.weak = weak;
this.strong = strong;
this.weakSetted = weakSetted;
this.strongSetted = strongSetted;
lastView = manager.getCurrentView();
}
}
/**
* Set this round as removed from its execution
*/
public void setRemoved() {
this.alreadyRemoved = true;
}
/**
* Informs if this round was removed from its execution
* @return True if it is removed, false otherwise
*/
public boolean isRemoved() {
return this.alreadyRemoved;
}
public void addToProof(PaxosMessage pm) {
proof.add(pm);
}
public Set<PaxosMessage> getProof() {
return proof;
}
/**
* Retrieves the duration for the timeout
* @return Duration for the timeout
*/
/*public long getTimeout() {
return this.timeout;
}*/
/**
* Retrieves this round's number
* @return This round's number
*/
public int getNumber() {
return number;
}
/**
* Retrieves this round's execution
* @return This round's execution
*/
public Execution getExecution() {
return execution;
}
/**
* Informs if there is a weakly accepted value from a replica
* @param acceptor The replica ID
* @return True if there is a weakly accepted value from a replica, false otherwise
*/
public boolean isWeakSetted(int acceptor) {
updateArrays();
//******* EDUARDO BEGIN **************//
int p = this.manager.getCurrentViewPos(acceptor);
if(p >= 0){
return weak[p] != null;
}else{
return false;
}
//******* EDUARDO END **************//
}
/**
* Informs if there is a strongly accepted value from a replica
* @param acceptor The replica ID
* @return True if there is a strongly accepted value from a replica, false otherwise
*/
public boolean isStrongSetted(int acceptor) {
updateArrays();
//******* EDUARDO BEGIN **************//
int p = this.manager.getCurrentViewPos(acceptor);
if(p >= 0){
return strong[p] != null;
}else{
return false;
}
//******* EDUARDO END **************//
}
/**
* Retrives the weakly accepted value from the specified replica
* @param acceptor The replica ID
* @return The value weakly accepted from the specified replica
*/
public byte[] getWeak(int acceptor) {
updateArrays();
//******* EDUARDO BEGIN **************//
int p = this.manager.getCurrentViewPos(acceptor);
if(p >= 0){
return this.weak[p];
}else{
return null;
}
//******* EDUARDO END **************//
}
/**
* Retrives all weakly accepted value from all replicas
* @return The values weakly accepted from all replicas
*/
public byte[][] getWeak() {
return this.weak;
}
/**
* Sets the weakly accepted value from the specified replica
* @param acceptor The replica ID
* @param value The value weakly accepted from the specified replica
*/
public void setWeak(int acceptor, byte[] value) { // TODO: Race condition?
updateArrays();
//******* EDUARDO BEGIN **************//
int p = this.manager.getCurrentViewPos(acceptor);
if (p >=0 && /*!weakSetted[p] &&*/ !isFrozen()) { //it can only be setted once
weak[p] = value;
weakSetted[p] = true;
}
//******* EDUARDO END **************//
}
/**
* Retrives the strongly accepted value from the specified replica
* @param acceptor The replica ID
* @return The value strongly accepted from the specified replica
*/
public byte[] getStrong(int acceptor) {
updateArrays();
//******* EDUARDO BEGIN **************//
int p = this.manager.getCurrentViewPos(acceptor);
if(p >= 0){
return strong[p];
}else{
return null;
}
//******* EDUARDO END **************//
}
/**
* Retrives all strongly accepted values from all replicas
* @return The values strongly accepted from all replicas
*/
public byte[][] getStrong() {
return strong;
}
/**
* Sets the strongly accepted value from the specified replica
* @param acceptor The replica ID
* @param value The value strongly accepted from the specified replica
*/
public void setStrong(int acceptor, byte[] value) { // TODO: race condition?
updateArrays();
//******* EDUARDO BEGIN **************//
int p = this.manager.getCurrentViewPos(acceptor);
if (p >= 0 /*&& !strongSetted[p]*/ && !isFrozen()) { //it can only be setted once
strong[p] = value;
strongSetted[p] = true;
}
//******* EDUARDO END **************//
}
/**
* Indicates if a collect message for this round was already sent
* @return True if done so, false otherwise
*/
public boolean isCollected() {
return collected;
}
/**
* Establishes that a collect message for this round was already sent
*/
public void collect() {
collected = true;
}
/**
* Indicates if this round is frozen
* @return True if so, false otherwise
*/
public boolean isFrozen() {
return frozen;
}
/**
* Establishes that this round is frozen
*/
public void freeze() {
frozen = true;
addFreeze(me);
}
/**
* Establishes that a replica locally freezed this round
* @param acceptor replica that locally freezed this round
*/
public void addFreeze(int acceptor) {
if (freeze == null) {
freeze = new TreeSet<Integer>();
}
freeze.add(acceptor);
}
/**
* Retrieves the ammount of replicas that locally freezed this round
* @return Ammount of replicas that locally freezed this round
*/
public int countFreeze() {
return freeze.size();
}
/**
* Retrives the ammount of replicas from which this process weakly accepted a specified value
* @param value The value in question
* @return Ammount of replicas from which this process weakly accepted the specified value
*/
public int countWeak(byte[] value) {
return count(weakSetted,weak, value);
}
/**
* Retrives the ammount of replicas from which this process strongly accepted a specified value
* @param value The value in question
* @return Ammount of replicas from which this process strongly accepted the specified value
*/
public int countStrong(byte[] value) {
return count(strongSetted,strong, value);
}
/**
* Counts how many times 'value' occurs in 'array'
* @param array Array where to count
* @param value Value to count
* @return Ammount of times that 'value' was find in 'array'
*/
private int count(boolean[] arraySetted,byte[][] array, byte[] value) {
if (value != null) {
int counter = 0;
for (int i = 0; i < array.length; i++) {
if (arraySetted != null && arraySetted[i] && Arrays.equals(value, array[i])) {
counter++;
}
}
return counter;
}
return 0;
}
/*************************** DEBUG METHODS *******************************/
/**
* Print round information.
*/
@Override
public String toString() {
StringBuffer buffWeak = new StringBuffer(1024);
StringBuffer buffStrong = new StringBuffer(1024);
StringBuffer buffDecide = new StringBuffer(1024);
buffWeak.append("W=(");
buffStrong.append("S=(");
buffDecide.append("D=(");
//recall that weak.length = strong.length = decide.length
for (int i = 0; i < weak.length - 1; i++) {
buffWeak.append(str(weak[i]) + " [" + (weak[i] != null ? weak[i].length : 0) + " bytes] ,");
buffStrong.append(str(strong[i]) + " [" + (strong[i] != null ? strong[i].length : 0) + " bytes] ,");
}
buffWeak.append(str(weak[weak.length - 1]) + " [" + (weak[weak.length - 1] != null ? weak[weak.length - 1].length : 0) + " bytes])");
buffStrong.append(str(strong[strong.length - 1]) + " [" + (strong[strong.length - 1] != null ? strong[strong.length - 1].length : 0) + " bytes])");
return "eid=" + execution.getId() + " r=" + getNumber() + " " + buffWeak + " " + buffStrong + " " + buffDecide;
}
private String str(byte[] obj) {
if(obj == null) {
return "*";
} else {
return Base64.encodeBase64String(obj);
}
}
@Override
public boolean equals(Object o) {
return this == o;
}
/**
* Clear all round info.
*/
public void clear() {
int n = manager.getCurrentViewN();
weakSetted = new boolean[n];
strongSetted = new boolean[n];
Arrays.fill(weakSetted, false);
Arrays.fill(strongSetted, false);
this.weak = new byte[n][];
this.strong = new byte[n][];
Arrays.fill((Object[]) weak, null);
Arrays.fill((Object[]) strong, null);
this.proof = new HashSet<PaxosMessage>();
}
}