1 /*******************************************************************************
2 * Copyright (c) 2012, 2016 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
12 * Alexandre Montplaisir - Initial API and implementation
13 *******************************************************************************/
15 package org
.eclipse
.tracecompass
.internal
.statesystem
.core
.backend
.historytree
;
18 import java
.io
.IOException
;
19 import java
.util
.List
;
21 import org
.eclipse
.jdt
.annotation
.NonNull
;
22 import org
.eclipse
.tracecompass
.common
.core
.collect
.BufferedBlockingQueue
;
23 import org
.eclipse
.tracecompass
.internal
.statesystem
.core
.Activator
;
24 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateSystemDisposedException
;
25 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.TimeRangeException
;
26 import org
.eclipse
.tracecompass
.statesystem
.core
.interval
.ITmfStateInterval
;
27 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
;
28 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.TmfStateValue
;
31 * Variant of the HistoryTreeBackend which runs all the interval-insertion logic
32 * in a separate thread.
34 * @author Alexandre Montplaisir
36 public final class ThreadedHistoryTreeBackend
extends HistoryTreeBackend
39 private static final int CHUNK_SIZE
= 127;
40 private final @NonNull BufferedBlockingQueue
<HTInterval
> intervalQueue
;
41 private final @NonNull Thread shtThread
;
43 * The backend tracks its end time separately from the tree, to take into
44 * consideration intervals in the queue.
46 private long fEndTime
;
49 * New state history constructor
51 * Note that it usually doesn't make sense to use a Threaded HT if you're
52 * opening an existing state-file, but you know what you're doing...
55 * The state system's id
57 * The name of the history file that will be created. Should end
59 * @param providerVersion
60 * Version of of the state provider. We will only try to reopen
61 * existing files if this version matches the one in the
64 * The earliest timestamp stored in the history
66 * The size of the interval insertion queue. 2000 - 10000 usually
69 * The size of the blocks in the file
71 * The maximum number of children allowed for each core node
73 * If there was a problem opening the history file for writing
75 public ThreadedHistoryTreeBackend(@NonNull String ssid
,
83 super(ssid
, newStateFile
, providerVersion
, startTime
, blockSize
, maxChildren
);
86 intervalQueue
= new BufferedBlockingQueue
<>(queueSize
/ CHUNK_SIZE
, CHUNK_SIZE
);
87 shtThread
= new Thread(this, "History Tree Thread"); //$NON-NLS-1$
92 * New State History constructor. This version provides default values for
93 * blockSize and maxChildren.
96 * The state system's id
98 * The name of the history file that will be created. Should end
100 * @param providerVersion
101 * Version of of the state provider. We will only try to reopen
102 * existing files if this version matches the one in the
105 * The earliest timestamp stored in the history
107 * The size of the interval insertion queue. 2000 - 10000 usually
109 * @throws IOException
110 * If there was a problem opening the history file for writing
112 public ThreadedHistoryTreeBackend(@NonNull String ssid
,
118 super(ssid
, newStateFile
, providerVersion
, startTime
);
119 fEndTime
= startTime
;
121 intervalQueue
= new BufferedBlockingQueue
<>(queueSize
/ CHUNK_SIZE
, CHUNK_SIZE
);
122 shtThread
= new Thread(this, "History Tree Thread"); //$NON-NLS-1$
127 * The Threaded version does not specify an "existing file" constructor,
128 * since the history is already built (and we only use the other thread
129 * during building). Just use a plain HistoryTreeProvider in this case.
131 * TODO but what about streaming??
135 public void insertPastState(long stateStartTime
, long stateEndTime
,
136 int quark
, ITmfStateValue value
) throws TimeRangeException
{
138 * Here, instead of directly inserting the elements in the History Tree
139 * underneath, we'll put them in the Queue. They will then be taken and
140 * processed by the other thread executing the run() method.
142 HTInterval interval
= new HTInterval(stateStartTime
, stateEndTime
,
143 quark
, (TmfStateValue
) value
);
144 intervalQueue
.put(interval
);
145 fEndTime
= Math
.max(fEndTime
, stateEndTime
);
149 public long getEndTime() {
154 public void finishedBuilding(long endTime
) {
156 * We need to commit everything in the History Tree and stop the
157 * standalone thread before returning to the StateHistorySystem. (SHS
158 * will then write the Attribute Tree to the file, that must not happen
159 * at the same time we are writing the last nodes!)
162 stopRunningThread(endTime
);
163 setFinishedBuilding(true);
168 public void dispose() {
169 if (!isFinishedBuilding()) {
170 stopRunningThread(Long
.MAX_VALUE
);
173 * isFinishedBuilding remains false, so the superclass will ask the
174 * back-end to delete the file.
179 private void stopRunningThread(long endTime
) {
180 if (!shtThread
.isAlive()) {
185 * Send a "poison pill" in the queue, then wait for the HT to finish its
189 HTInterval pill
= new HTInterval(-1, endTime
, -1, TmfStateValue
.nullValue());
190 intervalQueue
.put(pill
);
191 intervalQueue
.flushInputBuffer();
193 } catch (TimeRangeException e
) {
194 Activator
.getDefault().logError("Error closing state system", e
); //$NON-NLS-1$
195 } catch (InterruptedException e
) {
196 Activator
.getDefault().logError("State system interrupted", e
); //$NON-NLS-1$
203 HTInterval currentInterval
= intervalQueue
.blockingPeek();
204 while (currentInterval
.getStartTime() != -1) {
205 /* Send the interval to the History Tree */
206 getSHT().insertInterval(currentInterval
);
207 /* Actually remove the interval from the queue */
208 // FIXME Replace with remove() once it is implemented.
209 intervalQueue
.take();
210 currentInterval
= intervalQueue
.blockingPeek();
212 if (currentInterval
.getAttribute() != -1) {
213 /* Make sure this is the "poison pill" we are waiting for */
214 throw new IllegalStateException();
217 * We've been told we're done, let's write down everything and quit.
218 * The end time of this "signal interval" is actually correct.
220 getSHT().closeTree(currentInterval
.getEndTime());
222 } catch (TimeRangeException e
) {
223 /* This should not happen */
224 Activator
.getDefault().logError("Error starting the state system", e
); //$NON-NLS-1$
228 // ------------------------------------------------------------------------
230 // ------------------------------------------------------------------------
233 public void doQuery(List
<ITmfStateInterval
> currentStateInfo
, long t
)
234 throws TimeRangeException
, StateSystemDisposedException
{
235 super.doQuery(currentStateInfo
, t
);
237 if (isFinishedBuilding()) {
239 * The history tree is the only place to look for intervals once
240 * construction is finished.
246 * It is possible we may have missed some intervals due to them being in
247 * the queue while the query was ongoing. Go over the results to see if
250 for (int i
= 0; i
< currentStateInfo
.size(); i
++) {
251 if (currentStateInfo
.get(i
) == null) {
252 /* Query the missing interval via "unicast" */
253 ITmfStateInterval interval
= doSingularQuery(t
, i
);
254 currentStateInfo
.set(i
, interval
);
260 public ITmfStateInterval
doSingularQuery(long t
, int attributeQuark
)
261 throws TimeRangeException
, StateSystemDisposedException
{
262 ITmfStateInterval ret
= super.doSingularQuery(t
, attributeQuark
);
268 * We couldn't find the interval in the history tree. It's possible that
269 * it is currently in the intervalQueue. Look for it there. Note that
270 * BufferedBlockingQueue's iterator() is thread-safe (no need to lock
273 for (ITmfStateInterval interval
: intervalQueue
) {
274 if (interval
.getAttribute() == attributeQuark
&& interval
.intersects(t
)) {
280 * If we missed it again, it's because it got inserted in the tree
281 * *while we were iterating* on the queue. One last pass in the tree
284 * This case is really rare, which is why we do a second pass at the end
285 * if needed, instead of systematically checking in the queue first
288 return super.doSingularQuery(t
, attributeQuark
);