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.FileIO;
9 import org.simantics.acorn.Persistable;
10 import org.simantics.acorn.exception.AcornAccessVerificationException;
11 import org.simantics.acorn.exception.IllegalAcornStateException;
12 import org.simantics.utils.datastructures.Pair;
14 public abstract class LRUObject<MapKey, MapValue extends LRUObject<MapKey, MapValue>> implements Persistable {
16 public static boolean VERIFY = true;
19 final protected LRU<MapKey, MapValue> LRU;
20 final private Semaphore mutex = new Semaphore(1);
21 final private MapKey key;
22 final private String fileName;
25 protected long accessTime = AccessTime.getInstance().getAccessTime();
28 private boolean resident = true;
29 private boolean dirty = true;
30 private boolean forceResident = false;
33 // private boolean isForceResidentSetAfterLastGet = false;
35 private Path readDirectory;
37 private Thread mutexOwner;
40 public LRUObject(LRU<MapKey, MapValue> LRU, MapKey key, Path readDirectory, String fileName, int offset, int length, boolean dirty, boolean resident) {
43 this.fileName = fileName;
46 this.readDirectory = readDirectory;
48 this.resident = resident;
52 public LRUObject(LRU<MapKey, MapValue> LRU, MapKey key, Path readDirectory, String fileName, boolean dirty, boolean resident) {
53 this(LRU, key, readDirectory, fileName, -1, -1, dirty, resident);
59 public MapKey getKey() {
60 // This can be called without mutex
64 public void acquireMutex() throws IllegalAcornStateException {
66 while(!mutex.tryAcquire(3, TimeUnit.SECONDS)) {
67 System.err.println("Mutex is taking a long time to acquire - owner is " + mutexOwner);
71 mutexOwner = Thread.currentThread();
73 } catch (InterruptedException e) {
74 throw new IllegalAcornStateException(e);
78 public boolean tryAcquireMutex() {
79 return mutex.tryAcquire();
82 public void releaseMutex() {
87 public void toFile(Path bytes) throws IOException {
91 } catch (AcornAccessVerificationException e) {
92 throw new IOException("Exception occured during toFile for file " + fileName, e);
96 Pair<byte[], Integer> pair = toBytes();
97 byte[] data = pair.first;
98 int length = pair.second;
99 FileIO fio = FileIO.get(bytes);
100 int offset = fio.saveBytes(data, length, overwrite());
101 setPosition(offset, length);
102 } catch (AcornAccessVerificationException | IllegalAcornStateException e) {
103 throw new IOException("Exception occured during toFile for file " + fileName, e);
107 public int makeResident() throws AcornAccessVerificationException, IllegalAcornStateException {
108 if(VERIFY) verifyAccess();
109 return LRU.makeResident(this, false);
112 public int makeResident(boolean keepResident) throws AcornAccessVerificationException, IllegalAcornStateException {
113 if(VERIFY) verifyAccess();
114 return LRU.makeResident(this, true);
118 * Package implementation details
121 abstract void release();
122 abstract String getExtension();
124 String getStateKey() throws IllegalAcornStateException, AcornAccessVerificationException {
125 String result = getKey().toString() + "#" + getDirectory().getFileName() + "#" + getOffset() + "#" + getLength();
127 throw new IllegalAcornStateException(result);
131 long getLastAccessTime() throws AcornAccessVerificationException {
132 if(VERIFY) verifyAccess();
136 void accessed() throws AcornAccessVerificationException {
137 if(VERIFY) verifyAccess();
138 accessTime = AccessTime.getInstance().getAccessTime();
141 boolean persist() throws AcornAccessVerificationException {
142 if(VERIFY) verifyAccess();
143 if(LRU.persist(this)) {
144 readDirectory = LRU.getDirectory();
151 void setForceResident(boolean value) throws AcornAccessVerificationException {
152 if(VERIFY) verifyAccess();
153 forceResident = value;
154 // isForceResidentSetAfterLastGet = true;
157 boolean canBePersisted() throws AcornAccessVerificationException {
158 if(VERIFY) verifyAccess();
159 // isForceResidentSetAfterLastGet = false;
160 return !forceResident;
163 boolean isDirty() throws AcornAccessVerificationException {
164 if(VERIFY) verifyAccess();
168 boolean isResident() throws AcornAccessVerificationException {
169 if(VERIFY) verifyAccess();
173 String getFileName() throws AcornAccessVerificationException {
174 if(VERIFY) verifyAccess();
178 void setResident(boolean value) throws AcornAccessVerificationException {
179 if(VERIFY) verifyAccess();
183 void setDirty(boolean value) throws AcornAccessVerificationException {
184 if(VERIFY) verifyAccess();
188 byte[] readFile() throws IOException, AcornAccessVerificationException {
189 if(VERIFY) verifyAccess();
190 Path dir = getDirectory();
191 Path f = dir.resolve(getFileName());
192 FileIO fio = FileIO.get(f);
193 return fio.readBytes(getOffset(), getLength());
197 * Protected implementation details
200 abstract protected boolean overwrite();
202 abstract protected Pair<byte[],Integer> toBytes() throws IllegalAcornStateException;
204 protected void setDirty() throws AcornAccessVerificationException {
205 if(VERIFY) verifyAccess();
209 protected void verifyAccess() throws AcornAccessVerificationException {
210 if (mutex.availablePermits() != 0)
211 throw new AcornAccessVerificationException("fileName=" + fileName + " mutex has " + mutex.availablePermits() + " available permits, should be 0! Current mutexOwner is " + mutexOwner);
214 protected synchronized void cancelForceResident() throws AcornAccessVerificationException {
215 setForceResident(false);
219 * Private implementation details
222 private int getOffset() throws AcornAccessVerificationException {
223 if(VERIFY) verifyAccess();
227 private int getLength() throws AcornAccessVerificationException {
228 if(VERIFY) verifyAccess();
232 private void setPosition(int offset, int length) throws AcornAccessVerificationException, IllegalAcornStateException {
233 if(VERIFY) verifyAccess();
235 throw new IllegalAcornStateException("offset == -1 for " + fileName + " in " + readDirectory.toAbsolutePath() + ", dirty=" + dirty + ", resident=" + resident + ", forceResident=" + forceResident);
236 this.offset = offset;
237 this.length = length;
238 if(overwrite() && offset > 0)
239 throw new IllegalAcornStateException("overwrite() == true && offset > 0 for " + fileName + " in " + readDirectory.toAbsolutePath() + ", dirty=" + dirty + ", resident=" + resident + ", forceResident=" + forceResident);
242 private Path getDirectory() throws AcornAccessVerificationException {
243 if(VERIFY) verifyAccess();
244 return readDirectory;
247 public void moveTo(Path path) {
248 readDirectory = path;