]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.acorn/src/org/simantics/acorn/lru/LRUObject.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.acorn / src / org / simantics / acorn / lru / LRUObject.java
1 package org.simantics.acorn.lru;
2
3 import java.io.IOException;
4 import java.nio.file.Path;
5 import java.util.concurrent.Semaphore;
6 import java.util.concurrent.TimeUnit;
7
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;
15
16 public abstract class LRUObject<MapKey, MapValue extends LRUObject<MapKey, MapValue>> implements Persistable {
17
18     public abstract Logger getLogger();
19         public static boolean VERIFY = true;
20         
21         // Final stuff
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;
27         
28         // Mutable stuff
29         protected long accessTime = AccessTime.getInstance().getAccessTime();
30         private int offset;
31         private int length;
32         private boolean resident = true;
33         private boolean dirty = true;
34         private boolean forceResident = false;
35         
36         // DEBUG
37 //      private boolean isForceResidentSetAfterLastGet = false;
38         
39         private Path readDirectory;
40
41         private Thread mutexOwner;
42
43         // for loading
44         public LRUObject(LRU<MapKey, MapValue> LRU, FileCache fileCache, MapKey key, Path readDirectory, String fileName, int offset, int length, boolean dirty, boolean resident) {
45                 this.LRU = LRU;
46                 this.fileCache = fileCache;
47                 this.key = key;
48                 this.fileName = fileName;
49                 this.offset = offset;
50                 this.length = length;
51                 this.readDirectory = readDirectory;
52                 this.dirty = dirty;
53                 this.resident = resident;
54         }
55
56         // for creating
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);
59         }
60
61         /*
62          * Public interface
63          */
64         public MapKey getKey() {
65                 // This can be called without mutex
66                 return key;
67         }
68         
69         public void acquireMutex() throws IllegalAcornStateException {
70                 try {
71                         while(!mutex.tryAcquire(3, TimeUnit.SECONDS)) {
72                                 getLogger().info("Mutex is taking a long time to acquire - owner is " + mutexOwner);
73                         }
74                         
75                         if(VERIFY)
76                                 mutexOwner = Thread.currentThread();
77
78                 } catch (InterruptedException e) {
79                         throw new IllegalAcornStateException(e);
80                 }
81         }
82         
83         public boolean tryAcquireMutex() {
84                 return mutex.tryAcquire();
85         }
86         
87         public void releaseMutex() {
88                 mutex.release();
89         }
90
91         @Override
92         public void toFile(Path bytes) throws IOException {
93                 if(VERIFY) {
94                     try {
95                 verifyAccess();
96             } catch (AcornAccessVerificationException e) {
97                 throw new IOException("Exception occured during toFile for file " + fileName, e);
98             }
99                 }
100         try {
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);
109         }
110     }
111         
112         public int makeResident() throws AcornAccessVerificationException, IllegalAcornStateException {
113                 if(VERIFY) verifyAccess();
114                 return LRU.makeResident(this, false);
115         }
116
117         public int makeResident(boolean keepResident) throws AcornAccessVerificationException, IllegalAcornStateException {
118                 if(VERIFY) verifyAccess();
119                 return LRU.makeResident(this, true);
120         }
121
122         /*
123          * Package implementation details
124          */
125
126         abstract void release();
127         abstract String getExtension();
128         
129         String getStateKey() throws IllegalAcornStateException, AcornAccessVerificationException {
130                 String result = getKey().toString() + "#" + getDirectory().getFileName() + "#" + getOffset() + "#" + getLength(); 
131                 if(offset == -1)
132                     throw new IllegalAcornStateException(result);
133                 return result; 
134         }
135
136         long getLastAccessTime() throws AcornAccessVerificationException {
137                 if(VERIFY) verifyAccess();
138                 return accessTime;
139         }
140         
141         void accessed() throws AcornAccessVerificationException {
142                 if(VERIFY) verifyAccess();
143                 accessTime = AccessTime.getInstance().getAccessTime();
144         }
145         
146         boolean persist() throws AcornAccessVerificationException {
147                 if(VERIFY) verifyAccess();
148                 if(LRU.persist(this)) {
149                         readDirectory = LRU.getDirectory();
150                         return true;
151                 } else {
152                         return false;
153                 }
154         }
155         
156         void setForceResident(boolean value) throws AcornAccessVerificationException {
157         if(VERIFY) verifyAccess();
158         forceResident = value;
159 //        isForceResidentSetAfterLastGet = true;
160         }
161         
162         boolean canBePersisted() throws AcornAccessVerificationException {
163                 if(VERIFY) verifyAccess();
164 //              isForceResidentSetAfterLastGet = false;
165                 return !forceResident;
166         }
167         
168         boolean isDirty() throws AcornAccessVerificationException {
169                 if(VERIFY) verifyAccess();
170                 return dirty;
171         }
172         
173         boolean isResident() throws AcornAccessVerificationException {
174                 if(VERIFY) verifyAccess();
175                 return resident;
176         }
177         
178         String getFileName() throws AcornAccessVerificationException {
179                 if(VERIFY) verifyAccess();
180                 return fileName;
181         }
182
183         void setResident(boolean value) throws AcornAccessVerificationException {
184                 if(VERIFY) verifyAccess();
185                 resident = value;
186         }
187         
188         void setDirty(boolean value) throws AcornAccessVerificationException {
189                 if(VERIFY) verifyAccess();
190                 dirty = value;
191         }
192         
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());
199         }
200         
201         /*
202          * Protected implementation details
203          */
204
205         abstract protected boolean overwrite();
206         
207         abstract protected Pair<byte[],Integer> toBytes() throws IllegalAcornStateException;
208         
209         protected void setDirty() throws AcornAccessVerificationException {
210                 if(VERIFY) verifyAccess();
211                 dirty = true;
212         }
213         
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);
217         }
218
219         protected synchronized void cancelForceResident() throws AcornAccessVerificationException {
220                 setForceResident(false);
221         }
222         
223         /*
224          * Private implementation details
225          */
226         
227         private int getOffset() throws AcornAccessVerificationException {
228                 if(VERIFY) verifyAccess();
229                 return offset;
230         }
231         
232         private int getLength() throws AcornAccessVerificationException {
233                 if(VERIFY) verifyAccess();
234                 return length;
235         }
236         
237         private void setPosition(int offset, int length) throws AcornAccessVerificationException, IllegalAcornStateException {
238                 if(VERIFY) verifyAccess();
239                 if(offset == -1)
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);
245         }
246         
247         private Path getDirectory() throws AcornAccessVerificationException {
248                 if(VERIFY) verifyAccess();
249                 return readDirectory;
250         }
251
252         public void moveTo(Path path) {
253                 readDirectory = path;
254         }
255         
256 }