1 /*******************************************************************************
2 * Copyright (c) 2012, 2014 Ericsson
3 * Copyright (c) 2010, 2011 École Polytechnique de Montréal
4 * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
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
11 *******************************************************************************/
13 package org
.eclipse
.tracecompass
.internal
.statesystem
.core
.backend
.historytree
;
16 import java
.io
.FileInputStream
;
17 import java
.io
.FileOutputStream
;
18 import java
.io
.IOException
;
19 import java
.nio
.channels
.ClosedChannelException
;
20 import java
.nio
.channels
.FileChannel
;
22 import org
.eclipse
.jdt
.annotation
.NonNull
;
23 import org
.eclipse
.tracecompass
.internal
.statesystem
.core
.Activator
;
26 * This class abstracts inputs/outputs of the HistoryTree nodes.
28 * It contains all the methods and descriptors to handle reading/writing nodes
29 * to the tree-file on disk and all the caching mechanisms.
31 * This abstraction is mainly for code isolation/clarification purposes. Every
32 * HistoryTree must contain 1 and only 1 HT_IO element.
34 * @author Alexandre Montplaisir
38 /* Configuration of the History Tree */
39 private final HTConfig fConfig
;
41 /* Fields related to the file I/O */
42 private final FileInputStream fFileInputStream
;
43 private final FileOutputStream fFileOutputStream
;
44 private final FileChannel fFileChannelIn
;
45 private final FileChannel fFileChannelOut
;
47 // TODO test/benchmark optimal cache size
49 * Cache size, must be a power of 2
51 private static final int CACHE_SIZE
= 256;
52 private static final int CACHE_MASK
= CACHE_SIZE
- 1;
53 private final HTNode fNodeCache
[] = new HTNode
[CACHE_SIZE
];
56 * Standard constructor
59 * The configuration object for the StateHistoryTree
61 * Flag indicating that the file must be created from scratch
64 * An exception can be thrown when file cannot be accessed
66 public HT_IO(HTConfig config
, boolean newFile
) throws IOException
{
69 File historyTreeFile
= config
.getStateFile();
71 boolean success1
= true;
72 /* Create a new empty History Tree file */
73 if (historyTreeFile
.exists()) {
74 success1
= historyTreeFile
.delete();
76 boolean success2
= historyTreeFile
.createNewFile();
77 if (!(success1
&& success2
)) {
78 /* It seems we do not have permission to create the new file */
79 throw new IOException("Cannot create new file at " + //$NON-NLS-1$
80 historyTreeFile
.getName());
82 fFileInputStream
= new FileInputStream(historyTreeFile
);
83 fFileOutputStream
= new FileOutputStream(historyTreeFile
, false);
86 * We want to open an existing file, make sure we don't squash the
87 * existing content when opening the fos!
89 fFileInputStream
= new FileInputStream(historyTreeFile
);
90 fFileOutputStream
= new FileOutputStream(historyTreeFile
, true);
92 fFileChannelIn
= fFileInputStream
.getChannel();
93 fFileChannelOut
= fFileOutputStream
.getChannel();
97 * Read a node from the file on disk.
100 * The sequence number of the node to read.
101 * @return The object representing the node
102 * @throws ClosedChannelException
103 * Usually happens because the file was closed while we were
104 * reading. Instead of using a big reader-writer lock, we'll
105 * just catch this exception.
107 public synchronized @NonNull HTNode
readNode(int seqNumber
) throws ClosedChannelException
{
108 /* Do a cache lookup */
109 int offset
= seqNumber
& CACHE_MASK
;
110 HTNode readNode
= fNodeCache
[offset
];
111 if (readNode
!= null && readNode
.getSequenceNumber() == seqNumber
) {
117 seekFCToNodePos(fFileChannelIn
, seqNumber
);
118 readNode
= HTNode
.readNode(fConfig
, fFileChannelIn
);
120 /* Put the node in the cache. */
121 fNodeCache
[offset
] = readNode
;
124 } catch (ClosedChannelException e
) {
126 } catch (IOException e
) {
127 /* Other types of IOExceptions shouldn't happen at this point though */
128 Activator
.getDefault().logError(e
.getMessage(), e
);
129 throw new IllegalStateException();
133 public synchronized void writeNode(HTNode node
) {
135 /* Insert the node into the cache. */
136 int seqNumber
= node
.getSequenceNumber();
137 int offset
= seqNumber
& CACHE_MASK
;
138 fNodeCache
[offset
] = node
;
140 /* Position ourselves at the start of the node and write it */
141 seekFCToNodePos(fFileChannelOut
, seqNumber
);
142 node
.writeSelf(fFileChannelOut
);
143 } catch (IOException e
) {
144 /* If we were able to open the file, we should be fine now... */
145 Activator
.getDefault().logError(e
.getMessage(), e
);
149 public FileChannel
getFcOut() {
150 return fFileChannelOut
;
153 public FileInputStream
supplyATReader(int nodeOffset
) {
156 * Position ourselves at the start of the Mapping section in the
157 * file (which is right after the Blocks)
159 seekFCToNodePos(fFileChannelIn
, nodeOffset
);
160 } catch (IOException e
) {
161 Activator
.getDefault().logError(e
.getMessage(), e
);
163 return fFileInputStream
;
166 public synchronized void closeFile() {
168 fFileInputStream
.close();
169 fFileOutputStream
.close();
170 } catch (IOException e
) {
171 Activator
.getDefault().logError(e
.getMessage(), e
);
175 public synchronized void deleteFile() {
178 File historyTreeFile
= fConfig
.getStateFile();
179 if (!historyTreeFile
.delete()) {
180 /* We didn't succeed in deleting the file */
181 Activator
.getDefault().logError("Failed to delete" + historyTreeFile
.getName()); //$NON-NLS-1$
186 * Seek the given FileChannel to the position corresponding to the node that
190 * the channel to seek
192 * the node sequence number to seek the channel to
193 * @throws IOException
194 * If some other I/O error occurs
196 private void seekFCToNodePos(FileChannel fc
, int seqNumber
)
199 * Cast to (long) is needed to make sure the result is a long too and
200 * doesn't get truncated
202 fc
.position(HistoryTree
.TREE_HEADER_SIZE
203 + ((long) seqNumber
) * fConfig
.getBlockSize());