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