1 /*******************************************************************************
2 * Copyright (c) 2012, 2015 Ericsson
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Alexandre Montplaisir - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.tracecompass
.tmf
.core
.statesystem
;
15 import java
.util
.concurrent
.ArrayBlockingQueue
;
16 import java
.util
.concurrent
.BlockingQueue
;
18 import org
.eclipse
.jdt
.annotation
.Nullable
;
19 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystem
;
20 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystemBuilder
;
21 import org
.eclipse
.tracecompass
.tmf
.core
.event
.ITmfEvent
;
22 import org
.eclipse
.tracecompass
.tmf
.core
.event
.TmfEvent
;
23 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.ITmfTimestamp
;
24 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfContext
;
25 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
28 * Instead of using IStateChangeInput directly, one can extend this class, which
29 * defines a lot of the common functions of the state change input plugin.
31 * It will handle the state-system-processing in a separate thread, which is
32 * normally not a bad idea for traces of some size.
34 * processEvent() is replaced with eventHandle(), so that all the multi-thread
35 * logic is abstracted away.
37 * @author Alexandre Montplaisir
40 public abstract class AbstractTmfStateProvider
implements ITmfStateProvider
{
42 private static final int DEFAULT_EVENTS_QUEUE_SIZE
= 10000;
44 private final ITmfTrace fTrace
;
45 private final Class
<?
extends ITmfEvent
> fEventType
;
46 private final BlockingQueue
<ITmfEvent
> fEventsQueue
;
47 private final Thread fEventHandlerThread
;
49 private boolean fStateSystemAssigned
;
51 /** State system in which to insert the state changes */
52 private @Nullable ITmfStateSystemBuilder fSS
= null;
55 * Instantiate a new state provider plugin.
58 * The LTTng 2.0 kernel trace directory
60 * The specific class for the event type that will be used within
63 * Name given to this state change input. Only used internally.
65 public AbstractTmfStateProvider(ITmfTrace trace
,
66 Class
<?
extends ITmfEvent
> eventType
, String id
) {
68 fEventType
= eventType
;
69 fEventsQueue
= new ArrayBlockingQueue
<>(DEFAULT_EVENTS_QUEUE_SIZE
);
70 fStateSystemAssigned
= false;
72 fEventHandlerThread
= new Thread(new EventProcessor(), id
+ " Event Handler"); //$NON-NLS-1$
76 * Get the state system builder of this provider (to insert states in).
78 * @return The state system object to be filled
80 protected @Nullable ITmfStateSystemBuilder
getStateSystemBuilder() {
85 public ITmfTrace
getTrace() {
93 public long getStartTime() {
94 return fTrace
.getStartTime().normalize(0, ITmfTimestamp
.NANOSECOND_SCALE
).getValue();
101 public void assignTargetStateSystem(ITmfStateSystemBuilder ssb
) {
103 fStateSystemAssigned
= true;
104 fEventHandlerThread
.start();
111 public @Nullable ITmfStateSystem
getAssignedStateSystem() {
116 public void dispose() {
117 /* Insert a null event in the queue to stop the event handler's thread. */
119 fEventsQueue
.put(END_EVENT
);
120 fEventHandlerThread
.join();
121 } catch (InterruptedException e
) {
124 fStateSystemAssigned
= false;
129 public final Class
<?
extends ITmfEvent
> getExpectedEventType() {
134 public final void processEvent(ITmfEvent event
) {
135 /* Make sure the target state system has been assigned */
136 if (!fStateSystemAssigned
) {
137 System
.err
.println("Cannot process event without a target state system"); //$NON-NLS-1$
141 /* Insert the event we're received into the events queue */
142 ITmfEvent curEvent
= event
;
144 fEventsQueue
.put(curEvent
);
145 } catch (InterruptedException e
) {
151 * Block the caller until the events queue is empty.
153 public void waitForEmptyQueue() {
155 * We will first insert a dummy event that is guaranteed to not modify
156 * the state. That way, when that event leaves the queue, we will know
157 * for sure that the state system processed the preceding real event.
160 fEventsQueue
.put(EMPTY_QUEUE_EVENT
);
161 while (!fEventsQueue
.isEmpty()) {
164 } catch (InterruptedException e
) {
169 // ------------------------------------------------------------------------
170 // Special event types
171 // ------------------------------------------------------------------------
173 /** Fake event indicating the build is over, and the provider should close */
174 private static class EndEvent
extends TmfEvent
{
176 super(null, ITmfContext
.UNKNOWN_RANK
, null, null, null);
180 /** Fake event indicating we want to clear the current queue */
181 private static class EmptyQueueEvent
extends TmfEvent
{
182 public EmptyQueueEvent() {
183 super(null, ITmfContext
.UNKNOWN_RANK
, null, null, null);
187 private static final EndEvent END_EVENT
= new EndEvent();
188 private static final EmptyQueueEvent EMPTY_QUEUE_EVENT
= new EmptyQueueEvent();
190 // ------------------------------------------------------------------------
192 // ------------------------------------------------------------------------
195 * This is the runner class for the second thread, which will take the
196 * events from the queue and pass them through the state system.
198 private class EventProcessor
implements Runnable
{
200 private @Nullable ITmfEvent currentEvent
;
204 if (!fStateSystemAssigned
) {
205 System
.err
.println("Cannot run event manager without assigning a target state system first!"); //$NON-NLS-1$
211 event
= fEventsQueue
.take();
212 /* This is a singleton, we want to do != instead of !x.equals */
213 while (event
!= END_EVENT
) {
214 if (event
== EMPTY_QUEUE_EVENT
) {
215 /* Synchronization event, should be ignored */
216 event
= fEventsQueue
.take();
220 currentEvent
= event
;
222 /* Make sure this is an event the sub-class can process */
223 if (fEventType
.isInstance(event
) && event
.getType() != null) {
226 event
= fEventsQueue
.take();
228 /* We've received the last event, clean up */
230 } catch (InterruptedException e
) {
231 /* We've been interrupted abnormally */
232 System
.out
.println("Event handler interrupted!"); //$NON-NLS-1$
237 private void closeStateSystem() {
238 ITmfEvent event
= currentEvent
;
239 final long endTime
= (event
== null) ?
0 :
240 event
.getTimestamp().normalize(0, ITmfTimestamp
.NANOSECOND_SCALE
).getValue();
243 fSS
.closeHistory(endTime
);
248 // ------------------------------------------------------------------------
250 // ------------------------------------------------------------------------
253 * Handle the given event and send the appropriate state transitions into
254 * the the state system.
256 * This is basically the same thing as IStateChangeInput.processEvent(),
257 * except here processEvent() and eventHandle() are run in two different
258 * threads (and the AbstractStateChangeInput takes care of processEvent()
262 * The event to process. If you need a specific event type, you
263 * should check for its instance right at the beginning.
265 protected abstract void eventHandle(ITmfEvent event
);