Commit | Line | Data |
---|---|---|
a52fde77 | 1 | /******************************************************************************* |
61759503 | 2 | * Copyright (c) 2012, 2013 Ericsson |
a52fde77 AM |
3 | * Copyright (c) 2010, 2011 École Polytechnique de Montréal |
4 | * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com> | |
3de60236 | 5 | * |
a52fde77 AM |
6 | * All rights reserved. This program and the accompanying materials are |
7 | * made available under the terms of the Eclipse Public License v1.0 which | |
8 | * accompanies this distribution, and is available at | |
9 | * http://www.eclipse.org/legal/epl-v10.html | |
3de60236 | 10 | * |
a52fde77 AM |
11 | *******************************************************************************/ |
12 | ||
f9a76cac | 13 | package org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.historytree; |
a52fde77 AM |
14 | |
15 | import java.io.File; | |
16 | import java.io.FileInputStream; | |
17 | import java.io.FileOutputStream; | |
18 | import java.io.IOException; | |
3b7f5abe | 19 | import java.nio.channels.ClosedChannelException; |
a52fde77 AM |
20 | import java.nio.channels.FileChannel; |
21 | ||
22 | /** | |
23 | * This class exists mainly for code isolation/clarification purposes. It | |
24 | * contains all the methods and descriptors to handle reading/writing to the | |
25 | * tree-file on disk and all the caching mechanisms. Every HistoryTree should | |
26 | * contain 1 and only 1 HT_IO element. | |
3de60236 | 27 | * |
ffd0aa67 | 28 | * @author Alexandre Montplaisir |
3de60236 | 29 | * |
a52fde77 AM |
30 | */ |
31 | class HT_IO { | |
32 | ||
33 | /* reference to the tree to which this IO-object belongs */ | |
34 | private final HistoryTree tree; | |
35 | ||
36 | /* Fields related to the file I/O */ | |
36bf82a2 | 37 | private final File historyTreeFile; |
3de60236 AM |
38 | private final FileInputStream fis; |
39 | private final FileOutputStream fos; | |
40 | private final FileChannel fcIn; | |
41 | private final FileChannel fcOut; | |
a52fde77 | 42 | |
0c1d4577 | 43 | // TODO test/benchmark optimal cache size |
6993c1ca EB |
44 | private final int CACHE_SIZE = 256; |
45 | private final HTNode fNodeCache[] = new HTNode[CACHE_SIZE]; | |
46 | ||
a52fde77 AM |
47 | /** |
48 | * Standard constructor | |
3de60236 | 49 | * |
a52fde77 | 50 | * @param tree |
ab604305 AM |
51 | * @param newFile |
52 | * Are we creating a new file from scratch? | |
a52fde77 AM |
53 | * @throws IOException |
54 | */ | |
55 | HT_IO(HistoryTree tree, boolean newFile) throws IOException { | |
56 | this.tree = tree; | |
cb42195c AM |
57 | historyTreeFile = tree.getConfig().getStateFile(); |
58 | boolean success1 = true; | |
a52fde77 AM |
59 | |
60 | if (newFile) { | |
61 | /* Create a new empty History Tree file */ | |
62 | if (historyTreeFile.exists()) { | |
ab604305 AM |
63 | success1 = historyTreeFile.delete(); |
64 | } | |
cb42195c | 65 | boolean success2 = historyTreeFile.createNewFile(); |
ab604305 AM |
66 | if (!(success1 && success2)) { |
67 | /* It seems we do not have permission to create the new file */ | |
68 | throw new IOException("Cannot create new file at " + //$NON-NLS-1$ | |
69 | historyTreeFile.getName()); | |
a52fde77 | 70 | } |
a52fde77 AM |
71 | fis = new FileInputStream(historyTreeFile); |
72 | fos = new FileOutputStream(historyTreeFile, false); | |
73 | } else { | |
74 | /* | |
75 | * We want to open an existing file, make sure we don't squash the | |
76 | * existing content when opening the fos! | |
77 | */ | |
78 | this.fis = new FileInputStream(historyTreeFile); | |
79 | this.fos = new FileOutputStream(historyTreeFile, true); | |
80 | } | |
81 | this.fcIn = fis.getChannel(); | |
82 | this.fcOut = fos.getChannel(); | |
83 | } | |
84 | ||
85 | /** | |
86 | * Generic "read node" method, which checks if the node is in memory first, | |
87 | * and if it's not it goes to disk to retrieve it. | |
3de60236 | 88 | * |
a52fde77 AM |
89 | * @param seqNumber |
90 | * Sequence number of the node we want | |
91 | * @return The wanted node in object form | |
3b7f5abe AM |
92 | * @throws ClosedChannelException |
93 | * If the channel was closed before we could read | |
a52fde77 | 94 | */ |
3b7f5abe | 95 | HTNode readNode(int seqNumber) throws ClosedChannelException { |
a52fde77 AM |
96 | HTNode node = readNodeFromMemory(seqNumber); |
97 | if (node == null) { | |
98 | return readNodeFromDisk(seqNumber); | |
99 | } | |
100 | return node; | |
101 | } | |
102 | ||
103 | private HTNode readNodeFromMemory(int seqNumber) { | |
cb42195c | 104 | for (HTNode node : tree.getLatestBranch()) { |
a52fde77 AM |
105 | if (node.getSequenceNumber() == seqNumber) { |
106 | return node; | |
107 | } | |
108 | } | |
109 | return null; | |
110 | } | |
111 | ||
112 | /** | |
113 | * This method here isn't private, if we know for sure the node cannot be in | |
114 | * memory it's a bit faster to use this directly (when opening a file from | |
115 | * disk for example) | |
3b7f5abe AM |
116 | * |
117 | * @throws ClosedChannelException | |
118 | * Usually happens because the file was closed while we were | |
119 | * reading. Instead of using a big reader-writer lock, we'll | |
120 | * just catch this exception. | |
a52fde77 | 121 | */ |
3b7f5abe | 122 | synchronized HTNode readNodeFromDisk(int seqNumber) throws ClosedChannelException { |
6993c1ca EB |
123 | /* Do a cache lookup */ |
124 | int offset = seqNumber & (CACHE_SIZE - 1); | |
125 | HTNode readNode = fNodeCache[offset]; | |
126 | if (readNode != null && readNode.getSequenceNumber() == seqNumber) { | |
127 | return readNode; | |
128 | } | |
129 | ||
130 | /* Lookup on disk */ | |
a52fde77 AM |
131 | try { |
132 | seekFCToNodePos(fcIn, seqNumber); | |
ffd0aa67 | 133 | readNode = HTNode.readNode(tree.getConfig(), fcIn); |
6993c1ca EB |
134 | |
135 | /* Put the node in the cache. */ | |
136 | fNodeCache[offset] = readNode; | |
a52fde77 | 137 | return readNode; |
3b7f5abe AM |
138 | } catch (ClosedChannelException e) { |
139 | throw e; | |
a52fde77 | 140 | } catch (IOException e) { |
3b7f5abe | 141 | /* Other types of IOExceptions shouldn't happen at this point though */ |
a52fde77 AM |
142 | e.printStackTrace(); |
143 | return null; | |
144 | } | |
145 | } | |
146 | ||
147 | void writeNode(HTNode node) { | |
148 | try { | |
6993c1ca EB |
149 | /* Insert the node into the cache. */ |
150 | int seqNumber = node.getSequenceNumber(); | |
151 | int offset = seqNumber & (CACHE_SIZE - 1); | |
152 | fNodeCache[offset] = node; | |
153 | ||
a52fde77 | 154 | /* Position ourselves at the start of the node and write it */ |
6993c1ca | 155 | seekFCToNodePos(fcOut, seqNumber); |
a52fde77 AM |
156 | node.writeSelf(fcOut); |
157 | } catch (IOException e) { | |
158 | /* If we were able to open the file, we should be fine now... */ | |
159 | e.printStackTrace(); | |
160 | } | |
161 | } | |
162 | ||
163 | FileChannel getFcOut() { | |
164 | return this.fcOut; | |
165 | } | |
166 | ||
167 | FileInputStream supplyATReader() { | |
168 | try { | |
169 | /* | |
170 | * Position ourselves at the start of the Mapping section in the | |
171 | * file (which is right after the Blocks) | |
172 | */ | |
173 | seekFCToNodePos(fcIn, tree.getNodeCount()); | |
174 | } catch (IOException e) { | |
175 | e.printStackTrace(); | |
176 | } | |
177 | return fis; | |
178 | } | |
179 | ||
180 | File supplyATWriterFile() { | |
cb42195c | 181 | return tree.getConfig().getStateFile(); |
a52fde77 AM |
182 | } |
183 | ||
184 | long supplyATWriterFilePos() { | |
cb42195c AM |
185 | return HistoryTree.TREE_HEADER_SIZE |
186 | + ((long) tree.getNodeCount() * tree.getConfig().getBlockSize()); | |
a52fde77 AM |
187 | } |
188 | ||
1a4205d9 | 189 | synchronized void closeFile() { |
3de60236 AM |
190 | try { |
191 | fis.close(); | |
192 | fos.close(); | |
193 | } catch (IOException e) { | |
194 | e.printStackTrace(); | |
195 | } | |
1a4205d9 AM |
196 | } |
197 | ||
198 | synchronized void deleteFile() { | |
199 | closeFile(); | |
3de60236 | 200 | |
6993c1ca | 201 | if (!historyTreeFile.delete()) { |
36bf82a2 AM |
202 | /* We didn't succeed in deleting the file */ |
203 | //TODO log it? | |
204 | } | |
205 | } | |
206 | ||
a52fde77 AM |
207 | /** |
208 | * Seek the given FileChannel to the position corresponding to the node that | |
209 | * has seqNumber | |
3de60236 | 210 | * |
a52fde77 AM |
211 | * @param seqNumber |
212 | * @throws IOException | |
213 | */ | |
214 | private void seekFCToNodePos(FileChannel fc, int seqNumber) | |
215 | throws IOException { | |
cb42195c AM |
216 | fc.position(HistoryTree.TREE_HEADER_SIZE + (long) seqNumber |
217 | * tree.getConfig().getBlockSize()); | |
a52fde77 AM |
218 | /* |
219 | * cast to (long) is needed to make sure the result is a long too and | |
220 | * doesn't get truncated | |
221 | */ | |
222 | } | |
223 | ||
224 | } |