--- /dev/null
+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<MapKey, MapValue extends LRUObject<MapKey, MapValue>> implements Persistable {
+
+ public static boolean VERIFY = true;
+
+ // Final stuff
+ final protected LRU<MapKey, MapValue> 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<MapKey, MapValue> 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<MapKey, MapValue> 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<byte[],Integer> 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<byte[],Integer> 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;
+ }
+
+}
\ No newline at end of file