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;
15 public abstract class LRUObject<MapKey, MapValue extends LRUObject<MapKey, MapValue>> implements Persistable {
17 public static boolean VERIFY = true;
20 final protected LRU<MapKey, MapValue> LRU;
21 final protected FileCache fileCache;
22 final private Semaphore mutex = new Semaphore(1);
23 final private MapKey key;
24 final private String fileName;
27 protected long accessTime = AccessTime.getInstance().getAccessTime();
30 private boolean resident = true;
31 private boolean dirty = true;
32 private boolean forceResident = false;
35 // private boolean isForceResidentSetAfterLastGet = false;
37 private Path readDirectory;
39 private Thread mutexOwner;
42 public LRUObject(LRU<MapKey, MapValue> LRU, FileCache fileCache, MapKey key, Path readDirectory, String fileName, int offset, int length, boolean dirty, boolean resident) {
44 this.fileCache = fileCache;
46 this.fileName = fileName;
49 this.readDirectory = readDirectory;
51 this.resident = resident;
55 public LRUObject(LRU<MapKey, MapValue> LRU, FileCache fileCache, MapKey key, Path readDirectory, String fileName, boolean dirty, boolean resident) {
56 this(LRU, fileCache, key, readDirectory, fileName, -1, -1, dirty, resident);
62 public MapKey getKey() {
63 // This can be called without mutex
67 public void acquireMutex() throws IllegalAcornStateException {
69 while(!mutex.tryAcquire(3, TimeUnit.SECONDS)) {
70 System.err.println("Mutex is taking a long time to acquire - owner is " + mutexOwner);
74 mutexOwner = Thread.currentThread();
76 } catch (InterruptedException e) {
77 throw new IllegalAcornStateException(e);
81 public boolean tryAcquireMutex() {
82 return mutex.tryAcquire();
85 public void releaseMutex() {
90 public void toFile(Path bytes) throws IOException {
94 } catch (AcornAccessVerificationException e) {
95 throw new IOException("Exception occured during toFile for file " + fileName, e);
99 Pair<byte[], Integer> pair = toBytes();
100 byte[] data = pair.first;
101 int length = pair.second;
102 FileIO fio = fileCache.get(bytes);
103 int offset = fio.saveBytes(data, length, overwrite());
104 setPosition(offset, length);
105 } catch (AcornAccessVerificationException | IllegalAcornStateException e) {
106 throw new IOException("Exception occured during toFile for file " + fileName, e);
110 public int makeResident() throws AcornAccessVerificationException, IllegalAcornStateException {
111 if(VERIFY) verifyAccess();
112 return LRU.makeResident(this, false);
115 public int makeResident(boolean keepResident) throws AcornAccessVerificationException, IllegalAcornStateException {
116 if(VERIFY) verifyAccess();
117 return LRU.makeResident(this, true);
121 * Package implementation details
124 abstract void release();
125 abstract String getExtension();
127 String getStateKey() throws IllegalAcornStateException, AcornAccessVerificationException {
128 String result = getKey().toString() + "#" + getDirectory().getFileName() + "#" + getOffset() + "#" + getLength();
130 throw new IllegalAcornStateException(result);
134 long getLastAccessTime() throws AcornAccessVerificationException {
135 if(VERIFY) verifyAccess();
139 void accessed() throws AcornAccessVerificationException {
140 if(VERIFY) verifyAccess();
141 accessTime = AccessTime.getInstance().getAccessTime();
144 boolean persist() throws AcornAccessVerificationException {
145 if(VERIFY) verifyAccess();
146 if(LRU.persist(this)) {
147 readDirectory = LRU.getDirectory();
154 void setForceResident(boolean value) throws AcornAccessVerificationException {
155 if(VERIFY) verifyAccess();
156 forceResident = value;
157 // isForceResidentSetAfterLastGet = true;
160 boolean canBePersisted() throws AcornAccessVerificationException {
161 if(VERIFY) verifyAccess();
162 // isForceResidentSetAfterLastGet = false;
163 return !forceResident;
166 boolean isDirty() throws AcornAccessVerificationException {
167 if(VERIFY) verifyAccess();
171 boolean isResident() throws AcornAccessVerificationException {
172 if(VERIFY) verifyAccess();
176 String getFileName() throws AcornAccessVerificationException {
177 if(VERIFY) verifyAccess();
181 void setResident(boolean value) throws AcornAccessVerificationException {
182 if(VERIFY) verifyAccess();
186 void setDirty(boolean value) throws AcornAccessVerificationException {
187 if(VERIFY) verifyAccess();
191 byte[] readFile() throws IOException, AcornAccessVerificationException {
192 if(VERIFY) verifyAccess();
193 Path dir = getDirectory();
194 Path f = dir.resolve(getFileName());
195 FileIO fio = fileCache.get(f);
196 return fio.readBytes(getOffset(), getLength());
200 * Protected implementation details
203 abstract protected boolean overwrite();
205 abstract protected Pair<byte[],Integer> toBytes() throws IllegalAcornStateException;
207 protected void setDirty() throws AcornAccessVerificationException {
208 if(VERIFY) verifyAccess();
212 protected void verifyAccess() throws AcornAccessVerificationException {
213 if (mutex.availablePermits() != 0)
214 throw new AcornAccessVerificationException("fileName=" + fileName + " mutex has " + mutex.availablePermits() + " available permits, should be 0! Current mutexOwner is " + mutexOwner);
217 protected synchronized void cancelForceResident() throws AcornAccessVerificationException {
218 setForceResident(false);
222 * Private implementation details
225 private int getOffset() throws AcornAccessVerificationException {
226 if(VERIFY) verifyAccess();
230 private int getLength() throws AcornAccessVerificationException {
231 if(VERIFY) verifyAccess();
235 private void setPosition(int offset, int length) throws AcornAccessVerificationException, IllegalAcornStateException {
236 if(VERIFY) verifyAccess();
238 throw new IllegalAcornStateException("offset == -1 for " + fileName + " in " + readDirectory.toAbsolutePath() + ", dirty=" + dirty + ", resident=" + resident + ", forceResident=" + forceResident);
239 this.offset = offset;
240 this.length = length;
241 if(overwrite() && offset > 0)
242 throw new IllegalAcornStateException("overwrite() == true && offset > 0 for " + fileName + " in " + readDirectory.toAbsolutePath() + ", dirty=" + dirty + ", resident=" + resident + ", forceResident=" + forceResident);
245 private Path getDirectory() throws AcornAccessVerificationException {
246 if(VERIFY) verifyAccess();
247 return readDirectory;
250 public void moveTo(Path path) {
251 readDirectory = path;