Commit | Line | Data |
---|---|---|
a52fde77 | 1 | /******************************************************************************* |
60ae41e1 | 2 | * Copyright (c) 2012, 2014 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 | /** | |
360f899e EB |
23 | * This class abstracts inputs/outputs of the HistoryTree nodes. |
24 | * | |
25 | * It contains all the methods and descriptors to handle reading/writing nodes | |
26 | * to the tree-file on disk and all the caching mechanisms. | |
27 | * | |
28 | * This abstraction is mainly for code isolation/clarification purposes. | |
29 | * Every HistoryTree must contain 1 and only 1 HT_IO element. | |
3de60236 | 30 | * |
ffd0aa67 | 31 | * @author Alexandre Montplaisir |
3de60236 | 32 | * |
a52fde77 AM |
33 | */ |
34 | class HT_IO { | |
360f899e EB |
35 | /* Configuration of the History Tree */ |
36 | private final HTConfig fConfig; | |
a52fde77 AM |
37 | |
38 | /* Fields related to the file I/O */ | |
3de60236 AM |
39 | private final FileInputStream fis; |
40 | private final FileOutputStream fos; | |
41 | private final FileChannel fcIn; | |
42 | private final FileChannel fcOut; | |
a52fde77 | 43 | |
0c1d4577 | 44 | // TODO test/benchmark optimal cache size |
6993c1ca EB |
45 | private final int CACHE_SIZE = 256; |
46 | private final HTNode fNodeCache[] = new HTNode[CACHE_SIZE]; | |
47 | ||
a52fde77 AM |
48 | /** |
49 | * Standard constructor | |
3de60236 | 50 | * |
360f899e EB |
51 | * @param config |
52 | * The configuration object for the StateHistoryTree | |
ab604305 | 53 | * @param newFile |
360f899e EB |
54 | * Flag indicating that the file must be created from scratch |
55 | ||
a52fde77 | 56 | * @throws IOException |
360f899e | 57 | * An exception can be thrown when file cannot be accessed |
a52fde77 | 58 | */ |
8d47cc34 | 59 | public HT_IO(HTConfig config, boolean newFile) throws IOException { |
360f899e | 60 | fConfig = config; |
a52fde77 | 61 | |
360f899e | 62 | File historyTreeFile = config.getStateFile(); |
a52fde77 | 63 | if (newFile) { |
360f899e | 64 | boolean success1 = true; |
a52fde77 AM |
65 | /* Create a new empty History Tree file */ |
66 | if (historyTreeFile.exists()) { | |
ab604305 AM |
67 | success1 = historyTreeFile.delete(); |
68 | } | |
cb42195c | 69 | boolean success2 = historyTreeFile.createNewFile(); |
ab604305 AM |
70 | if (!(success1 && success2)) { |
71 | /* It seems we do not have permission to create the new file */ | |
72 | throw new IOException("Cannot create new file at " + //$NON-NLS-1$ | |
73 | historyTreeFile.getName()); | |
a52fde77 | 74 | } |
a52fde77 AM |
75 | fis = new FileInputStream(historyTreeFile); |
76 | fos = new FileOutputStream(historyTreeFile, false); | |
77 | } else { | |
78 | /* | |
79 | * We want to open an existing file, make sure we don't squash the | |
80 | * existing content when opening the fos! | |
81 | */ | |
82 | this.fis = new FileInputStream(historyTreeFile); | |
83 | this.fos = new FileOutputStream(historyTreeFile, true); | |
84 | } | |
85 | this.fcIn = fis.getChannel(); | |
86 | this.fcOut = fos.getChannel(); | |
87 | } | |
88 | ||
a52fde77 | 89 | /** |
8d47cc34 | 90 | * Read a node from the file on disk. |
3b7f5abe | 91 | * |
8d47cc34 AM |
92 | * @param seqNumber |
93 | * The sequence number of the node to read. | |
94 | * @return The object representing the node | |
3b7f5abe AM |
95 | * @throws ClosedChannelException |
96 | * Usually happens because the file was closed while we were | |
97 | * reading. Instead of using a big reader-writer lock, we'll | |
98 | * just catch this exception. | |
a52fde77 | 99 | */ |
8d47cc34 | 100 | public synchronized HTNode readNode(int seqNumber) throws ClosedChannelException { |
6993c1ca EB |
101 | /* Do a cache lookup */ |
102 | int offset = seqNumber & (CACHE_SIZE - 1); | |
103 | HTNode readNode = fNodeCache[offset]; | |
104 | if (readNode != null && readNode.getSequenceNumber() == seqNumber) { | |
105 | return readNode; | |
106 | } | |
107 | ||
108 | /* Lookup on disk */ | |
a52fde77 AM |
109 | try { |
110 | seekFCToNodePos(fcIn, seqNumber); | |
360f899e | 111 | readNode = HTNode.readNode(fConfig, fcIn); |
6993c1ca EB |
112 | |
113 | /* Put the node in the cache. */ | |
114 | fNodeCache[offset] = readNode; | |
a52fde77 | 115 | return readNode; |
3b7f5abe AM |
116 | } catch (ClosedChannelException e) { |
117 | throw e; | |
a52fde77 | 118 | } catch (IOException e) { |
3b7f5abe | 119 | /* Other types of IOExceptions shouldn't happen at this point though */ |
a52fde77 AM |
120 | e.printStackTrace(); |
121 | return null; | |
122 | } | |
123 | } | |
124 | ||
e8b7cc14 | 125 | public synchronized void writeNode(HTNode node) { |
a52fde77 | 126 | try { |
6993c1ca EB |
127 | /* Insert the node into the cache. */ |
128 | int seqNumber = node.getSequenceNumber(); | |
129 | int offset = seqNumber & (CACHE_SIZE - 1); | |
130 | fNodeCache[offset] = node; | |
131 | ||
a52fde77 | 132 | /* Position ourselves at the start of the node and write it */ |
6993c1ca | 133 | seekFCToNodePos(fcOut, seqNumber); |
a52fde77 AM |
134 | node.writeSelf(fcOut); |
135 | } catch (IOException e) { | |
136 | /* If we were able to open the file, we should be fine now... */ | |
137 | e.printStackTrace(); | |
138 | } | |
139 | } | |
140 | ||
e8b7cc14 | 141 | public FileChannel getFcOut() { |
a52fde77 AM |
142 | return this.fcOut; |
143 | } | |
144 | ||
8d47cc34 | 145 | public FileInputStream supplyATReader(int nodeOffset) { |
a52fde77 AM |
146 | try { |
147 | /* | |
148 | * Position ourselves at the start of the Mapping section in the | |
149 | * file (which is right after the Blocks) | |
150 | */ | |
360f899e | 151 | seekFCToNodePos(fcIn, nodeOffset); |
a52fde77 AM |
152 | } catch (IOException e) { |
153 | e.printStackTrace(); | |
154 | } | |
155 | return fis; | |
156 | } | |
157 | ||
8d47cc34 | 158 | public synchronized void closeFile() { |
3de60236 AM |
159 | try { |
160 | fis.close(); | |
161 | fos.close(); | |
162 | } catch (IOException e) { | |
163 | e.printStackTrace(); | |
164 | } | |
1a4205d9 AM |
165 | } |
166 | ||
8d47cc34 | 167 | public synchronized void deleteFile() { |
1a4205d9 | 168 | closeFile(); |
3de60236 | 169 | |
360f899e | 170 | File historyTreeFile = fConfig.getStateFile(); |
6993c1ca | 171 | if (!historyTreeFile.delete()) { |
36bf82a2 AM |
172 | /* We didn't succeed in deleting the file */ |
173 | //TODO log it? | |
174 | } | |
175 | } | |
176 | ||
a52fde77 AM |
177 | /** |
178 | * Seek the given FileChannel to the position corresponding to the node that | |
179 | * has seqNumber | |
3de60236 | 180 | * |
360f899e EB |
181 | * @param fc the channel to seek |
182 | * @param seqNumber the node sequence number to seek the channel to | |
a52fde77 AM |
183 | * @throws IOException |
184 | */ | |
185 | private void seekFCToNodePos(FileChannel fc, int seqNumber) | |
186 | throws IOException { | |
a52fde77 | 187 | /* |
360f899e | 188 | * Cast to (long) is needed to make sure the result is a long too and |
a52fde77 AM |
189 | * doesn't get truncated |
190 | */ | |
360f899e EB |
191 | fc.position(HistoryTree.TREE_HEADER_SIZE |
192 | + ((long) seqNumber) * fConfig.getBlockSize()); | |
a52fde77 AM |
193 | } |
194 | ||
195 | } |