Commit | Line | Data |
---|---|---|
8c8bf09f | 1 | /******************************************************************************* |
8967c8c0 | 2 | * Copyright (c) 2009, 2014 Ericsson |
0283f7ff | 3 | * |
8c8bf09f ASL |
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 | |
0283f7ff | 8 | * |
8c8bf09f | 9 | * Contributors: |
fd3f1eff AM |
10 | * Francois Chouinard - Initial API and implementation, replace background |
11 | * requests by preemptable requests | |
12 | * Alexandre Montplaisir - Merge with TmfDataProvider | |
8967c8c0 | 13 | * Bernd Hufmann - Add timer based coalescing for background requests |
8c8bf09f ASL |
14 | *******************************************************************************/ |
15 | ||
2bdf0193 | 16 | package org.eclipse.tracecompass.tmf.core.component; |
8c8bf09f | 17 | |
d77f31da BH |
18 | import java.util.ArrayList; |
19 | import java.util.Collections; | |
8967c8c0 | 20 | import java.util.Iterator; |
f45257df | 21 | import java.util.LinkedList; |
fd3f1eff | 22 | import java.util.List; |
8967c8c0 BH |
23 | import java.util.Timer; |
24 | import java.util.TimerTask; | |
fd3f1eff | 25 | |
2bdf0193 AM |
26 | import org.eclipse.tracecompass.internal.tmf.core.TmfCoreTracer; |
27 | import org.eclipse.tracecompass.internal.tmf.core.component.TmfEventThread; | |
28 | import org.eclipse.tracecompass.internal.tmf.core.component.TmfProviderManager; | |
29 | import org.eclipse.tracecompass.internal.tmf.core.request.TmfCoalescedEventRequest; | |
30 | import org.eclipse.tracecompass.internal.tmf.core.request.TmfRequestExecutor; | |
31 | import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; | |
32 | import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest; | |
33 | import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType; | |
34 | import org.eclipse.tracecompass.tmf.core.signal.TmfEndSynchSignal; | |
35 | import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; | |
36 | import org.eclipse.tracecompass.tmf.core.signal.TmfStartSynchSignal; | |
37 | import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; | |
38 | import org.eclipse.tracecompass.tmf.core.trace.ITmfContext; | |
8c8bf09f ASL |
39 | |
40 | /** | |
fd3f1eff AM |
41 | * An abstract base class that implements ITmfEventProvider. |
42 | * <p> | |
43 | * This abstract class implements the housekeeping methods to register/ | |
44 | * de-register the event provider and to handle generically the event requests. | |
45 | * </p> | |
0283f7ff | 46 | * |
8fd82db5 | 47 | * @author Francois Chouinard |
c4767854 | 48 | * @since 3.0 |
8c8bf09f | 49 | */ |
fd3f1eff AM |
50 | public abstract class TmfEventProvider extends TmfComponent implements ITmfEventProvider { |
51 | ||
52 | // ------------------------------------------------------------------------ | |
53 | // Constants | |
54 | // ------------------------------------------------------------------------ | |
55 | ||
c4767854 AM |
56 | /** Default amount of events per request "chunk" |
57 | * @since 3.0 */ | |
fd3f1eff AM |
58 | public static final int DEFAULT_BLOCK_SIZE = 50000; |
59 | ||
8967c8c0 BH |
60 | /** Delay for coalescing background requests (in milli-seconds) */ |
61 | private static final long DELAY = 1000; | |
62 | ||
fd3f1eff AM |
63 | // ------------------------------------------------------------------------ |
64 | // Attributes | |
65 | // ------------------------------------------------------------------------ | |
66 | ||
f45257df AM |
67 | /** List of coalesced requests */ |
68 | private final List<TmfCoalescedEventRequest> fPendingCoalescedRequests = new LinkedList<>(); | |
fd3f1eff | 69 | |
f45257df AM |
70 | /** The type of event handled by this provider */ |
71 | private Class<? extends ITmfEvent> fType; | |
fd3f1eff AM |
72 | |
73 | private final TmfRequestExecutor fExecutor; | |
74 | ||
75 | private final Object fLock = new Object(); | |
76 | ||
77 | private int fSignalDepth = 0; | |
78 | ||
79 | private int fRequestPendingCounter = 0; | |
8c8bf09f | 80 | |
506219d0 | 81 | private Timer fTimer; |
8967c8c0 BH |
82 | |
83 | private boolean fIsTimeout = false; | |
84 | ||
d77f31da BH |
85 | /** |
86 | * The parent event provider. | |
87 | */ | |
88 | private TmfEventProvider fParent = null; | |
89 | /** | |
90 | * The list if children event provider. | |
91 | */ | |
92 | private final List<TmfEventProvider> fChildren = Collections.synchronizedList(new ArrayList<TmfEventProvider>()); | |
93 | ||
00641a97 FC |
94 | // ------------------------------------------------------------------------ |
95 | // Constructors | |
96 | // ------------------------------------------------------------------------ | |
97 | ||
063f0d27 AM |
98 | /** |
99 | * Default constructor | |
100 | */ | |
12c155f5 | 101 | public TmfEventProvider() { |
00641a97 | 102 | super(); |
fd3f1eff | 103 | fExecutor = new TmfRequestExecutor(); |
12c155f5 | 104 | } |
8c8bf09f | 105 | |
063f0d27 | 106 | /** |
f45257df | 107 | * Standard constructor. Instantiate and initialize at the same time. |
063f0d27 AM |
108 | * |
109 | * @param name | |
fd3f1eff | 110 | * Name of the provider |
063f0d27 | 111 | * @param type |
fd3f1eff | 112 | * The type of events that will be handled |
063f0d27 | 113 | */ |
f45257df | 114 | public TmfEventProvider(String name, Class<? extends ITmfEvent> type) { |
fd3f1eff | 115 | this(); |
fd3f1eff | 116 | init(name, type); |
12c155f5 FC |
117 | } |
118 | ||
063f0d27 | 119 | /** |
f45257df | 120 | * Initialize this data provider |
fd3f1eff AM |
121 | * |
122 | * @param name | |
123 | * Name of the provider | |
124 | * @param type | |
125 | * The type of events that will be handled | |
126 | */ | |
f45257df AM |
127 | public void init(String name, Class<? extends ITmfEvent> type) { |
128 | super.init(name); | |
129 | fType = type; | |
130 | fExecutor.init(); | |
131 | ||
132 | fSignalDepth = 0; | |
506219d0 BH |
133 | |
134 | synchronized (fLock) { | |
135 | fTimer = new Timer(); | |
136 | } | |
137 | ||
f45257df | 138 | TmfProviderManager.register(fType, this); |
fd3f1eff AM |
139 | } |
140 | ||
141 | @Override | |
142 | public void dispose() { | |
143 | TmfProviderManager.deregister(fType, this); | |
144 | fExecutor.stop(); | |
506219d0 BH |
145 | synchronized (fLock) { |
146 | if (fTimer != null) { | |
147 | fTimer.cancel(); | |
148 | } | |
149 | fTimer = null; | |
150 | } | |
d77f31da BH |
151 | |
152 | synchronized (fChildren) { | |
153 | for (TmfEventProvider child : fChildren) { | |
154 | child.dispose(); | |
155 | } | |
156 | fChildren.clear(); | |
157 | } | |
fd3f1eff | 158 | super.dispose(); |
12c155f5 FC |
159 | } |
160 | ||
00641a97 | 161 | // ------------------------------------------------------------------------ |
fd3f1eff AM |
162 | // Accessors |
163 | // ------------------------------------------------------------------------ | |
164 | ||
fd3f1eff AM |
165 | /** |
166 | * Get the event type this provider handles | |
167 | * | |
168 | * @return The type of ITmfEvent | |
169 | */ | |
0f89d4ba | 170 | public Class<? extends ITmfEvent> getType() { |
fd3f1eff AM |
171 | return fType; |
172 | } | |
173 | ||
174 | // ------------------------------------------------------------------------ | |
175 | // ITmfRequestHandler | |
00641a97 FC |
176 | // ------------------------------------------------------------------------ |
177 | ||
c4767854 AM |
178 | /** |
179 | * @since 3.0 | |
180 | */ | |
5419a136 | 181 | @Override |
fd3f1eff AM |
182 | public void sendRequest(final ITmfEventRequest request) { |
183 | synchronized (fLock) { | |
5a597798 GB |
184 | |
185 | if (TmfCoreTracer.isRequestTraced()) { | |
186 | TmfCoreTracer.traceRequest(request.getRequestId(), "SENT to provider " + getName()); //$NON-NLS-1$ | |
187 | } | |
188 | ||
8967c8c0 BH |
189 | if (request.getExecType() == ExecutionType.FOREGROUND) { |
190 | if ((fSignalDepth > 0) || (fRequestPendingCounter > 0)) { | |
191 | coalesceEventRequest(request); | |
192 | } else { | |
f45257df | 193 | queueRequest(request); |
8967c8c0 BH |
194 | } |
195 | return; | |
196 | } | |
197 | ||
506219d0 BH |
198 | /* |
199 | * Dispatch request in case timer is not running. | |
200 | */ | |
201 | if (fTimer == null) { | |
202 | queueRequest(request); | |
203 | return; | |
204 | } | |
205 | ||
8967c8c0 BH |
206 | /* |
207 | * For the first background request in the request pending queue | |
208 | * a timer will be started to allow other background requests to | |
209 | * coalesce. | |
210 | */ | |
211 | boolean startTimer = (getNbPendingBackgroundRequests() == 0); | |
212 | coalesceEventRequest(request); | |
213 | if (startTimer) { | |
214 | TimerTask task = new TimerTask() { | |
215 | @Override | |
216 | public void run() { | |
217 | synchronized (fLock) { | |
218 | fIsTimeout = true; | |
219 | fireRequest(); | |
220 | } | |
221 | } | |
222 | }; | |
223 | fTimer.schedule(task, DELAY); | |
fd3f1eff AM |
224 | } |
225 | } | |
226 | } | |
227 | ||
f45257df | 228 | private void fireRequest() { |
fd3f1eff AM |
229 | synchronized (fLock) { |
230 | if (fRequestPendingCounter > 0) { | |
231 | return; | |
232 | } | |
8967c8c0 | 233 | |
fd3f1eff | 234 | if (fPendingCoalescedRequests.size() > 0) { |
8967c8c0 BH |
235 | Iterator<TmfCoalescedEventRequest> iter = fPendingCoalescedRequests.iterator(); |
236 | while (iter.hasNext()) { | |
237 | ExecutionType type = (fIsTimeout ? ExecutionType.BACKGROUND : ExecutionType.FOREGROUND); | |
238 | ITmfEventRequest request = iter.next(); | |
239 | if (type == request.getExecType()) { | |
f45257df | 240 | queueRequest(request); |
8967c8c0 BH |
241 | iter.remove(); |
242 | } | |
fd3f1eff | 243 | } |
fd3f1eff | 244 | } |
5419a136 | 245 | } |
5419a136 AM |
246 | } |
247 | ||
fd3f1eff AM |
248 | /** |
249 | * Increments/decrements the pending requests counters and fires the request | |
250 | * if necessary (counter == 0). Used for coalescing requests across multiple | |
251 | * TmfDataProvider's. | |
252 | * | |
253 | * @param isIncrement | |
254 | * Should we increment (true) or decrement (false) the pending | |
255 | * counter | |
256 | */ | |
5419a136 | 257 | @Override |
fd3f1eff AM |
258 | public void notifyPendingRequest(boolean isIncrement) { |
259 | synchronized (fLock) { | |
260 | if (isIncrement) { | |
8967c8c0 | 261 | fRequestPendingCounter++; |
fd3f1eff AM |
262 | } else { |
263 | if (fRequestPendingCounter > 0) { | |
264 | fRequestPendingCounter--; | |
265 | } | |
266 | ||
267 | // fire request if all pending requests are received | |
268 | if (fRequestPendingCounter == 0) { | |
269 | fireRequest(); | |
270 | } | |
271 | } | |
272 | } | |
273 | } | |
274 | ||
275 | // ------------------------------------------------------------------------ | |
276 | // Coalescing | |
277 | // ------------------------------------------------------------------------ | |
278 | ||
279 | /** | |
280 | * Create a new request from an existing one, and add it to the coalesced | |
281 | * requests | |
282 | * | |
283 | * @param request | |
284 | * The request to copy | |
c4767854 | 285 | * @since 3.0 |
fd3f1eff | 286 | */ |
8967c8c0 BH |
287 | protected void newCoalescedEventRequest(ITmfEventRequest request) { |
288 | synchronized (fLock) { | |
672a642a | 289 | TmfCoalescedEventRequest coalescedRequest = new TmfCoalescedEventRequest( |
fd3f1eff AM |
290 | request.getDataType(), |
291 | request.getRange(), | |
292 | request.getIndex(), | |
293 | request.getNbRequested(), | |
294 | request.getExecType()); | |
295 | coalescedRequest.addRequest(request); | |
5419a136 | 296 | if (TmfCoreTracer.isRequestTraced()) { |
8b56808c GB |
297 | TmfCoreTracer.traceRequest(request.getRequestId(), "COALESCED with " + coalescedRequest.getRequestId()); //$NON-NLS-1$ |
298 | TmfCoreTracer.traceRequest(coalescedRequest.getRequestId(), "now contains " + coalescedRequest.getSubRequestIds()); //$NON-NLS-1$ | |
5419a136 AM |
299 | } |
300 | fPendingCoalescedRequests.add(coalescedRequest); | |
8967c8c0 | 301 | } |
fd3f1eff AM |
302 | } |
303 | ||
304 | /** | |
305 | * Add an existing requests to the list of coalesced ones | |
306 | * | |
307 | * @param request | |
308 | * The request to add to the list | |
c4767854 | 309 | * @since 3.0 |
fd3f1eff AM |
310 | */ |
311 | protected void coalesceEventRequest(ITmfEventRequest request) { | |
312 | synchronized (fLock) { | |
313 | for (TmfCoalescedEventRequest coalescedRequest : fPendingCoalescedRequests) { | |
314 | if (coalescedRequest.isCompatible(request)) { | |
315 | coalescedRequest.addRequest(request); | |
316 | if (TmfCoreTracer.isRequestTraced()) { | |
8b56808c GB |
317 | TmfCoreTracer.traceRequest(request.getRequestId(), "COALESCED with " + coalescedRequest.getRequestId()); //$NON-NLS-1$ |
318 | TmfCoreTracer.traceRequest(coalescedRequest.getRequestId(), "now contains " + coalescedRequest.getSubRequestIds()); //$NON-NLS-1$ | |
fd3f1eff AM |
319 | } |
320 | return; | |
321 | } | |
322 | } | |
323 | newCoalescedEventRequest(request); | |
324 | } | |
325 | } | |
326 | ||
8967c8c0 BH |
327 | /** |
328 | * Gets the number of background requests in pending queue. | |
329 | * | |
330 | * @return the number of background requests in pending queue | |
331 | */ | |
332 | private int getNbPendingBackgroundRequests() { | |
333 | int nbBackgroundRequests = 0; | |
334 | synchronized (fLock) { | |
335 | for (ITmfEventRequest request : fPendingCoalescedRequests) { | |
336 | if (request.getExecType() == ExecutionType.BACKGROUND) { | |
337 | nbBackgroundRequests++; | |
338 | } | |
339 | } | |
340 | } | |
341 | return nbBackgroundRequests; | |
342 | } | |
343 | ||
fd3f1eff AM |
344 | // ------------------------------------------------------------------------ |
345 | // Request processing | |
346 | // ------------------------------------------------------------------------ | |
347 | ||
fd3f1eff AM |
348 | /** |
349 | * Queue a request. | |
350 | * | |
351 | * @param request | |
352 | * The data request | |
c4767854 | 353 | * @since 3.0 |
fd3f1eff AM |
354 | */ |
355 | protected void queueRequest(final ITmfEventRequest request) { | |
356 | ||
357 | if (fExecutor.isShutdown()) { | |
358 | request.cancel(); | |
359 | return; | |
360 | } | |
361 | ||
362 | TmfEventThread thread = new TmfEventThread(this, request); | |
363 | ||
364 | if (TmfCoreTracer.isRequestTraced()) { | |
8b56808c | 365 | TmfCoreTracer.traceRequest(request.getRequestId(), "QUEUED"); //$NON-NLS-1$ |
fd3f1eff AM |
366 | } |
367 | ||
368 | fExecutor.execute(thread); | |
369 | } | |
370 | ||
fd3f1eff AM |
371 | /** |
372 | * Initialize the provider based on the request. The context is provider | |
373 | * specific and will be updated by getNext(). | |
374 | * | |
375 | * @param request | |
376 | * The request | |
377 | * @return An application specific context; null if request can't be | |
378 | * serviced | |
c4767854 | 379 | * @since 3.0 |
fd3f1eff AM |
380 | */ |
381 | public abstract ITmfContext armRequest(ITmfEventRequest request); | |
382 | ||
383 | /** | |
384 | * Checks if the data meets the request completion criteria. | |
385 | * | |
386 | * @param request | |
387 | * The request | |
388 | * @param event | |
389 | * The data to verify | |
390 | * @param nbRead | |
391 | * The number of events read so far | |
392 | * @return true if completion criteria is met | |
c4767854 | 393 | * @since 3.0 |
fd3f1eff AM |
394 | */ |
395 | public boolean isCompleted(ITmfEventRequest request, ITmfEvent event, int nbRead) { | |
396 | boolean requestCompleted = isCompleted2(request, nbRead); | |
397 | if (!requestCompleted) { | |
398 | ITmfTimestamp endTime = request.getRange().getEndTime(); | |
065cc19b | 399 | return event.getTimestamp().compareTo(endTime) > 0; |
fd3f1eff AM |
400 | } |
401 | return requestCompleted; | |
402 | } | |
403 | ||
404 | private static boolean isCompleted2(ITmfEventRequest request,int nbRead) { | |
405 | return request.isCompleted() || nbRead >= request.getNbRequested(); | |
406 | } | |
407 | ||
408 | // ------------------------------------------------------------------------ | |
409 | // Pass-through's to the request executor | |
410 | // ------------------------------------------------------------------------ | |
411 | ||
412 | /** | |
413 | * @return the shutdown state (i.e. if it is accepting new requests) | |
414 | * @since 2.0 | |
415 | */ | |
416 | protected boolean executorIsShutdown() { | |
417 | return fExecutor.isShutdown(); | |
418 | } | |
419 | ||
420 | /** | |
421 | * @return the termination state | |
422 | * @since 2.0 | |
423 | */ | |
424 | protected boolean executorIsTerminated() { | |
425 | return fExecutor.isTerminated(); | |
426 | } | |
427 | ||
428 | // ------------------------------------------------------------------------ | |
429 | // Signal handlers | |
430 | // ------------------------------------------------------------------------ | |
431 | ||
432 | /** | |
433 | * Handler for the start synch signal | |
434 | * | |
435 | * @param signal | |
436 | * Incoming signal | |
437 | */ | |
438 | @TmfSignalHandler | |
439 | public void startSynch(TmfStartSynchSignal signal) { | |
440 | synchronized (fLock) { | |
441 | fSignalDepth++; | |
442 | } | |
443 | } | |
444 | ||
445 | /** | |
446 | * Handler for the end synch signal | |
447 | * | |
448 | * @param signal | |
449 | * Incoming signal | |
450 | */ | |
451 | @TmfSignalHandler | |
452 | public void endSynch(TmfEndSynchSignal signal) { | |
453 | synchronized (fLock) { | |
454 | fSignalDepth--; | |
455 | if (fSignalDepth == 0) { | |
8967c8c0 | 456 | fIsTimeout = false; |
fd3f1eff AM |
457 | fireRequest(); |
458 | } | |
5419a136 AM |
459 | } |
460 | } | |
951d134a | 461 | |
d77f31da BH |
462 | @Override |
463 | public ITmfEventProvider getParent() { | |
464 | synchronized (fLock) { | |
465 | return fParent; | |
466 | } | |
467 | } | |
468 | ||
469 | @Override | |
470 | public void setParent(ITmfEventProvider parent) { | |
471 | if (!(parent instanceof TmfEventProvider)) { | |
472 | throw new IllegalArgumentException(); | |
473 | } | |
474 | ||
475 | synchronized (fLock) { | |
476 | fParent = (TmfEventProvider) parent; | |
477 | } | |
478 | } | |
479 | ||
480 | @Override | |
481 | public List<ITmfEventProvider> getChildren() { | |
482 | synchronized (fChildren) { | |
483 | List<ITmfEventProvider> list = new ArrayList<>(); | |
484 | list.addAll(fChildren); | |
485 | return list; | |
486 | } | |
487 | } | |
488 | ||
489 | @Override | |
490 | public <T extends ITmfEventProvider> List<T> getChildren(Class<T> clazz) { | |
491 | List<T> list = new ArrayList<>(); | |
492 | synchronized (fChildren) { | |
493 | for (TmfEventProvider child : fChildren) { | |
494 | if (clazz.isAssignableFrom(child.getClass())) { | |
495 | list.add(clazz.cast(child)); | |
496 | } | |
497 | } | |
498 | } | |
499 | return list; | |
500 | } | |
501 | ||
502 | @Override | |
503 | public ITmfEventProvider getChild(String name) { | |
504 | synchronized (fChildren) { | |
505 | for (TmfEventProvider child : fChildren) { | |
506 | if (child.getName().equals(name)) { | |
507 | return child; | |
508 | } | |
509 | } | |
510 | } | |
511 | return null; | |
512 | } | |
513 | ||
514 | @SuppressWarnings("null") | |
515 | @Override | |
516 | public ITmfEventProvider getChild(int index) { | |
517 | return fChildren.get(index); | |
518 | } | |
519 | ||
520 | @Override | |
521 | public void addChild(ITmfEventProvider child) { | |
522 | if (!(child instanceof TmfEventProvider)) { | |
523 | throw new IllegalArgumentException(); | |
524 | } | |
525 | fChildren.add((TmfEventProvider)child); | |
526 | } | |
527 | ||
528 | @Override | |
529 | public int getNbChildren() { | |
530 | return fChildren.size(); | |
531 | } | |
8c8bf09f | 532 | } |