import java.nio.channels.FileChannel;
/**
- * This class exists mainly for code isolation/clarification purposes. It
- * contains all the methods and descriptors to handle reading/writing to the
- * tree-file on disk and all the caching mechanisms. Every HistoryTree should
- * contain 1 and only 1 HT_IO element.
+ * This class abstracts inputs/outputs of the HistoryTree nodes.
+ *
+ * It contains all the methods and descriptors to handle reading/writing nodes
+ * to the tree-file on disk and all the caching mechanisms.
+ *
+ * This abstraction is mainly for code isolation/clarification purposes.
+ * Every HistoryTree must contain 1 and only 1 HT_IO element.
*
* @author Alexandre Montplaisir
*
*/
class HT_IO {
-
- /* reference to the tree to which this IO-object belongs */
- private final HistoryTree tree;
+ /* Configuration of the History Tree */
+ private final HTConfig fConfig;
/* Fields related to the file I/O */
- private final File historyTreeFile;
private final FileInputStream fis;
private final FileOutputStream fos;
private final FileChannel fcIn;
/**
* Standard constructor
*
- * @param tree
+ * @param config
+ * The configuration object for the StateHistoryTree
* @param newFile
- * Are we creating a new file from scratch?
+ * Flag indicating that the file must be created from scratch
+
* @throws IOException
+ * An exception can be thrown when file cannot be accessed
*/
- HT_IO(HistoryTree tree, boolean newFile) throws IOException {
- this.tree = tree;
- historyTreeFile = tree.getConfig().getStateFile();
- boolean success1 = true;
+ HT_IO(HTConfig config, boolean newFile) throws IOException {
+ fConfig = config;
+ File historyTreeFile = config.getStateFile();
if (newFile) {
+ boolean success1 = true;
/* Create a new empty History Tree file */
if (historyTreeFile.exists()) {
success1 = historyTreeFile.delete();
this.fcOut = fos.getChannel();
}
- /**
- * Generic "read node" method, which checks if the node is in memory first,
- * and if it's not it goes to disk to retrieve it.
- *
- * @param seqNumber
- * Sequence number of the node we want
- * @return The wanted node in object form
- * @throws ClosedChannelException
- * If the channel was closed before we could read
- */
- HTNode readNode(int seqNumber) throws ClosedChannelException {
- HTNode node = readNodeFromMemory(seqNumber);
- if (node == null) {
- return readNodeFromDisk(seqNumber);
- }
- return node;
- }
-
- private HTNode readNodeFromMemory(int seqNumber) {
- for (HTNode node : tree.getLatestBranch()) {
- if (node.getSequenceNumber() == seqNumber) {
- return node;
- }
- }
- return null;
- }
-
/**
* This method here isn't private, if we know for sure the node cannot be in
* memory it's a bit faster to use this directly (when opening a file from
* reading. Instead of using a big reader-writer lock, we'll
* just catch this exception.
*/
- synchronized HTNode readNodeFromDisk(int seqNumber) throws ClosedChannelException {
+ synchronized HTNode readNode(int seqNumber) throws ClosedChannelException {
/* Do a cache lookup */
int offset = seqNumber & (CACHE_SIZE - 1);
HTNode readNode = fNodeCache[offset];
/* Lookup on disk */
try {
seekFCToNodePos(fcIn, seqNumber);
- readNode = HTNode.readNode(tree.getConfig(), fcIn);
+ readNode = HTNode.readNode(fConfig, fcIn);
/* Put the node in the cache. */
fNodeCache[offset] = readNode;
return this.fcOut;
}
- FileInputStream supplyATReader() {
+ FileInputStream supplyATReader(int nodeOffset) {
try {
/*
* Position ourselves at the start of the Mapping section in the
* file (which is right after the Blocks)
*/
- seekFCToNodePos(fcIn, tree.getNodeCount());
+ seekFCToNodePos(fcIn, nodeOffset);
} catch (IOException e) {
e.printStackTrace();
}
return fis;
}
- File supplyATWriterFile() {
- return tree.getConfig().getStateFile();
- }
-
- long supplyATWriterFilePos() {
- return HistoryTree.TREE_HEADER_SIZE
- + ((long) tree.getNodeCount() * tree.getConfig().getBlockSize());
- }
-
synchronized void closeFile() {
try {
fis.close();
synchronized void deleteFile() {
closeFile();
+ File historyTreeFile = fConfig.getStateFile();
if (!historyTreeFile.delete()) {
/* We didn't succeed in deleting the file */
//TODO log it?
* Seek the given FileChannel to the position corresponding to the node that
* has seqNumber
*
- * @param seqNumber
+ * @param fc the channel to seek
+ * @param seqNumber the node sequence number to seek the channel to
* @throws IOException
*/
private void seekFCToNodePos(FileChannel fc, int seqNumber)
throws IOException {
- fc.position(HistoryTree.TREE_HEADER_SIZE + (long) seqNumber
- * tree.getConfig().getBlockSize());
/*
- * cast to (long) is needed to make sure the result is a long too and
+ * Cast to (long) is needed to make sure the result is a long too and
* doesn't get truncated
*/
+ fc.position(HistoryTree.TREE_HEADER_SIZE
+ + ((long) seqNumber) * fConfig.getBlockSize());
}
}
/** Latest timestamp found in the tree (at any given moment) */
private long treeEnd;
- /** How many nodes exist in this tree, total */
+ /** The total number of nodes that exists in this tree */
private int nodeCount;
/** "Cache" to keep the active nodes in memory */
latestBranch = new ArrayList<CoreNode>();
/* Prepare the IO object */
- treeIO = new HT_IO(this, true);
+ treeIO = new HT_IO(config, true);
/* Add the first node to the tree */
CoreNode firstNode = initNewCoreNode(-1, conf.getTreeStart());
buffer.flip();
/*
- * Check the magic number,to make sure we're opening the right type of
+ * Check the magic number to make sure we're opening the right type of
* file
*/
res = buffer.getInt();
if (res != expProviderVersion &&
expProviderVersion != ITmfStateProvider.IGNORE_PROVIDER_VERSION) {
/*
- * The existing history was built using a event handler that doesn't
- * match the current one in the framework. Information could be all
- * wrong, so we'll force a rebuild of the history file instead.
+ * The existing history was built using an event handler that doesn't
+ * match the current one in the framework.
+ *
+ * Information could be all wrong. Instead of keeping an incorrect
+ * history file, a rebuild is done.
*/
fc.close();
fis.close();
* file, not extremely elegant. But how to pass the information here to
* the SHT otherwise?
*/
- this.treeIO = new HT_IO(this, false);
+ this.treeIO = new HT_IO(config, false);
rebuildLatestBranch(rootNodeSeqNb);
this.treeEnd = latestBranch.get(0).getNodeEnd();
return nodeCount;
}
- HT_IO getTreeIO() {
- return treeIO;
- }
-
List<CoreNode> getLatestBranch() {
return Collections.unmodifiableList(latestBranch);
}
+ // ------------------------------------------------------------------------
+ // HT_IO interface
+ // ------------------------------------------------------------------------
+
+ File supplyATWriterFile() {
+ return config.getStateFile();
+ }
+
+ FileInputStream supplyATReader() {
+ return treeIO.supplyATReader(getNodeCount());
+ }
+
+ long supplyATWriterFilePos() {
+ return HistoryTree.TREE_HEADER_SIZE
+ + ((long) getNodeCount() * config.getBlockSize());
+ }
+
+ HTNode readNode(int seqNumber) throws ClosedChannelException {
+ /* Try to read the node from memory */
+ for (HTNode node : getLatestBranch()) {
+ if (node.getSequenceNumber() == seqNumber) {
+ return node;
+ }
+ }
+
+ /* Read the node from disk */
+ return treeIO.readNode(seqNumber);
+ }
+
+ void writeNode(HTNode node) {
+ treeIO.writeNode(node);
+ }
+
+ void closeFile() {
+ treeIO.closeFile();
+ }
+
+ void deleteFile() {
+ treeIO.deleteFile();
+ }
+
// ------------------------------------------------------------------------
// Operations
// ------------------------------------------------------------------------
this.latestBranch = new ArrayList<CoreNode>();
- nextChildNode = treeIO.readNodeFromDisk(rootNodeSeqNb);
+ nextChildNode = treeIO.readNode(rootNodeSeqNb);
latestBranch.add((CoreNode) nextChildNode);
while (latestBranch.get(latestBranch.size() - 1).getNbChildren() > 0) {
- nextChildNode = treeIO.readNodeFromDisk(latestBranch.get(latestBranch.size() - 1).getLatestChild());
+ nextChildNode = treeIO.readNode(latestBranch.get(latestBranch.size() - 1).getLatestChild());
latestBranch.add((CoreNode) nextChildNode);
}
}
* node has to be on disk
*/
if (currentNode.isDone()) {
- return treeIO.readNodeFromDisk(potentialNextSeqNb);
+ return treeIO.readNode(potentialNextSeqNb);
}
return treeIO.readNode(potentialNextSeqNb);
}
* History Tree backend for storing a state history. This is the basic version
* that runs in the same thread as the class creating it.
*
- * @author alexmont
+ * @author Alexandre Montplaisir
*
*/
public class HistoryTreeBackend implements IStateHistoryBackend {
/** The history tree that sits underneath */
protected final HistoryTree sht;
- /** Direct reference to the tree's IO object */
- private final HT_IO treeIO;
-
/** Indicates if the history tree construction is done */
protected boolean isFinishedBuilding = false;
final HTConfig conf = new HTConfig(newStateFile, blockSize, maxChildren,
providerVersion, startTime);
sht = new HistoryTree(conf);
- treeIO = sht.getTreeIO();
}
/**
public HistoryTreeBackend(File existingStateFile, int providerVersion)
throws IOException {
sht = new HistoryTree(existingStateFile, providerVersion);
- treeIO = sht.getTreeIO();
isFinishedBuilding = true;
}
@Override
public FileInputStream supplyAttributeTreeReader() {
- return treeIO.supplyATReader();
+ return sht.supplyATReader();
}
@Override
public File supplyAttributeTreeWriterFile() {
- return treeIO.supplyATWriterFile();
+ return sht.supplyATWriterFile();
}
@Override
public long supplyAttributeTreeWriterFilePosition() {
- return treeIO.supplyATWriterFilePos();
+ return sht.supplyATWriterFilePos();
}
@Override
public void removeFiles() {
- treeIO.deleteFile();
+ sht.deleteFile();
}
@Override
public void dispose() {
if (isFinishedBuilding) {
- treeIO.closeFile();
+ sht.closeFile();
} else {
/*
* The build is being interrupted, delete the file we partially
* built since it won't be complete, so shouldn't be re-used in the
* future (.deleteFile() will close the file first)
*/
- treeIO.deleteFile();
+ sht.deleteFile();
}
}
try {
for (int seq = 0; seq < sht.getNodeCount(); seq++) {
- node = treeIO.readNode(seq);
+ node = sht.readNode(seq);
total += node.getNodeUsagePRC();
}
} catch (ClosedChannelException e) {