tmf: fix concurrency issue in analyses modules (bug 447434)
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / tmf / core / statesystem / TmfStateSystemAnalysisModule.java
CommitLineData
8a6ff07f 1/*******************************************************************************
60ae41e1 2 * Copyright (c) 2013, 2014 École Polytechnique de Montréal
8a6ff07f
GB
3 *
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
8 *
9 * Contributors:
10 * Geneviève Bastien - Initial API and implementation
baa96b1d 11 * Bernd Hufmann - Integrated history builder functionality
8a6ff07f
GB
12 *******************************************************************************/
13
2bdf0193 14package org.eclipse.tracecompass.tmf.core.statesystem;
8a6ff07f
GB
15
16import java.io.File;
baa96b1d 17import java.io.IOException;
5237a931 18import java.util.Collections;
09ec275e 19import java.util.concurrent.CountDownLatch;
8a6ff07f
GB
20
21import org.eclipse.core.runtime.IProgressMonitor;
e1c415b3 22import org.eclipse.core.runtime.IStatus;
baa96b1d 23import org.eclipse.core.runtime.NullProgressMonitor;
8a6ff07f 24import org.eclipse.jdt.annotation.NonNull;
baa96b1d
BH
25import org.eclipse.jdt.annotation.NonNullByDefault;
26import org.eclipse.jdt.annotation.Nullable;
2bdf0193
AM
27import org.eclipse.tracecompass.internal.tmf.core.statesystem.backends.partial.PartialHistoryBackend;
28import org.eclipse.tracecompass.internal.tmf.core.statesystem.backends.partial.PartialStateSystem;
e894a508
AM
29import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
30import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
31import org.eclipse.tracecompass.statesystem.core.StateSystemFactory;
32import org.eclipse.tracecompass.statesystem.core.backend.IStateHistoryBackend;
33import org.eclipse.tracecompass.statesystem.core.backend.InMemoryBackend;
34import org.eclipse.tracecompass.statesystem.core.backend.NullBackend;
35import org.eclipse.tracecompass.statesystem.core.backend.historytree.HistoryTreeBackend;
36import org.eclipse.tracecompass.statesystem.core.backend.historytree.ThreadedHistoryTreeBackend;
2bdf0193
AM
37import org.eclipse.tracecompass.tmf.core.analysis.TmfAbstractAnalysisModule;
38import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
39import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
40import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest;
41import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest;
42import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
43import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
44import org.eclipse.tracecompass.tmf.core.trace.TmfExperiment;
45import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
8a6ff07f
GB
46
47/**
48 * Abstract analysis module to generate a state system. It is a base class that
49 * can be used as a shortcut by analysis who just need to build a single state
50 * system with a state provider.
51 *
52 * Analysis implementing this class should only need to provide a state system
53 * and optionally a backend (default to NULL) and, if required, a filename
54 * (defaults to the analysis'ID)
55 *
56 * @author Geneviève Bastien
57 * @since 3.0
58 */
baa96b1d 59@NonNullByDefault
8a6ff07f 60public abstract class TmfStateSystemAnalysisModule extends TmfAbstractAnalysisModule
5237a931 61 implements ITmfAnalysisModuleWithStateSystems {
8a6ff07f 62
8a6ff07f
GB
63 private static final String EXTENSION = ".ht"; //$NON-NLS-1$
64
09ec275e
AM
65 private final CountDownLatch fInitialized = new CountDownLatch(1);
66
baa96b1d
BH
67 @Nullable private ITmfStateSystemBuilder fStateSystem;
68 @Nullable private ITmfStateProvider fStateProvider;
69 @Nullable private IStateHistoryBackend fHtBackend;
70 @Nullable private ITmfEventRequest fRequest;
5237a931 71
8a6ff07f
GB
72 /**
73 * State system backend types
74 *
75 * @author Geneviève Bastien
76 */
77 protected enum StateSystemBackendType {
78 /** Full history in file */
79 FULL,
80 /** In memory state system */
81 INMEM,
82 /** Null history */
83 NULL,
84 /** State system backed with partial history */
85 PARTIAL
86 }
87
72221aa4
AM
88
89 /**
90 * Retrieve a state system belonging to trace, by passing the ID of the
91 * relevant analysis module.
92 *
93 * This will start the execution of the analysis module, and start the
94 * construction of the state system, if needed.
95 *
96 * @param trace
97 * The trace for which you want the state system
98 * @param moduleId
99 * The ID of the state system analysis module
100 * @return The state system, or null if there was no match
101 * @since 3.1
102 */
103 public static @Nullable ITmfStateSystem getStateSystem(ITmfTrace trace, String moduleId) {
104 TmfStateSystemAnalysisModule module =
105 trace.getAnalysisModuleOfClass(TmfStateSystemAnalysisModule.class, moduleId);
106 if (module != null) {
e1c415b3
BH
107 IStatus status = module.schedule();
108 if (status.isOK()) {
109 module.waitForInitialization();
110 /*
111 * FIXME If we keep a reference to "module", the compiler expects us to
112 * close it. The Analysis Module's API should be reworked to not expose
113 * these objects directly (utility classes instead?)
114 */
115 return module.getStateSystem();
116 }
72221aa4
AM
117 }
118 return null;
119 }
120
8a6ff07f
GB
121 /**
122 * Get the state provider for this analysis module
123 *
124 * @return the state provider
125 */
8a6ff07f
GB
126 protected abstract ITmfStateProvider createStateProvider();
127
128 /**
129 * Get the state system backend type used by this module
130 *
131 * @return The {@link StateSystemBackendType}
132 */
a3b864c0
AM
133 protected StateSystemBackendType getBackendType() {
134 /* Using full history by default, sub-classes can override */
135 return StateSystemBackendType.FULL;
136 }
8a6ff07f
GB
137
138 /**
139 * Get the supplementary file name where to save this state system. The
140 * default is the ID of the analysis followed by the extension.
141 *
142 * @return The supplementary file name
143 */
144 protected String getSsFileName() {
145 return getId() + EXTENSION;
146 }
147
148 /**
baa96b1d
BH
149 * Get the state system generated by this analysis, or null if it is not yet
150 * created.
8a6ff07f
GB
151 *
152 * @return The state system
153 */
baa96b1d 154 @Nullable
8a6ff07f
GB
155 public ITmfStateSystem getStateSystem() {
156 return fStateSystem;
157 }
158
09ec275e
AM
159 /**
160 * Block the calling thread until the analysis module has been initialized.
161 * After this method returns, {@link #getStateSystem()} should not return
162 * null anymore.
163 */
164 public void waitForInitialization() {
165 try {
166 fInitialized.await();
167 } catch (InterruptedException e) {}
168 }
169
baa96b1d
BH
170 // ------------------------------------------------------------------------
171 // TmfAbstractAnalysisModule
172 // ------------------------------------------------------------------------
8a6ff07f 173
baa96b1d
BH
174 @Override
175 protected boolean executeAnalysis(@Nullable final IProgressMonitor monitor) {
176 IProgressMonitor mon = (monitor == null ? new NullProgressMonitor() : monitor);
177 final ITmfStateProvider provider = createStateProvider();
8a6ff07f 178
84a9548a 179 String id = getId();
84a9548a 180
8a6ff07f
GB
181 /* FIXME: State systems should make use of the monitor, to be cancelled */
182 try {
183 /* Get the state system according to backend */
184 StateSystemBackendType backend = getBackendType();
185 String directory;
baa96b1d 186 File htFile;
e1c415b3
BH
187
188 ITmfTrace trace = getTrace();
189 if (trace == null) {
190 // Analysis was cancelled in the meantime
191 fInitialized.countDown();
192 return false;
193 }
8a6ff07f
GB
194 switch (backend) {
195 case FULL:
e1c415b3 196 directory = TmfTraceManager.getSupplementaryFileDir(trace);
baa96b1d 197 htFile = new File(directory + getSsFileName());
84a9548a 198 createFullHistory(id, provider, htFile);
8a6ff07f
GB
199 break;
200 case PARTIAL:
e1c415b3 201 directory = TmfTraceManager.getSupplementaryFileDir(trace);
baa96b1d 202 htFile = new File(directory + getSsFileName());
84a9548a 203 createPartialHistory(id, provider, htFile);
8a6ff07f
GB
204 break;
205 case INMEM:
84a9548a 206 createInMemoryHistory(id, provider);
8a6ff07f
GB
207 break;
208 case NULL:
84a9548a 209 createNullHistory(id, provider);
8a6ff07f
GB
210 break;
211 default:
212 break;
213 }
214 } catch (TmfTraceException e) {
e1c415b3 215 fInitialized.countDown();
8a6ff07f
GB
216 return false;
217 }
baa96b1d 218 return !mon.isCanceled();
8a6ff07f
GB
219 }
220
221 @Override
222 protected void canceling() {
baa96b1d
BH
223 ITmfEventRequest req = fRequest;
224 if ((req != null) && (!req.isCompleted())) {
225 req.cancel();
226 }
227 }
228
a1529f38
AM
229 @Override
230 public void dispose() {
130e6f4e 231 super.dispose();
a1529f38
AM
232 if (fStateSystem != null) {
233 fStateSystem.dispose();
234 }
a1529f38
AM
235 }
236
baa96b1d
BH
237 // ------------------------------------------------------------------------
238 // History creation methods
239 // ------------------------------------------------------------------------
240
241 /*
242 * Load the history file matching the target trace. If the file already
243 * exists, it will be opened directly. If not, it will be created from
244 * scratch.
245 */
84a9548a 246 private void createFullHistory(String id, ITmfStateProvider provider, File htFile) throws TmfTraceException {
baa96b1d
BH
247
248 /* If the target file already exists, do not rebuild it uselessly */
249 // TODO for now we assume it's complete. Might be a good idea to check
250 // at least if its range matches the trace's range.
251
252 if (htFile.exists()) {
253 /* Load an existing history */
254 final int version = provider.getVersion();
255 try {
84a9548a
AM
256 IStateHistoryBackend backend = new HistoryTreeBackend(htFile, version);
257 fHtBackend = backend;
bcec0116 258 fStateSystem = StateSystemFactory.newStateSystem(id, backend, false);
09ec275e 259 fInitialized.countDown();
baa96b1d
BH
260 return;
261 } catch (IOException e) {
262 /*
263 * There was an error opening the existing file. Perhaps it was
264 * corrupted, perhaps it's an old version? We'll just
265 * fall-through and try to build a new one from scratch instead.
266 */
267 }
268 }
269
270 /* Size of the blocking queue to use when building a state history */
271 final int QUEUE_SIZE = 10000;
272
273 try {
84a9548a 274 IStateHistoryBackend backend = new ThreadedHistoryTreeBackend(htFile,
baa96b1d 275 provider.getStartTime(), provider.getVersion(), QUEUE_SIZE);
84a9548a 276 fHtBackend = backend;
bcec0116 277 fStateSystem = StateSystemFactory.newStateSystem(id, backend);
baa96b1d
BH
278 provider.assignTargetStateSystem(fStateSystem);
279 build(provider);
280 } catch (IOException e) {
281 /*
282 * If it fails here however, it means there was a problem writing to
283 * the disk, so throw a real exception this time.
284 */
285 throw new TmfTraceException(e.toString(), e);
286 }
287 }
288
289 /*
290 * Create a new state system backed with a partial history. A partial
291 * history is similar to a "full" one (which you get with
292 * {@link #newFullHistory}), except that the file on disk is much smaller,
293 * but queries are a bit slower.
294 *
295 * Also note that single-queries are implemented using a full-query
296 * underneath, (which are much slower), so this might not be a good fit for
297 * a use case where you have to do lots of single queries.
298 */
84a9548a 299 private void createPartialHistory(String id, ITmfStateProvider provider, File htPartialFile)
baa96b1d
BH
300 throws TmfTraceException {
301 /*
302 * The order of initializations is very tricky (but very important!)
303 * here. We need to follow this pattern:
304 * (1 is done before the call to this method)
305 *
306 * 1- Instantiate realStateProvider
307 * 2- Instantiate realBackend
308 * 3- Instantiate partialBackend, with prereqs:
309 * 3a- Instantiate partialProvider, via realProvider.getNew()
310 * 3b- Instantiate nullBackend (partialSS's backend)
311 * 3c- Instantiate partialSS
312 * 3d- partialProvider.assignSS(partialSS)
313 * 4- Instantiate realSS
314 * 5- partialSS.assignUpstream(realSS)
315 * 6- realProvider.assignSS(realSS)
316 * 7- Call HistoryBuilder(realProvider, realSS, partialBackend) to build the thing.
317 */
318
319 /* Size of the blocking queue to use when building a state history */
320 final int QUEUE_SIZE = 10000;
321
322 final long granularity = 50000;
323
324 /* 2 */
325 IStateHistoryBackend realBackend = null;
326 try {
327 realBackend = new ThreadedHistoryTreeBackend(htPartialFile,
328 provider.getStartTime(), provider.getVersion(), QUEUE_SIZE);
329 } catch (IOException e) {
330 throw new TmfTraceException(e.toString(), e);
331 }
332
333 /* 3a */
334 ITmfStateProvider partialProvider = provider.getNewInstance();
335
336 /* 3b-3c, constructor automatically uses a NullBackend */
337 PartialStateSystem pss = new PartialStateSystem();
338
339 /* 3d */
340 partialProvider.assignTargetStateSystem(pss);
341
342 /* 3 */
343 IStateHistoryBackend partialBackend =
344 new PartialHistoryBackend(partialProvider, pss, realBackend, granularity);
345
346 /* 4 */
bcec0116 347 @SuppressWarnings("restriction")
e894a508
AM
348 org.eclipse.tracecompass.internal.statesystem.core.StateSystem realSS =
349 (org.eclipse.tracecompass.internal.statesystem.core.StateSystem) StateSystemFactory.newStateSystem(id, partialBackend);
baa96b1d
BH
350
351 /* 5 */
352 pss.assignUpstream(realSS);
353
354 /* 6 */
355 provider.assignTargetStateSystem(realSS);
356
357 /* 7 */
358 fHtBackend = partialBackend;
359 fStateSystem = realSS;
360
361 build(provider);
362 }
363
364 /*
365 * Create a new state system using a null history back-end. This means that
366 * no history intervals will be saved anywhere, and as such only
367 * {@link ITmfStateSystem#queryOngoingState} will be available.
368 */
84a9548a
AM
369 private void createNullHistory(String id, ITmfStateProvider provider) {
370 IStateHistoryBackend backend = new NullBackend();
371 fHtBackend = backend;
bcec0116 372 fStateSystem = StateSystemFactory.newStateSystem(id, backend);
baa96b1d
BH
373 provider.assignTargetStateSystem(fStateSystem);
374 build(provider);
375 }
376
377 /*
378 * Create a new state system using in-memory interval storage. This should
379 * only be done for very small state system, and will be naturally limited
380 * to 2^31 intervals.
381 */
84a9548a
AM
382 private void createInMemoryHistory(String id, ITmfStateProvider provider) {
383 IStateHistoryBackend backend = new InMemoryBackend(provider.getStartTime());
384 fHtBackend = backend;
bcec0116 385 fStateSystem = StateSystemFactory.newStateSystem(id, backend);
baa96b1d
BH
386 provider.assignTargetStateSystem(fStateSystem);
387 build(provider);
388 }
389
a1529f38 390 private void disposeProvider(boolean deleteFiles) {
baa96b1d
BH
391 ITmfStateProvider provider = fStateProvider;
392 if (provider != null) {
393 provider.dispose();
394 }
395 if (deleteFiles && (fHtBackend != null)) {
396 fHtBackend.removeFiles();
397 }
398 }
399
400 private void build(ITmfStateProvider provider) {
401 if ((fStateSystem == null) || (fHtBackend == null)) {
402 throw new IllegalArgumentException();
403 }
404
405 ITmfEventRequest request = fRequest;
406 if ((request != null) && (!request.isCompleted())) {
407 request.cancel();
408 }
409
410 request = new StateSystemEventRequest(provider);
411 provider.getTrace().sendRequest(request);
412
8a6ff07f 413 /*
baa96b1d
BH
414 * Only now that we've actually started the build, we'll update the
415 * class fields, so that they become visible for other callers.
8a6ff07f 416 */
baa96b1d
BH
417 fStateProvider = provider;
418 fRequest = request;
419
09ec275e
AM
420 /*
421 * The state system object is now created, we can consider this module
422 * "initialized" (components can retrieve it and start doing queries).
423 */
424 fInitialized.countDown();
425
426 /*
427 * Block the executeAnalysis() construction is complete (so that the
428 * progress monitor displays that it is running).
429 */
baa96b1d 430 try {
09ec275e 431 request.waitForCompletion();
baa96b1d
BH
432 } catch (InterruptedException e) {
433 e.printStackTrace();
434 }
435 }
436
437 private class StateSystemEventRequest extends TmfEventRequest {
438 private final ITmfStateProvider sci;
439 private final ITmfTrace trace;
440
441 public StateSystemEventRequest(ITmfStateProvider sp) {
442 super(sp.getExpectedEventType(),
443 TmfTimeRange.ETERNITY,
444 0,
445 ITmfEventRequest.ALL_DATA,
446 ITmfEventRequest.ExecutionType.BACKGROUND);
447 this.sci = sp;
448
449 // sci.getTrace() will eventually return a @NonNull
450 @SuppressWarnings("null")
451 @NonNull ITmfTrace tr = sci.getTrace();
2d208fb7 452 trace = tr;
baa96b1d 453
baa96b1d
BH
454 }
455
456 @Override
41f3b36b 457 public void handleData(final ITmfEvent event) {
baa96b1d 458 super.handleData(event);
41f3b36b 459 if (event.getTrace() == trace) {
baa96b1d 460 sci.processEvent(event);
2d208fb7
GB
461 } else if (trace instanceof TmfExperiment) {
462 /*
463 * If the request is for an experiment, check if the event is
464 * from one of the child trace
465 */
466 for (ITmfTrace childTrace : ((TmfExperiment) trace).getTraces()) {
467 if (childTrace == event.getTrace()) {
468 sci.processEvent(event);
469 }
470 }
baa96b1d
BH
471 }
472 }
473
474 @Override
475 public void handleSuccess() {
476 super.handleSuccess();
a1529f38 477 disposeProvider(false);
baa96b1d
BH
478 }
479
480 @Override
481 public void handleCancel() {
482 super.handleCancel();
a1529f38 483 disposeProvider(true);
baa96b1d
BH
484 }
485
486 @Override
487 public void handleFailure() {
488 super.handleFailure();
a1529f38 489 disposeProvider(true);
baa96b1d 490 }
8a6ff07f
GB
491 }
492
5237a931
AM
493 // ------------------------------------------------------------------------
494 // ITmfAnalysisModuleWithStateSystems
495 // ------------------------------------------------------------------------
496
497 @Override
baa96b1d
BH
498 @Nullable
499 public ITmfStateSystem getStateSystem(String id) {
5237a931
AM
500 if (id.equals(getId())) {
501 return fStateSystem;
502 }
503 return null;
504 }
505
8a6ff07f 506 @Override
5237a931 507 public Iterable<ITmfStateSystem> getStateSystems() {
baa96b1d
BH
508 @SuppressWarnings("null")
509 @NonNull Iterable<ITmfStateSystem> ret = Collections.singleton((ITmfStateSystem) fStateSystem);
510 return ret;
8a6ff07f
GB
511 }
512}
This page took 0.066333 seconds and 5 git commands to generate.