package lsr.paxos.storage; import static lsr.common.ProcessDescriptor.processDescriptor; import java.util.ArrayList; import java.util.SortedMap; import lsr.paxos.Snapshot; import lsr.paxos.storage.ConsensusInstance.LogEntryState; public class InMemoryStorage implements Storage { // Must be volatile because it is read by other threads // other than the Protocol thread without locking. protected volatile int view; private volatile int firstUncommitted = 0; protected Log log; private Snapshot lastSnapshot; private ArrayList<ViewChangeListener> viewChangeListeners = new ArrayList<Storage.ViewChangeListener>(); // must be non-null for proper serialization - NOPping otherwise private long[] epoch = new long[0]; /** * Initializes new instance of <code>InMemoryStorage</code> class with empty * log. */ public InMemoryStorage() { log = new Log(); } /** * Initializes new instance of <code>InMemoryStorage</code> class with * specified log. * * @param log - the initial content of the log */ public InMemoryStorage(Log log) { this.log = log; } public Log getLog() { return log; } public Snapshot getLastSnapshot() { return lastSnapshot; } public void setLastSnapshot(Snapshot snapshot) { assert lastSnapshot == null || lastSnapshot.compareTo(snapshot) <= 0; lastSnapshot = snapshot; } public int getView() { return view; } public void setView(int view) throws IllegalArgumentException { assert view > this.view : "Cannot set smaller or equal view."; this.view = view; fireViewChangeListeners(); } public int getFirstUncommitted() { return firstUncommitted; } public void updateFirstUncommitted() { if (lastSnapshot != null) { firstUncommitted = Math.max(firstUncommitted, lastSnapshot.getNextInstanceId()); } SortedMap<Integer, ConsensusInstance> logs = log.getInstanceMap(); while (firstUncommitted < log.getNextId() && logs.get(firstUncommitted).getState() == LogEntryState.DECIDED) { firstUncommitted++; } } public long[] getEpoch() { return epoch; } public void setEpoch(long[] epoch) { this.epoch = epoch; } public void updateEpoch(long[] epoch) { assert epoch.length == this.epoch.length : "Incorrect epoch length"; for (int i = 0; i < epoch.length; i++) { this.epoch[i] = Math.max(this.epoch[i], epoch[i]); } } public void updateEpoch(long newEpoch, int id) { assert id < epoch.length : "Incorrect id"; epoch[id] = Math.max(epoch[id], newEpoch); } public boolean isInWindow(int instanceId) { return instanceId < firstUncommitted + processDescriptor.windowSize; } public int getWindowUsed() { return getLog().getNextId() - getFirstUncommitted(); } public boolean isWindowFull() { return getWindowUsed() == processDescriptor.windowSize; } public boolean isIdle() { return getLog().nextId == firstUncommitted; } public boolean addViewChangeListener(ViewChangeListener l) { if (viewChangeListeners.contains(l)) return false; return viewChangeListeners.add(l); } public boolean removeViewChangeListener(ViewChangeListener l) { return viewChangeListeners.remove(l); } protected void fireViewChangeListeners() { for (ViewChangeListener l : viewChangeListeners) l.viewChanged(view, processDescriptor.getLeaderOfView(view)); } public long getRunUniqueId() { long base = 0; switch (processDescriptor.crashModel) { case FullSS: base = getEpoch()[0]; break; case ViewSS: base = getView(); break; case EpochSS: base = getEpoch()[processDescriptor.localId]; break; case CrashStop: break; default: throw new RuntimeException(); } return base; } }