/*******************************************************************************
* Copyright (c) 2012, 2013 Ericsson
* Copyright (c) 2010, 2011 École Polytechnique de Montréal
* Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
*******************************************************************************/
package fr.inria.linuxtools.statesystem.core.backend.historytree;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import fr.inria.linuxtools.internal.statesystem.core.Activator;
import fr.inria.linuxtools.internal.statesystem.core.backend.historytree.HTInterval;
import fr.inria.linuxtools.statesystem.core.exceptions.TimeRangeException;
import fr.inria.linuxtools.statesystem.core.statevalue.ITmfStateValue;
import fr.inria.linuxtools.statesystem.core.statevalue.TmfStateValue;
/**
* Variant of the HistoryTreeBackend which runs all the interval-insertion logic
* in a separate thread.
*
* @author Alexandre Montplaisir
* @since 3.0
*/
public final class ThreadedHistoryTreeBackend extends HistoryTreeBackend
implements Runnable {
private BlockingQueue<HTInterval> intervalQueue;
private final Thread shtThread;
/**
* New state history constructor
*
* Note that it usually doesn't make sense to use a Threaded HT if you're
* opening an existing state-file, but you know what you're doing...
*
* @param newStateFile
* The name of the history file that will be created. Should end
* in ".ht"
* @param blockSize
* The size of the blocks in the file
* @param maxChildren
* The maximum number of children allowed for each core node
* @param startTime
* The earliest timestamp stored in the history
* @param providerVersion
* Version of of the state provider. We will only try to reopen
* existing files if this version matches the one in the
* framework.
* @param queueSize
* The size of the interval insertion queue. 2000 - 10000 usually
* works well
* @throws IOException
* If there was a problem opening the history file for writing
*/
public ThreadedHistoryTreeBackend(File newStateFile, int blockSize,
int maxChildren, long startTime, int providerVersion, int queueSize)
throws IOException {
super(newStateFile, blockSize, maxChildren, providerVersion, startTime);
intervalQueue = new ArrayBlockingQueue<>(queueSize);
shtThread = new Thread(this, "History Tree Thread"); //$NON-NLS-1$
shtThread.start();
}
/**
* New State History constructor. This version provides default values for
* blockSize and maxChildren.
*
* @param newStateFile
* The name of the history file that will be created. Should end
* in ".ht"
* @param startTime
* The earliest timestamp stored in the history
* @param providerVersion
* Version of of the state provider. We will only try to reopen
* existing files if this version matches the one in the
* framework.
* @param queueSize
* The size of the interval insertion queue. 2000 - 10000 usually
* works well
* @throws IOException
* If there was a problem opening the history file for writing
*/
public ThreadedHistoryTreeBackend(File newStateFile, long startTime,
int providerVersion, int queueSize) throws IOException {
super(newStateFile, providerVersion, startTime);
intervalQueue = new ArrayBlockingQueue<>(queueSize);
shtThread = new Thread(this, "History Tree Thread"); //$NON-NLS-1$
shtThread.start();
}
/*
* The Threaded version does not specify an "existing file" constructor,
* since the history is already built (and we only use the other thread
* during building). Just use a plain HistoryTreeProvider in this case.
*
* TODO but what about streaming??
*/
@Override
public void insertPastState(long stateStartTime, long stateEndTime,
int quark, ITmfStateValue value) throws TimeRangeException {
/*
* Here, instead of directly inserting the elements in the History Tree
* underneath, we'll put them in the Queue. They will then be taken and
* processed by the other thread executing the run() method.
*/
HTInterval interval = new HTInterval(stateStartTime, stateEndTime,
quark, (TmfStateValue) value);
try {
intervalQueue.put(interval);
} catch (InterruptedException e) {
Activator.getDefault().logError("State system interrupted", e); //$NON-NLS-1$
}
}
@Override
public void finishedBuilding(long endTime) {
/*
* We need to commit everything in the History Tree and stop the
* standalone thread before returning to the StateHistorySystem. (SHS
* will then write the Attribute Tree to the file, that must not happen
* at the same time we are writing the last nodes!)
*/
stopRunningThread(endTime);
isFinishedBuilding = true;
return;
}
@Override
public void dispose() {
if (!isFinishedBuilding) {
stopRunningThread(Long.MAX_VALUE);
}
/*
* isFinishedBuilding remains false, so the superclass will ask the
* back-end to delete the file.
*/
super.dispose();
}
private void stopRunningThread(long endTime) {
if (!shtThread.isAlive()) {
return;
}
/*
* Send a "poison pill" in the queue, then wait for the HT to finish
* its closeTree()
*/
try {
HTInterval pill = new HTInterval(-1, endTime, -1, TmfStateValue.nullValue());
intervalQueue.put(pill);
shtThread.join();
} catch (TimeRangeException e) {
Activator.getDefault().logError("Error closing state system", e); //$NON-NLS-1$
} catch (InterruptedException e) {
Activator.getDefault().logError("State system interrupted", e); //$NON-NLS-1$
}
}
@Override
public void run() {
if (intervalQueue == null) {
Activator.getDefault().logError("Cannot start the storage backend without its interval queue."); //$NON-NLS-1$
return;
}
HTInterval currentInterval;
try {
currentInterval = intervalQueue.take();
while (currentInterval.getStartTime() != -1) {
/* Send the interval to the History Tree */
sht.insertInterval(currentInterval);
currentInterval = intervalQueue.take();
}
assert (currentInterval.getAttribute() == -1);
/*
* We've been told we're done, let's write down everything and quit.
* The end time of this "signal interval" is actually correct.
*/
sht.closeTree(currentInterval.getEndTime());
return;
} catch (InterruptedException e) {
/* We've been interrupted abnormally */
Activator.getDefault().logError("State History Tree interrupted!", e); //$NON-NLS-1$
} catch (TimeRangeException e) {
/* This also should not happen */
Activator.getDefault().logError("Error starting the state system", e); //$NON-NLS-1$
}
}
}