/******************************************************************************* * Copyright (c) 2013 Imperial College London. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Raul Castro Fernandez - initial design and implementation ******************************************************************************/ package uk.ac.imperial.lsds.seep.api.largestateimpls; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; import uk.ac.imperial.lsds.seep.state.LargeState; import uk.ac.imperial.lsds.seep.state.Streamable; import uk.ac.imperial.lsds.seep.state.Versionable; public class SeepMatrix extends Matrix implements Versionable, Streamable, LargeState{ private static final long serialVersionUID = 1L; private AtomicBoolean dirtyMode = new AtomicBoolean(); private Semaphore mutex = new Semaphore(1); private Iterator<Integer> iterator = null; // We keep here the original index and the row. Original index to reduce the reconciliation time private HashMap<Integer, ArrayList<Component>> dirtyState = new HashMap<Integer, ArrayList<Component>>(); //private ArrayList<ArrayList<Component>> dirtyState_newRows = new ArrayList<ArrayList<Component>>(); private HashMap<Integer, ArrayList<Component>> dirtyState_newRows = new HashMap<Integer, ArrayList<Component>>(); // here we store idx - tag, instead of tag-idx, so that we can do the reverse mapping when reconciliating private HashMap<Integer, Integer> dirtyState_IDX_TAG = new HashMap<Integer, Integer>(); // and also keep this, so that rows are correctly read while in dirty state private HashMap<Integer, Integer> dirtyState_TAG_IDX = new HashMap<Integer, Integer>(); public SeepMatrix(){ super(); } @Override public void lock(){ try { this.mutex.acquire(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void release(){ this.mutex.release(); } // WRITE public synchronized void updateMatrixByReplacingValue(int rowTag, int col, int value){ try { this.mutex.acquire(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if(dirtyMode.get()){ // System.out.println("AA1"); if(rowIds.get(rowTag) != null){ // If it exists, then update val in col //System.out.println("a1"); ArrayList<Component> row = super.getRowVectorWithTag(rowTag); //System.out.println("a2"); //int rowIdx = rows.indexOf(row); // Looping through size if(row == null){ return; } int rowIdx = rowIds.get(rowTag); // We go to the specific col to update for(int i = 0; i< row.size(); i++){ // If the current col is equal or greater than col to update, insert col before this one int currentElCol = row.get(i).col; if(currentElCol > col){ int insertIdx = (i-1) < 0 ? 0 : i-1; row.add(insertIdx, new Component(col, value)); break; } else if(currentElCol == col){ row.set(i, new Component(col, value)); break; } // else if the current col is lesser than col, and is the last element... else if(currentElCol < col && row.size() == i+1){ // Insert at the end row.add(new Component(col, value)); break; } } // Finally leave the updated row in the dirty state dirtyState.put(rowIdx, row); // // Finally insert the updated row // rows.set(rowIdx, row); } else{ // Non existent row, create a new one with the given value ArrayList<Component> c = new ArrayList<Component>(); c.add(new Component(col, value)); // Add to rows and update rowIds dirtyState_newRows.put(rowTag, c); // We add the new row to our artificial matrix // dirtyState_newRows.add(c); // // fuck the gods... // dirtyState_TAG_IDX.put(rowTag, dirtyState_newRows.size()); // dirtyState_IDX_TAG.put(dirtyState_newRows.size(), rowTag); // dirtyState_newRows(rowTag, c); } } else{ // System.out.println("AA2"); mutex.release(); super.updateMatrixByReplacingValue(rowTag, col, value); } mutex.release(); } //READ public ArrayList<Component> getRowVectorWithTag(int rowTag) { ArrayList<Component> toReturn = null; try { this.mutex.acquire(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if(dirtyMode.get()){ if(dirtyState_TAG_IDX.get(rowTag) == null){ // if not in dirty, we check in the original one toReturn = super.getRowVectorWithTag(rowTag); mutex.release(); return toReturn; } int rowIndex = dirtyState_TAG_IDX.get(rowTag); toReturn = dirtyState_newRows.get(rowIndex-1); mutex.release(); return toReturn; } else{ toReturn = super.getRowVectorWithTag(rowTag); mutex.release(); return toReturn; } } /** IFACE methods **/ public void setSnapshotMode(boolean newValue){ this.dirtyMode.set(newValue); } public synchronized void reconcile(){ try { this.mutex.acquire(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //First we update the previously already existent for(Map.Entry<Integer, ArrayList<Component>> entry : dirtyState.entrySet()){ int idx = entry.getKey(); rows.set(idx, dirtyState.get(entry.getValue())); } // and now we add the new stuff for(int i = 0; i<dirtyState_newRows.size(); i++){ for(Integer rowId : dirtyState_newRows.keySet()){ ArrayList<Component> newRow = dirtyState_newRows.get(rowId); rows.add(newRow); rowSize++; int rowIdx = rows.size()-1; rowIds.put(rowId, rowIdx); } // ArrayList<Component> newRow = dirtyState_newRows.get(i); // rows.add(newRow); // rowSize++; // if(dirtyState_IDX_TAG == null){ // System.out.println("dirtyState tag null"); // } // else{ // System.out.println("dirtyState_IDX_TAG.get(i) is null"); // } // int rowTag = rows.size(); // //int rowTag = dirtyState_IDX_TAG.get(i); // rowIds.put(rowTag, rows.size()-1); } dirtyState.clear(); dirtyState_newRows.clear(); dirtyState_IDX_TAG.clear(); dirtyState_TAG_IDX.clear(); dirtyMode.set(false); this.mutex.release(); } int realIndexWhileAppendingChunks = 0; @Override public void appendChunk(ArrayList<Object> chunk) { if(chunk == null){ System.out.println("RECREATED STATE SIZE: "+this.rows.size()); this.rowSize = rows.size(); realIndexWhileAppendingChunks = 0; // For next merging return; } System.out.println("Appending: "+chunk.size()); for(int i = 0; i<chunk.size(); i++){ int rowId = (Integer)chunk.get(i); i++; ArrayList<Component> row = (ArrayList<Component>)chunk.get(i); //this.rowIds.put(rowId, rowId); this.rowIds.put(rowId, realIndexWhileAppendingChunks); realIndexWhileAppendingChunks++; this.rows.add(row); } } @Override public Object getFromBackup(Object key) { int idx = this.rowIds.get(key); return this.rows.get(idx); } @Override public Iterator getIterator() { iterator = this.rowIds.keySet().iterator(); return iterator; } @Override public int getSize() { return rowSize; } @Override public int getTotalNumberOfChunks(int chunkSize) { double chunks = (double)this.rows.size()/chunkSize; System.out.println("$$$$ CHUNKS: "+chunks); int totalChunks = (int) Math.ceil((double)this.size()/chunkSize); System.out.println("MAP SIZE: "+this.size()+" so total chunks: "+totalChunks); return totalChunks; } @Override public void reset() { this.dirtyState.clear(); this.dirtyState_IDX_TAG.clear(); this.dirtyState_newRows.clear(); this.dirtyState_TAG_IDX.clear(); this.rowIds.clear(); this.rows.clear(); this.rowSize = 0; } @Override public ArrayList<Object> streamSplitState(int chunkSize) { ArrayList<Object> chunk = new ArrayList<Object>(); int sizeCounter = 0; while(iterator.hasNext()){ int rowId = iterator.next(); chunk.add(rowId); // add tag Object row = this.getFromBackup(rowId); chunk.add(row); sizeCounter++; if(sizeCounter >= chunkSize){ return chunk; } } return null; } @Override public Object getVersionableAndStreamableState() { return this; } }