Commit | Line | Data |
---|---|---|
8584dc20 FC |
1 | /******************************************************************************* |
2 | * Copyright (c) 2012 Ericsson | |
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 | * Francois Chouinard - Initial API and implementation | |
11 | *******************************************************************************/ | |
12 | ||
13 | package org.eclipse.linuxtools.internal.tmf.core.request; | |
14 | ||
15 | import java.util.ArrayList; | |
16 | import java.util.List; | |
17 | ||
18 | import org.eclipse.core.runtime.IStatus; | |
19 | import org.eclipse.core.runtime.MultiStatus; | |
20 | import org.eclipse.linuxtools.internal.tmf.core.Activator; | |
21 | import org.eclipse.linuxtools.tmf.core.event.ITmfEvent; | |
22 | import org.eclipse.linuxtools.tmf.core.event.TmfTimeRange; | |
23 | import org.eclipse.linuxtools.tmf.core.request.ITmfRequest; | |
24 | import org.eclipse.linuxtools.tmf.core.request.TmfBlockFilter; | |
25 | import org.eclipse.linuxtools.tmf.core.request.TmfRangeFilter; | |
26 | import org.eclipse.linuxtools.tmf.core.request.TmfRequest; | |
27 | ||
28 | /** | |
29 | * The TMF coalesced request | |
30 | * <p> | |
31 | * Since different TMF components can issue simultaneous requests to their event | |
32 | * provider (e.g. as the result of a user action), it is desirable to coalesce | |
33 | * these requests when possible in order to reduce costly I/O with the back-end. | |
34 | * <p> | |
35 | * The TmfCoalescedRequest acts as a request aggregator. It bundles compatible | |
36 | * requests and is the one dispatched to the event provider. As each event is | |
37 | * read in sequence, it re-distributes them to its sub-requests as appropriate. | |
38 | * <p> | |
39 | * The sub-request compatibility is evaluated based on the following criteria: | |
40 | * <ul> | |
41 | * <li>Request type (pre-emptible or not) | |
42 | * <li>Block ranges (start index + nb requested) overlap or are contiguous | |
43 | * <li>Time ranges overlap or are contiguous | |
44 | * </ul> | |
45 | * | |
46 | * @author Francois Chouinard | |
47 | * @version 1.0 | |
48 | */ | |
49 | public class TmfCoalescedRequest extends TmfRequest { | |
50 | ||
51 | // ------------------------------------------------------------------------ | |
52 | // Attributes | |
53 | // ------------------------------------------------------------------------ | |
54 | ||
55 | /** The list of coalesced requests */ | |
56 | private final List<ITmfRequest> fSubRequests = new ArrayList<ITmfRequest>(); | |
57 | ||
58 | /** The list of coalesced requests */ | |
59 | private int fNbSubRequests; | |
60 | ||
61 | // ------------------------------------------------------------------------ | |
62 | // Constructor | |
63 | // ------------------------------------------------------------------------ | |
64 | ||
65 | /** | |
66 | * Default constructor | |
67 | */ | |
68 | public TmfCoalescedRequest() { | |
69 | this(TmfRequestPriority.NORMAL); | |
70 | } | |
71 | ||
72 | /** | |
73 | * Basic constructor | |
74 | * @param priority the request priority | |
75 | */ | |
76 | public TmfCoalescedRequest(TmfRequestPriority priority) { | |
77 | super(priority); | |
78 | } | |
79 | ||
80 | /** | |
81 | * Create a coalesced request based on the provided request | |
82 | * | |
83 | * @param request the base request | |
84 | */ | |
85 | public TmfCoalescedRequest(ITmfRequest request) { | |
86 | super(request != null ? request.getRequestPriority() : null); | |
87 | ||
88 | // Initialize sub-requests list with the request | |
89 | if (request != null) { | |
90 | fSubRequests.add(request); | |
91 | fNbSubRequests++; | |
92 | request.setParent(this); | |
93 | ||
94 | // Collect the filter values of interestIndex | |
95 | TmfBlockFilter blockFilter = (TmfBlockFilter) request.getEventFilter(TmfBlockFilter.class); | |
96 | long startIndex = blockFilter.getStartIndex(); | |
97 | long nbRequested = blockFilter.getNbRequested(); | |
98 | addEventFilter(new TmfBlockFilter(startIndex, nbRequested)); | |
99 | ||
100 | TmfRangeFilter rangeFilter = (TmfRangeFilter) request.getEventFilter(TmfRangeFilter.class); | |
101 | TmfTimeRange timeRange = rangeFilter.getTimeRange(); | |
102 | addEventFilter(new TmfRangeFilter(timeRange)); | |
103 | } | |
104 | } | |
105 | ||
106 | // ------------------------------------------------------------------------ | |
107 | // Request execution state | |
108 | // ------------------------------------------------------------------------ | |
109 | ||
110 | /* (non-Javadoc) | |
111 | * @see org.eclipse.linuxtools.tmf.core.request.TmfRequest#isCompleted() | |
112 | */ | |
113 | @Override | |
114 | public synchronized boolean isCompleted() { | |
115 | for (ITmfRequest request : fSubRequests) { | |
116 | if (!request.isCompleted()) { | |
117 | return false; | |
118 | } | |
119 | } | |
120 | return super.isCompleted(); | |
121 | } | |
122 | ||
123 | // ------------------------------------------------------------------------ | |
124 | // Request completion status | |
125 | // ------------------------------------------------------------------------ | |
126 | ||
127 | /* (non-Javadoc) | |
128 | * @see org.eclipse.linuxtools.tmf.core.request.TmfRequest#isCancelled() | |
129 | */ | |
130 | @Override | |
131 | public synchronized boolean isCancelled() { | |
132 | for (ITmfRequest request : fSubRequests) { | |
133 | if (!request.isCancelled()) { | |
134 | return false; | |
135 | } | |
136 | } | |
137 | return super.isCancelled(); | |
138 | } | |
139 | ||
140 | // ------------------------------------------------------------------------ | |
141 | // Request operations | |
142 | // ------------------------------------------------------------------------ | |
143 | ||
144 | /* (non-Javadoc) | |
145 | * @see org.eclipse.linuxtools.tmf.core.request.TmfRequest#start() | |
146 | */ | |
147 | @Override | |
148 | public void start() { | |
149 | for (ITmfRequest request : fSubRequests) { | |
150 | if (!request.isCompleted()) { | |
151 | request.start(); | |
152 | } | |
153 | } | |
154 | super.start(); | |
155 | } | |
156 | ||
157 | /* (non-Javadoc) | |
158 | * @see org.eclipse.linuxtools.tmf.core.request.TmfRequest#done() | |
159 | */ | |
160 | @Override | |
161 | public void done() { | |
162 | if (fRequestStatus == null) { | |
163 | fRequestStatus = new MultiStatus(Activator.PLUGIN_ID, IStatus.OK, "OK", null); //$NON-NLS-1$ | |
164 | for (ITmfRequest request : fSubRequests) { | |
165 | if (!request.isCompleted()) { | |
166 | request.done(); | |
167 | } | |
168 | ((MultiStatus) fRequestStatus).add(request.getStatus()); | |
169 | } | |
170 | } | |
171 | super.done(); | |
172 | } | |
173 | ||
174 | /* (non-Javadoc) | |
175 | * @see org.eclipse.linuxtools.tmf.core.request.TmfRequest#fail() | |
176 | */ | |
177 | @Override | |
178 | public void fail() { | |
179 | fRequestStatus = new MultiStatus(Activator.PLUGIN_ID, IStatus.ERROR, "FAIL", null); //$NON-NLS-1$ | |
180 | for (ITmfRequest request : fSubRequests) { | |
181 | if (!request.isCompleted()) { | |
182 | request.fail(); | |
183 | ((MultiStatus) fRequestStatus).add(request.getStatus()); | |
184 | } | |
185 | } | |
186 | super.fail(); | |
187 | } | |
188 | ||
189 | /* (non-Javadoc) | |
190 | * @see org.eclipse.linuxtools.tmf.core.request.TmfRequest#cancel() | |
191 | */ | |
192 | @Override | |
193 | public void cancel() { | |
194 | fRequestStatus = new MultiStatus(Activator.PLUGIN_ID, IStatus.CANCEL, "CANCEL", null); //$NON-NLS-1$ | |
195 | for (ITmfRequest request : fSubRequests) { | |
196 | if (!request.isCompleted()) { | |
197 | request.cancel(); | |
198 | } | |
199 | ((MultiStatus) fRequestStatus).add(request.getStatus()); | |
200 | } | |
201 | super.cancel(); | |
202 | } | |
203 | ||
204 | // ------------------------------------------------------------------------ | |
205 | // Request processing hooks | |
206 | // ------------------------------------------------------------------------ | |
207 | ||
208 | /* (non-Javadoc) | |
209 | * @see org.eclipse.linuxtools.tmf.core.request.TmfRequest#handleEvent(org.eclipse.linuxtools.tmf.core.event.ITmfEvent) | |
210 | */ | |
211 | @Override | |
212 | public synchronized void handleEvent(ITmfEvent event) { | |
213 | super.handleEvent(event); | |
214 | for (ITmfRequest request : fSubRequests) { | |
215 | if (!request.isCompleted() && request.matches(event)) { | |
216 | request.handleEvent(event); | |
217 | } | |
218 | } | |
219 | } | |
220 | ||
221 | /* (non-Javadoc) | |
222 | * @see org.eclipse.linuxtools.tmf.core.request.ITmfRequest#notifyParent(org.eclipse.linuxtools.tmf.core.request.ITmfRequest) | |
223 | */ | |
224 | @Override | |
140b65fa | 225 | public synchronized void notifyParent(ITmfRequest child) { |
8584dc20 FC |
226 | if (--fNbSubRequests <= 0) { |
227 | done(); | |
228 | super.notifyParent(this); | |
229 | } | |
230 | } | |
231 | ||
232 | // ------------------------------------------------------------------------ | |
233 | // Management | |
234 | // ------------------------------------------------------------------------ | |
235 | ||
236 | /** | |
237 | * Add a request to this one. | |
238 | * | |
239 | * @param request The request to add | |
240 | * @return true if the request was successfully coalesced, false otherwise | |
241 | */ | |
242 | public synchronized boolean addRequest(ITmfRequest request) { | |
243 | if (isCompatible(request)) { | |
244 | fSubRequests.add(request); | |
245 | fNbSubRequests++; | |
246 | request.setParent(this); | |
247 | adjustFilters(request); | |
248 | return true; | |
249 | } | |
250 | return false; | |
251 | } | |
252 | ||
253 | /** | |
254 | * @return The list of IDs of the sub-requests | |
255 | */ | |
256 | @SuppressWarnings("nls") | |
257 | public String getSubRequestIds() { | |
258 | StringBuffer result = new StringBuffer("["); | |
259 | for (int i = 0; i < fSubRequests.size(); i++) { | |
260 | if (i != 0) { | |
261 | result.append(","); | |
262 | } | |
263 | result.append(fSubRequests.get(i).getRequestId()); | |
264 | } | |
265 | result.append("]"); | |
266 | return result.toString(); | |
267 | } | |
268 | ||
269 | // ------------------------------------------------------------------------ | |
270 | // Compatibility checks | |
271 | // ------------------------------------------------------------------------ | |
272 | ||
273 | /** | |
274 | * Check if a request is compatible i.e. can be coalesced with the | |
275 | * other sub-requests. | |
276 | * Compatibility is evaluated on the following criteria: | |
277 | * - Request type (pre-emptible or not) | |
278 | * - Block parameters (start index + requested) | |
279 | * - Time range | |
280 | * | |
281 | * @param request The request to evaluate | |
282 | * @return true if the request is compatible, false otherwise | |
283 | */ | |
284 | public boolean isCompatible(ITmfRequest request) { | |
285 | if (request.getRequestPriority() != getRequestPriority()) { | |
286 | return false; | |
287 | } | |
288 | // Check the block range | |
289 | TmfBlockFilter blockFilter = (TmfBlockFilter) request.getEventFilter(TmfBlockFilter.class); | |
290 | if (!isCompatible(blockFilter)) { | |
291 | return false; | |
292 | } | |
293 | // Check the time range | |
294 | TmfRangeFilter rangeFilter = (TmfRangeFilter) request.getEventFilter(TmfRangeFilter.class); | |
295 | if (!isCompatible(rangeFilter)) { | |
296 | return false; | |
297 | } | |
298 | return true; | |
299 | } | |
300 | ||
301 | /** | |
302 | * Check if the filter time range overlaps the coalesced request time range. | |
303 | * The test boils down to a verification of the intersection of the ranges. | |
304 | * | |
305 | * @param filter the time range filter to test | |
306 | * @return true if the time range is compatible; false otherwise | |
307 | */ | |
308 | private boolean isCompatible(TmfRangeFilter filter) { | |
309 | TmfRangeFilter rangeFilter = (TmfRangeFilter) getEventFilter(TmfRangeFilter.class); | |
310 | return rangeFilter.getTimeRange().getIntersection(filter.getTimeRange()) != null; | |
311 | } | |
312 | ||
313 | /** | |
314 | * Check if the filter block overlaps the coalesced request block. | |
315 | * The test boils down to a verification that at least one of the block | |
316 | * boundaries falls within the other block boundaries. | |
317 | * | |
318 | * @param filter the block filter to test | |
319 | * @return true if the time range is compatible; false otherwise | |
320 | */ | |
321 | private boolean isCompatible(TmfBlockFilter filter) { | |
322 | TmfBlockFilter blockFilter = (TmfBlockFilter) getEventFilter(TmfBlockFilter.class); | |
323 | return filter.getStartIndex() - 1 <= (blockFilter.getEndIndex()) && | |
324 | filter.getEndIndex() - 1 >= (blockFilter.getStartIndex()); | |
325 | } | |
326 | ||
327 | // ------------------------------------------------------------------------ | |
328 | // Filter adjustments | |
329 | // ------------------------------------------------------------------------ | |
330 | ||
331 | /** | |
332 | * Adjust the coalesced request filters based on a given request | |
333 | * | |
334 | * @param request the request to consider | |
335 | */ | |
336 | private void adjustFilters(ITmfRequest request) { | |
337 | TmfBlockFilter blockFilter = (TmfBlockFilter) request.getEventFilter(TmfBlockFilter.class); | |
338 | adjustFilter(blockFilter); | |
339 | TmfRangeFilter rangeFilter = (TmfRangeFilter) request.getEventFilter(TmfRangeFilter.class); | |
340 | adjustFilter(rangeFilter); | |
341 | } | |
342 | ||
343 | /** | |
344 | * @param filter the block filter to adjust | |
345 | */ | |
346 | private void adjustFilter(TmfBlockFilter filter) { | |
347 | TmfBlockFilter blockFilter = (TmfBlockFilter) getEventFilter(TmfBlockFilter.class); | |
348 | long startIndex = Math.min(blockFilter.getStartIndex(), filter.getStartIndex()); | |
349 | long endIndex = Math.max(blockFilter.getEndIndex(), filter.getEndIndex()); | |
350 | long nbRequested = endIndex - startIndex; | |
351 | addEventFilter(new TmfBlockFilter(startIndex, nbRequested)); | |
352 | } | |
353 | ||
354 | /** | |
355 | * @param filter the time range filter to adjust | |
356 | */ | |
357 | private void adjustFilter(TmfRangeFilter filter) { | |
358 | TmfRangeFilter rangeFilter = (TmfRangeFilter) getEventFilter(TmfRangeFilter.class); | |
359 | TmfTimeRange timeRange = rangeFilter.getTimeRange().getUnion(filter.getTimeRange()); | |
360 | addEventFilter(new TmfRangeFilter(timeRange)); | |
361 | } | |
362 | ||
363 | // ------------------------------------------------------------------------ | |
364 | // Object | |
365 | // ------------------------------------------------------------------------ | |
366 | ||
367 | /* (non-Javadoc) | |
368 | * @see org.eclipse.linuxtools.tmf.core.request.TmfRequest#hashCode() | |
369 | */ | |
370 | @Override | |
371 | public int hashCode() { | |
372 | final int prime = 31; | |
373 | int result = super.hashCode(); | |
374 | result = prime * result + ((fSubRequests == null) ? 0 : fSubRequests.hashCode()); | |
375 | return result; | |
376 | } | |
377 | ||
378 | /* (non-Javadoc) | |
379 | * @see org.eclipse.linuxtools.tmf.core.request.TmfRequest#equals(java.lang.Object) | |
380 | */ | |
381 | @Override | |
382 | public boolean equals(Object obj) { | |
383 | if (this == obj) { | |
384 | return true; | |
385 | } | |
386 | if (!super.equals(obj)) { | |
387 | return false; | |
388 | } | |
389 | if (!(obj instanceof TmfCoalescedRequest)) { | |
390 | return false; | |
391 | } | |
392 | TmfCoalescedRequest other = (TmfCoalescedRequest) obj; | |
393 | if (fSubRequests == null) { | |
394 | if (other.fSubRequests != null) { | |
395 | return false; | |
396 | } | |
397 | } else if (!fSubRequests.equals(other.fSubRequests)) { | |
398 | return false; | |
399 | } | |
400 | return true; | |
401 | } | |
402 | ||
403 | /* (non-Javadoc) | |
404 | * @see org.eclipse.linuxtools.tmf.core.request.TmfRequest#toString() | |
405 | */ | |
406 | @Override | |
407 | @SuppressWarnings("nls") | |
408 | public String toString() { | |
409 | return "TmfCoalescedRequest [fSubRequests=" + fSubRequests + "]"; | |
410 | } | |
411 | ||
412 | } |