package org.simantics.acorn.lru; import java.io.IOException; import java.nio.file.Path; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import org.simantics.acorn.FileIO; import org.simantics.acorn.Persistable; import org.simantics.utils.datastructures.Pair; public abstract class LRUObject> implements Persistable { public static boolean VERIFY = true; // Final stuff final protected LRU LRU; final private Semaphore mutex = new Semaphore(1); final private MapKey key; final private String fileName; // Mutable stuff protected long accessTime = AccessTime.getInstance().getAccessTime(); private int offset; private int length; private boolean resident = true; private boolean dirty = true; private boolean forceResident = false; // DEBUG // private boolean isForceResidentSetAfterLastGet = false; private Path readDirectory; private Thread mutexOwner; // for loading public LRUObject(LRU LRU, MapKey key, Path readDirectory, String fileName, int offset, int length, boolean dirty, boolean resident) { this.LRU = LRU; this.key = key; this.fileName = fileName; this.offset = offset; this.length = length; this.readDirectory = readDirectory; this.dirty = dirty; this.resident = resident; } // for creating public LRUObject(LRU LRU, MapKey key, Path readDirectory, String fileName, boolean dirty, boolean resident) { this(LRU, key, readDirectory, fileName, -1, -1, dirty, resident); } /* * Public interface */ public MapKey getKey() { // This can be called without mutex return key; } public void acquireMutex() { try { while(!mutex.tryAcquire(3, TimeUnit.SECONDS)) { System.err.println("Mutex is taking a long time to acquire - owner is " + mutexOwner); } if(VERIFY) mutexOwner = Thread.currentThread(); } catch (InterruptedException e) { throw new IllegalStateException(e); } } public boolean tryAcquireMutex() { return mutex.tryAcquire(); } public void releaseMutex() { mutex.release(); } @Override public void toFile(Path bytes) throws IOException { if(VERIFY) verifyAccess(); Pair pair = toBytes(); byte[] data = pair.first; int length = pair.second; FileIO fio = FileIO.get(bytes); int offset = fio.saveBytes(data, length, overwrite()); setPosition(offset, length); } public int makeResident() { if(VERIFY) verifyAccess(); return LRU.makeResident(this, false); } public int makeResident(boolean keepResident) { if(VERIFY) verifyAccess(); return LRU.makeResident(this, true); } /* * Package implementation details */ abstract void release(); abstract String getExtension(); String getStateKey() { String result = getKey().toString() + "#" + getDirectory().getFileName() + "#" + getOffset() + "#" + getLength(); if(offset == -1) throw new IllegalStateException(result); return result; } long getLastAccessTime() { if(VERIFY) verifyAccess(); return accessTime; } void accessed() { if(VERIFY) verifyAccess(); accessTime = AccessTime.getInstance().getAccessTime(); } boolean persist() { if(VERIFY) verifyAccess(); if(LRU.persist(this)) { readDirectory = LRU.getDirectory(); return true; } else { return false; } } void setForceResident(boolean value) { if(VERIFY) verifyAccess(); forceResident = value; // isForceResidentSetAfterLastGet = true; } boolean canBePersisted() { if(VERIFY) verifyAccess(); // isForceResidentSetAfterLastGet = false; return !forceResident; } boolean isDirty() { if(VERIFY) verifyAccess(); return dirty; } boolean isResident() { if(VERIFY) verifyAccess(); return resident; } String getFileName() { if(VERIFY) verifyAccess(); return fileName; } void setResident(boolean value) { if(VERIFY) verifyAccess(); resident = value; } void setDirty(boolean value) { if(VERIFY) verifyAccess(); dirty = value; } byte[] readFile() throws IOException { if(VERIFY) verifyAccess(); Path dir = getDirectory(); Path f = dir.resolve(getFileName()); FileIO fio = FileIO.get(f); return fio.readBytes(getOffset(), getLength()); } /* * Protected implementation details */ abstract protected boolean overwrite(); abstract protected Pair toBytes(); protected void setDirty() { if(VERIFY) verifyAccess(); dirty = true; } protected void verifyAccess() { assert(mutex.availablePermits() == 0); } protected synchronized void cancelForceResident() { setForceResident(false); } /* * Private implementation details */ private int getOffset() { if(VERIFY) verifyAccess(); return offset; } private int getLength() { if(VERIFY) verifyAccess(); return length; } private void setPosition(int offset, int length) { if(VERIFY) verifyAccess(); if(offset == -1) throw new IllegalStateException(); this.offset = offset; this.length = length; if(overwrite() && offset > 0) throw new IllegalStateException(); } private Path getDirectory() { if(VERIFY) verifyAccess(); return readDirectory; } }