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;
13 import org.slf4j.Logger;
15 public abstract class LRUObject<MapKey, MapValue extends LRUObject<MapKey, MapValue>> implements Persistable {
17 public abstract Logger getLogger();
18 public static boolean VERIFY = true;
21 final protected LRU<MapKey, MapValue> LRU;
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, MapKey key, Path readDirectory, String fileName, int offset, int length, boolean dirty, boolean resident) {
45 this.fileName = fileName;
48 this.readDirectory = readDirectory;
50 this.resident = resident;
54 public LRUObject(LRU<MapKey, MapValue> LRU, MapKey key, Path readDirectory, String fileName, boolean dirty, boolean resident) {
55 this(LRU, key, readDirectory, fileName, -1, -1, dirty, resident);
61 public MapKey getKey() {
62 // This can be called without mutex
66 public void acquireMutex() throws IllegalAcornStateException {
68 while(!mutex.tryAcquire(3, TimeUnit.SECONDS)) {
69 getLogger().info("Mutex is taking a long time to acquire - owner is " + mutexOwner);
73 mutexOwner = Thread.currentThread();
75 } catch (InterruptedException e) {
76 throw new IllegalAcornStateException(e);
80 public boolean tryAcquireMutex() {
81 return mutex.tryAcquire();
84 public void releaseMutex() {
89 public void toFile(Path bytes) throws IOException {
93 } catch (AcornAccessVerificationException e) {
94 throw new IOException("Exception occured during toFile for file " + fileName, e);
98 Pair<byte[], Integer> pair = toBytes();
99 byte[] data = pair.first;
100 int length = pair.second;
101 FileIO fio = FileIO.get(bytes);
102 int offset = fio.saveBytes(data, length, overwrite());
103 setPosition(offset, length);
104 } catch (AcornAccessVerificationException | IllegalAcornStateException e) {
105 throw new IOException("Exception occured during toFile for file " + fileName, e);
109 public int makeResident() throws AcornAccessVerificationException, IllegalAcornStateException {
110 if(VERIFY) verifyAccess();
111 return LRU.makeResident(this, false);
114 public int makeResident(boolean keepResident) throws AcornAccessVerificationException, IllegalAcornStateException {
115 if(VERIFY) verifyAccess();
116 return LRU.makeResident(this, true);
120 * Package implementation details
123 abstract void release();
124 abstract String getExtension();
126 String getStateKey() throws IllegalAcornStateException, AcornAccessVerificationException {
127 String result = getKey().toString() + "#" + getDirectory().getFileName() + "#" + getOffset() + "#" + getLength();
129 throw new IllegalAcornStateException(result);
133 long getLastAccessTime() throws AcornAccessVerificationException {
134 if(VERIFY) verifyAccess();
138 void accessed() throws AcornAccessVerificationException {
139 if(VERIFY) verifyAccess();
140 accessTime = AccessTime.getInstance().getAccessTime();
143 boolean persist() throws AcornAccessVerificationException {
144 if(VERIFY) verifyAccess();
145 if(LRU.persist(this)) {
146 readDirectory = LRU.getDirectory();
153 void setForceResident(boolean value) throws AcornAccessVerificationException {
154 if(VERIFY) verifyAccess();
155 forceResident = value;
156 // isForceResidentSetAfterLastGet = true;
159 boolean canBePersisted() throws AcornAccessVerificationException {
160 if(VERIFY) verifyAccess();
161 // isForceResidentSetAfterLastGet = false;
162 return !forceResident;
165 boolean isDirty() throws AcornAccessVerificationException {
166 if(VERIFY) verifyAccess();
170 boolean isResident() throws AcornAccessVerificationException {
171 if(VERIFY) verifyAccess();
175 String getFileName() throws AcornAccessVerificationException {
176 if(VERIFY) verifyAccess();
180 void setResident(boolean value) throws AcornAccessVerificationException {
181 if(VERIFY) verifyAccess();
185 void setDirty(boolean value) throws AcornAccessVerificationException {
186 if(VERIFY) verifyAccess();
190 byte[] readFile() throws IOException, AcornAccessVerificationException {
191 if(VERIFY) verifyAccess();
192 Path dir = getDirectory();
193 Path f = dir.resolve(getFileName());
194 FileIO fio = FileIO.get(f);
195 return fio.readBytes(getOffset(), getLength());
199 * Protected implementation details
202 abstract protected boolean overwrite();
204 abstract protected Pair<byte[],Integer> toBytes() throws IllegalAcornStateException;
206 protected void setDirty() throws AcornAccessVerificationException {
207 if(VERIFY) verifyAccess();
211 protected void verifyAccess() throws AcornAccessVerificationException {
212 if (mutex.availablePermits() != 0)
213 throw new AcornAccessVerificationException("fileName=" + fileName + " mutex has " + mutex.availablePermits() + " available permits, should be 0! Current mutexOwner is " + mutexOwner);
216 protected synchronized void cancelForceResident() throws AcornAccessVerificationException {
217 setForceResident(false);
221 * Private implementation details
224 private int getOffset() throws AcornAccessVerificationException {
225 if(VERIFY) verifyAccess();
229 private int getLength() throws AcornAccessVerificationException {
230 if(VERIFY) verifyAccess();
234 private void setPosition(int offset, int length) throws AcornAccessVerificationException, IllegalAcornStateException {
235 if(VERIFY) verifyAccess();
237 throw new IllegalAcornStateException("offset == -1 for " + fileName + " in " + readDirectory.toAbsolutePath() + ", dirty=" + dirty + ", resident=" + resident + ", forceResident=" + forceResident);
238 this.offset = offset;
239 this.length = length;
240 if(overwrite() && offset > 0)
241 throw new IllegalAcornStateException("overwrite() == true && offset > 0 for " + fileName + " in " + readDirectory.toAbsolutePath() + ", dirty=" + dirty + ", resident=" + resident + ", forceResident=" + forceResident);
244 private Path getDirectory() throws AcornAccessVerificationException {
245 if(VERIFY) verifyAccess();
246 return readDirectory;
249 public void moveTo(Path path) {
250 readDirectory = path;