1 package org.simantics.acorn.lru;
3 import java.io.IOException;
4 import java.nio.file.Path;
5 import java.util.concurrent.Semaphore;
6 import java.util.concurrent.TimeUnit;
8 import org.simantics.acorn.FileCache;
9 import org.simantics.acorn.FileIO;
10 import org.simantics.acorn.Persistable;
11 import org.simantics.acorn.exception.AcornAccessVerificationException;
12 import org.simantics.acorn.exception.IllegalAcornStateException;
13 import org.simantics.utils.datastructures.Pair;
14 import org.slf4j.Logger;
16 public abstract class LRUObject<MapKey, MapValue extends LRUObject<MapKey, MapValue>> implements Persistable {
18 public abstract Logger getLogger();
19 public static boolean VERIFY = true;
22 final protected LRU<MapKey, MapValue> LRU;
23 final protected FileCache fileCache;
24 final private Semaphore mutex = new Semaphore(1);
25 final private MapKey key;
26 final private String fileName;
29 protected long accessTime = AccessTime.getInstance().getAccessTime();
32 private boolean resident = true;
33 private boolean dirty = true;
34 private boolean forceResident = false;
37 // private boolean isForceResidentSetAfterLastGet = false;
39 private Path readDirectory;
41 private Thread mutexOwner;
44 public LRUObject(LRU<MapKey, MapValue> LRU, FileCache fileCache, MapKey key, Path readDirectory, String fileName, int offset, int length, boolean dirty, boolean resident) {
46 this.fileCache = fileCache;
48 this.fileName = fileName;
51 this.readDirectory = readDirectory;
53 this.resident = resident;
57 public LRUObject(LRU<MapKey, MapValue> LRU, FileCache fileCache, MapKey key, Path readDirectory, String fileName, boolean dirty, boolean resident) {
58 this(LRU, fileCache, key, readDirectory, fileName, -1, -1, dirty, resident);
64 public MapKey getKey() {
65 // This can be called without mutex
69 public void acquireMutex() throws IllegalAcornStateException {
71 while(!mutex.tryAcquire(3, TimeUnit.SECONDS)) {
72 getLogger().info("Mutex is taking a long time to acquire - owner is " + mutexOwner);
76 mutexOwner = Thread.currentThread();
78 } catch (InterruptedException e) {
79 throw new IllegalAcornStateException(e);
83 public boolean tryAcquireMutex() {
84 return mutex.tryAcquire();
87 public void releaseMutex() {
92 public void toFile(Path bytes) throws IOException {
96 } catch (AcornAccessVerificationException e) {
97 throw new IOException("Exception occured during toFile for file " + fileName, e);
101 Pair<byte[], Integer> pair = toBytes();
102 byte[] data = pair.first;
103 int length = pair.second;
104 FileIO fio = fileCache.get(bytes);
105 int offset = fio.saveBytes(data, length, overwrite());
106 setPosition(offset, length);
107 } catch (AcornAccessVerificationException | IllegalAcornStateException e) {
108 throw new IOException("Exception occured during toFile for file " + fileName, e);
112 public int makeResident() throws AcornAccessVerificationException, IllegalAcornStateException {
113 if(VERIFY) verifyAccess();
114 return LRU.makeResident(this, false);
117 public int makeResident(boolean keepResident) throws AcornAccessVerificationException, IllegalAcornStateException {
118 if(VERIFY) verifyAccess();
119 return LRU.makeResident(this, true);
123 * Package implementation details
126 abstract void release();
127 abstract String getExtension();
129 String getStateKey() throws IllegalAcornStateException, AcornAccessVerificationException {
130 String result = getKey().toString() + "#" + getDirectory().getFileName() + "#" + getOffset() + "#" + getLength();
132 throw new IllegalAcornStateException(result);
136 long getLastAccessTime() throws AcornAccessVerificationException {
137 if(VERIFY) verifyAccess();
141 void accessed() throws AcornAccessVerificationException {
142 if(VERIFY) verifyAccess();
143 accessTime = AccessTime.getInstance().getAccessTime();
146 boolean persist() throws AcornAccessVerificationException {
147 if(VERIFY) verifyAccess();
148 if(LRU.persist(this)) {
149 readDirectory = LRU.getDirectory();
156 void setForceResident(boolean value) throws AcornAccessVerificationException {
157 if(VERIFY) verifyAccess();
158 forceResident = value;
159 // isForceResidentSetAfterLastGet = true;
162 boolean canBePersisted() throws AcornAccessVerificationException {
163 if(VERIFY) verifyAccess();
164 // isForceResidentSetAfterLastGet = false;
165 return !forceResident;
168 boolean isDirty() throws AcornAccessVerificationException {
169 if(VERIFY) verifyAccess();
173 boolean isResident() throws AcornAccessVerificationException {
174 if(VERIFY) verifyAccess();
178 String getFileName() throws AcornAccessVerificationException {
179 if(VERIFY) verifyAccess();
183 void setResident(boolean value) throws AcornAccessVerificationException {
184 if(VERIFY) verifyAccess();
188 void setDirty(boolean value) throws AcornAccessVerificationException {
189 if(VERIFY) verifyAccess();
193 byte[] readFile() throws IOException, AcornAccessVerificationException {
194 if(VERIFY) verifyAccess();
195 Path dir = getDirectory();
196 Path f = dir.resolve(getFileName());
197 FileIO fio = fileCache.get(f);
198 return fio.readBytes(getOffset(), getLength());
202 * Protected implementation details
205 abstract protected boolean overwrite();
207 abstract protected Pair<byte[],Integer> toBytes() throws IllegalAcornStateException;
209 protected void setDirty() throws AcornAccessVerificationException {
210 if(VERIFY) verifyAccess();
214 protected void verifyAccess() throws AcornAccessVerificationException {
215 if (mutex.availablePermits() != 0)
216 throw new AcornAccessVerificationException("fileName=" + fileName + " mutex has " + mutex.availablePermits() + " available permits, should be 0! Current mutexOwner is " + mutexOwner);
219 protected synchronized void cancelForceResident() throws AcornAccessVerificationException {
220 setForceResident(false);
224 * Private implementation details
227 private int getOffset() throws AcornAccessVerificationException {
228 if(VERIFY) verifyAccess();
232 private int getLength() throws AcornAccessVerificationException {
233 if(VERIFY) verifyAccess();
237 private void setPosition(int offset, int length) throws AcornAccessVerificationException, IllegalAcornStateException {
238 if(VERIFY) verifyAccess();
240 throw new IllegalAcornStateException("offset == -1 for " + fileName + " in " + readDirectory.toAbsolutePath() + ", dirty=" + dirty + ", resident=" + resident + ", forceResident=" + forceResident);
241 this.offset = offset;
242 this.length = length;
243 if(overwrite() && offset > 0)
244 throw new IllegalAcornStateException("overwrite() == true && offset > 0 for " + fileName + " in " + readDirectory.toAbsolutePath() + ", dirty=" + dirty + ", resident=" + resident + ", forceResident=" + forceResident);
247 private Path getDirectory() throws AcornAccessVerificationException {
248 if(VERIFY) verifyAccess();
249 return readDirectory;
252 public void moveTo(Path path) {
253 readDirectory = path;