ss: Rework the HTNode cache
[deliverable/tracecompass.git] / statesystem / org.eclipse.tracecompass.statesystem.core / src / org / eclipse / tracecompass / internal / statesystem / core / backend / historytree / HT_IO.java
CommitLineData
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
e894a508 13package org.eclipse.tracecompass.internal.statesystem.core.backend.historytree;
a52fde77 14
1f48ef66
AM
15import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
16
a52fde77
AM
17import java.io.File;
18import java.io.FileInputStream;
19import java.io.FileOutputStream;
20import java.io.IOException;
3b7f5abe 21import java.nio.channels.ClosedChannelException;
a52fde77 22import java.nio.channels.FileChannel;
b2648641 23import java.util.logging.Logger;
a52fde77 24
aa353506 25import org.eclipse.jdt.annotation.NonNull;
b2648641 26import org.eclipse.tracecompass.common.core.log.TraceCompassLog;
1f48ef66
AM
27import java.util.Objects;
28import java.util.concurrent.ExecutionException;
29
30import org.eclipse.jdt.annotation.Nullable;
49a817d5
MK
31import org.eclipse.tracecompass.internal.statesystem.core.Activator;
32
1f48ef66
AM
33import com.google.common.cache.CacheBuilder;
34import com.google.common.cache.CacheLoader;
35import com.google.common.cache.LoadingCache;
36
a52fde77 37/**
360f899e
EB
38 * This class abstracts inputs/outputs of the HistoryTree nodes.
39 *
40 * It contains all the methods and descriptors to handle reading/writing nodes
41 * to the tree-file on disk and all the caching mechanisms.
42 *
49a817d5
MK
43 * This abstraction is mainly for code isolation/clarification purposes. Every
44 * HistoryTree must contain 1 and only 1 HT_IO element.
3de60236 45 *
ffd0aa67 46 * @author Alexandre Montplaisir
a52fde77
AM
47 */
48class HT_IO {
4018d70a 49
1f48ef66
AM
50 private static final Logger LOGGER = TraceCompassLog.getLogger(HT_IO.class);
51
52 // ------------------------------------------------------------------------
53 // Global cache of nodes
54 // ------------------------------------------------------------------------
55
56 private static final class CacheKey {
57
58 public final HT_IO fStateHistory;
59 public final int fSeqNumber;
4018d70a 60
1f48ef66
AM
61 public CacheKey(HT_IO stateHistory, int seqNumber) {
62 fStateHistory = stateHistory;
63 fSeqNumber = seqNumber;
4018d70a
MK
64 }
65
1f48ef66
AM
66 @Override
67 public int hashCode() {
68 return Objects.hash(fStateHistory, fSeqNumber);
4018d70a
MK
69 }
70
1f48ef66
AM
71 @Override
72 public boolean equals(@Nullable Object obj) {
73 if (this == obj) {
74 return true;
75 }
76 if (obj == null) {
77 return false;
78 }
79 if (getClass() != obj.getClass()) {
80 return false;
81 }
82 CacheKey other = (CacheKey) obj;
83 return (fStateHistory.equals(other.fStateHistory) &&
84 fSeqNumber == other.fSeqNumber);
4018d70a
MK
85 }
86 }
87
1f48ef66
AM
88 private static final int CACHE_SIZE = 256;
89
90 private static final LoadingCache<CacheKey, HTNode> NODE_CACHE =
91 checkNotNull(CacheBuilder.newBuilder()
92 .maximumSize(CACHE_SIZE)
93 .build(new CacheLoader<CacheKey, HTNode>() {
94 @Override
95 public HTNode load(CacheKey key) throws IOException {
96 HT_IO io = key.fStateHistory;
97 int seqNb = key.fSeqNumber;
98
99 LOGGER.finest(() -> "[HtIo:CacheMiss] seqNum=" + seqNb); //$NON-NLS-1$
100
101 io.seekFCToNodePos(io.fFileChannelIn, seqNb);
102 return HTNode.readNode(io.fConfig, io.fFileChannelIn);
103 }
104 }));
105
106
107 // ------------------------------------------------------------------------
108 // Instance fields
109 // ------------------------------------------------------------------------
110
360f899e
EB
111 /* Configuration of the History Tree */
112 private final HTConfig fConfig;
a52fde77
AM
113
114 /* Fields related to the file I/O */
c772ff74
MK
115 private final FileInputStream fFileInputStream;
116 private final FileOutputStream fFileOutputStream;
117 private final FileChannel fFileChannelIn;
118 private final FileChannel fFileChannelOut;
a52fde77 119
1f48ef66
AM
120 // ------------------------------------------------------------------------
121 // Methods
122 // ------------------------------------------------------------------------
123
6993c1ca 124
b2648641 125
a52fde77
AM
126 /**
127 * Standard constructor
3de60236 128 *
360f899e 129 * @param config
49a817d5 130 * The configuration object for the StateHistoryTree
ab604305 131 * @param newFile
360f899e 132 * Flag indicating that the file must be created from scratch
49a817d5 133 *
a52fde77 134 * @throws IOException
360f899e 135 * An exception can be thrown when file cannot be accessed
a52fde77 136 */
8d47cc34 137 public HT_IO(HTConfig config, boolean newFile) throws IOException {
360f899e 138 fConfig = config;
a52fde77 139
360f899e 140 File historyTreeFile = config.getStateFile();
a52fde77 141 if (newFile) {
360f899e 142 boolean success1 = true;
a52fde77
AM
143 /* Create a new empty History Tree file */
144 if (historyTreeFile.exists()) {
ab604305
AM
145 success1 = historyTreeFile.delete();
146 }
cb42195c 147 boolean success2 = historyTreeFile.createNewFile();
ab604305
AM
148 if (!(success1 && success2)) {
149 /* It seems we do not have permission to create the new file */
150 throw new IOException("Cannot create new file at " + //$NON-NLS-1$
151 historyTreeFile.getName());
a52fde77 152 }
c772ff74
MK
153 fFileInputStream = new FileInputStream(historyTreeFile);
154 fFileOutputStream = new FileOutputStream(historyTreeFile, false);
a52fde77
AM
155 } else {
156 /*
157 * We want to open an existing file, make sure we don't squash the
158 * existing content when opening the fos!
159 */
c772ff74
MK
160 fFileInputStream = new FileInputStream(historyTreeFile);
161 fFileOutputStream = new FileOutputStream(historyTreeFile, true);
a52fde77 162 }
c772ff74
MK
163 fFileChannelIn = fFileInputStream.getChannel();
164 fFileChannelOut = fFileOutputStream.getChannel();
a52fde77
AM
165 }
166
a52fde77 167 /**
8d47cc34 168 * Read a node from the file on disk.
3b7f5abe 169 *
8d47cc34
AM
170 * @param seqNumber
171 * The sequence number of the node to read.
172 * @return The object representing the node
3b7f5abe
AM
173 * @throws ClosedChannelException
174 * Usually happens because the file was closed while we were
175 * reading. Instead of using a big reader-writer lock, we'll
176 * just catch this exception.
a52fde77 177 */
aa353506 178 public synchronized @NonNull HTNode readNode(int seqNumber) throws ClosedChannelException {
1f48ef66
AM
179 /* Do a cache lookup. If it's not present it will be loaded from disk */
180 LOGGER.finest(() -> "[HtIo:CacheLookup] seqNum=" + seqNumber); //$NON-NLS-1$
181 CacheKey key = new CacheKey(this, seqNumber);
a52fde77 182 try {
1f48ef66 183 return checkNotNull(NODE_CACHE.get(key));
6993c1ca 184
1f48ef66
AM
185 } catch (ExecutionException e) {
186 /* Get the inner exception that was generated */
187 Throwable cause = e.getCause();
188 if (cause instanceof ClosedChannelException) {
189 throw (ClosedChannelException) cause;
190 }
4018d70a 191 /*
1f48ef66 192 * Other types of IOExceptions shouldn't happen at this point though.
4018d70a 193 */
49a817d5 194 Activator.getDefault().logError(e.getMessage(), e);
aa353506 195 throw new IllegalStateException();
a52fde77
AM
196 }
197 }
198
e8b7cc14 199 public synchronized void writeNode(HTNode node) {
a52fde77 200 try {
6993c1ca 201 int seqNumber = node.getSequenceNumber();
1f48ef66
AM
202
203 /* "Write-back" the node into the cache */
204 CacheKey key = new CacheKey(this, seqNumber);
205 NODE_CACHE.put(key, node);
6993c1ca 206
a52fde77 207 /* Position ourselves at the start of the node and write it */
c772ff74
MK
208 seekFCToNodePos(fFileChannelOut, seqNumber);
209 node.writeSelf(fFileChannelOut);
a52fde77
AM
210 } catch (IOException e) {
211 /* If we were able to open the file, we should be fine now... */
49a817d5 212 Activator.getDefault().logError(e.getMessage(), e);
a52fde77
AM
213 }
214 }
215
e8b7cc14 216 public FileChannel getFcOut() {
c772ff74 217 return fFileChannelOut;
a52fde77
AM
218 }
219
8d47cc34 220 public FileInputStream supplyATReader(int nodeOffset) {
a52fde77
AM
221 try {
222 /*
223 * Position ourselves at the start of the Mapping section in the
224 * file (which is right after the Blocks)
225 */
c772ff74 226 seekFCToNodePos(fFileChannelIn, nodeOffset);
a52fde77 227 } catch (IOException e) {
49a817d5 228 Activator.getDefault().logError(e.getMessage(), e);
a52fde77 229 }
c772ff74 230 return fFileInputStream;
a52fde77
AM
231 }
232
8d47cc34 233 public synchronized void closeFile() {
3de60236 234 try {
c772ff74
MK
235 fFileInputStream.close();
236 fFileOutputStream.close();
3de60236 237 } catch (IOException e) {
49a817d5 238 Activator.getDefault().logError(e.getMessage(), e);
3de60236 239 }
1a4205d9
AM
240 }
241
8d47cc34 242 public synchronized void deleteFile() {
1a4205d9 243 closeFile();
3de60236 244
360f899e 245 File historyTreeFile = fConfig.getStateFile();
6993c1ca 246 if (!historyTreeFile.delete()) {
36bf82a2 247 /* We didn't succeed in deleting the file */
49a817d5 248 Activator.getDefault().logError("Failed to delete" + historyTreeFile.getName()); //$NON-NLS-1$
36bf82a2
AM
249 }
250 }
251
a52fde77
AM
252 /**
253 * Seek the given FileChannel to the position corresponding to the node that
254 * has seqNumber
3de60236 255 *
49a817d5
MK
256 * @param fc
257 * the channel to seek
258 * @param seqNumber
259 * the node sequence number to seek the channel to
a52fde77 260 * @throws IOException
49a817d5 261 * If some other I/O error occurs
a52fde77
AM
262 */
263 private void seekFCToNodePos(FileChannel fc, int seqNumber)
264 throws IOException {
a52fde77 265 /*
360f899e 266 * Cast to (long) is needed to make sure the result is a long too and
a52fde77
AM
267 * doesn't get truncated
268 */
360f899e 269 fc.position(HistoryTree.TREE_HEADER_SIZE
49a817d5 270 + ((long) seqNumber) * fConfig.getBlockSize());
a52fde77
AM
271 }
272
273}
This page took 0.117351 seconds and 5 git commands to generate.