--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2013 Ericsson
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Simon Delisle - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.linuxtools.tmf.core.tests.request;
+
+import static org.junit.Assert.*;
+import static org.junit.Assume.assumeTrue;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.linuxtools.tmf.core.ctfadaptor.CtfTmfEvent;
+import org.eclipse.linuxtools.tmf.core.ctfadaptor.CtfTmfTrace;
+import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
+import org.eclipse.linuxtools.tmf.core.exceptions.TmfTraceException;
+import org.eclipse.linuxtools.tmf.core.request.TmfDataRequest;
+import org.eclipse.linuxtools.tmf.core.request.TmfEventRequest;
+import org.eclipse.linuxtools.tmf.core.signal.TmfTimeSynchSignal;
+import org.eclipse.linuxtools.tmf.core.tests.shared.CtfTmfTestTraces;
+import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
+import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimeRange;
+import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test suite for the scheduler.
+ */
+public class TmfSchedulerTest {
+
+ // ------------------------------------------------------------------------
+ // Constants
+ // ------------------------------------------------------------------------
+
+ private static final int TRACE_INDEX = 0;
+ private static final String PATH = CtfTmfTestTraces.getTestTracePath(TRACE_INDEX);
+ private static final int NB_EVENTS_TRACE = 695319;
+ private static final int NB_EVENTS_TIME_RANGE = 155133;
+
+ // ------------------------------------------------------------------------
+ // Attributes
+ // ------------------------------------------------------------------------
+
+ private CtfTmfTrace fixture;
+
+ private long fStartTime;
+ private long fEndTime;
+ private TmfTimeRange fForegroundTimeRange;
+
+ private final List<String> fOrderList = Collections.synchronizedList(new ArrayList<String>());
+ private int fForegroundId = 0;
+ private int fBackgroundId = 0;
+
+ /**
+ * Perform pre-test initialization.
+ *
+ * @throws TmfTraceException
+ * If the test trace is not found
+ */
+ @Before
+ public void setUp() throws TmfTraceException {
+ assumeTrue(CtfTmfTestTraces.tracesExist());
+ fixture = new CtfTmfTrace();
+ fixture.initTrace((IResource) null, PATH, CtfTmfEvent.class);
+ fixture.indexTrace(true);
+ fStartTime = fixture.getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
+ fEndTime = fixture.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
+
+ long foregroundStartTime = fStartTime + ((fEndTime - fStartTime) / 4);
+ long foregroundEndTime = fStartTime + ((fEndTime - fStartTime) / 2);
+ fForegroundTimeRange = new TmfTimeRange(new TmfTimestamp(foregroundStartTime, ITmfTimestamp.NANOSECOND_SCALE, 0), new TmfTimestamp(foregroundEndTime, ITmfTimestamp.NANOSECOND_SCALE, 0));
+ }
+
+ /**
+ * Perform post-test clean-up.
+ */
+ @After
+ public void tearDown() {
+ if (fixture != null) {
+ fixture.dispose();
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Tests cases
+ // ------------------------------------------------------------------------
+
+ /**
+ * Test one background request
+ */
+ @Test
+ public void backgroundRequest() {
+ BackgroundRequest background = new BackgroundRequest(TmfTimeRange.ETERNITY);
+ fixture.sendRequest(background);
+ try {
+ background.waitForCompletion();
+ } catch (InterruptedException e) {
+ fail();
+ }
+ assertEquals(NB_EVENTS_TRACE, background.getNbEvents());
+ }
+
+ /**
+ * Test one foreground request
+ */
+ @Test
+ public void foregroundRequest() {
+ ForegroundRequest foreground = new ForegroundRequest(TmfTimeRange.ETERNITY);
+ fixture.sendRequest(foreground);
+ try {
+ foreground.waitForCompletion();
+ } catch (InterruptedException e) {
+ fail();
+ }
+ assertEquals(NB_EVENTS_TRACE, foreground.getNbEvents());
+ }
+
+ /**
+ * Test one foreground and one background request for the entire trace at
+ * the same time
+ */
+ @Test
+ public void TestMultiRequest1() {
+ BackgroundRequest background = new BackgroundRequest(TmfTimeRange.ETERNITY);
+ ForegroundRequest foreground = new ForegroundRequest(TmfTimeRange.ETERNITY);
+
+ fixture.sendRequest(background);
+ fixture.sendRequest(foreground);
+ try {
+ background.waitForCompletion();
+ foreground.waitForCompletion();
+ } catch (InterruptedException e) {
+ fail();
+ }
+
+ assertEquals(NB_EVENTS_TRACE, background.getNbEvents());
+ assertEquals(NB_EVENTS_TRACE, foreground.getNbEvents());
+ }
+
+ /**
+ * Test one background request for the entire trace and one foreground
+ * request for smaller time range
+ */
+ @Test
+ public void TestMultiRequest2() {
+ BackgroundRequest background2 = new BackgroundRequest(TmfTimeRange.ETERNITY);
+ ForegroundRequest foreground2 = new ForegroundRequest(fForegroundTimeRange);
+
+ fixture.sendRequest(background2);
+ fixture.sendRequest(foreground2);
+ try {
+ background2.waitForCompletion();
+ foreground2.waitForCompletion();
+ } catch (InterruptedException e) {
+ fail();
+ }
+
+ assertEquals(NB_EVENTS_TRACE, background2.getNbEvents());
+ assertEquals(NB_EVENTS_TIME_RANGE, foreground2.getNbEvents());
+ }
+
+ /**
+ * Test two foreground request, one to select a time range and one to select
+ * an event in this time range
+ */
+ @Test
+ public void TestMultiRequest3() {
+ ForegroundRequest foreground3 = new ForegroundRequest(TmfTimeRange.ETERNITY);
+ fixture.sendRequest(foreground3);
+
+ TmfTimeSynchSignal signal3 = new TmfTimeSynchSignal(this, new TmfTimestamp(fForegroundTimeRange.getStartTime()));
+ fixture.broadcast(signal3);
+
+ try {
+ foreground3.waitForCompletion();
+ } catch (InterruptedException e) {
+ fail();
+ }
+
+ assertEquals(NB_EVENTS_TRACE, foreground3.getNbEvents());
+ }
+
+ /**
+ * Test two foreground request, one to select a time range and one to select
+ * an event before this time range
+ */
+ @Test
+ public void TestMultiRequest4() {
+ ForegroundRequest foreground4 = new ForegroundRequest(fForegroundTimeRange);
+ fixture.sendRequest(foreground4);
+ TmfTimeSynchSignal signal4 = new TmfTimeSynchSignal(this, new TmfTimestamp(fStartTime + ((fEndTime - fStartTime) / 8)));
+ fixture.broadcast(signal4);
+
+ try {
+ foreground4.waitForCompletion();
+ } catch (InterruptedException e) {
+ fail();
+ }
+
+ assertEquals(NB_EVENTS_TIME_RANGE, foreground4.getNbEvents());
+ }
+
+ /**
+ * Test two foreground request, one to select a time range and one to select
+ * an event after this time range
+ */
+ @Test
+ public void TestMultiRequest5() {
+ ForegroundRequest foreground5 = new ForegroundRequest(fForegroundTimeRange);
+ fixture.sendRequest(foreground5);
+ TmfTimeSynchSignal signal5 = new TmfTimeSynchSignal(this, new TmfTimestamp(fEndTime - ((fEndTime - fStartTime) / 4)));
+ fixture.broadcast(signal5);
+
+ try {
+ foreground5.waitForCompletion();
+ } catch (InterruptedException e) {
+ fail();
+ }
+
+ assertEquals(NB_EVENTS_TIME_RANGE, foreground5.getNbEvents());
+ }
+
+ /**
+ * Test one background and one foreground request for the entire trace and
+ * one foreground request to select an event
+ */
+ @Test
+ public void TestMultiRequest6() {
+ BackgroundRequest background6 = new BackgroundRequest(TmfTimeRange.ETERNITY);
+ ForegroundRequest foreground6 = new ForegroundRequest(TmfTimeRange.ETERNITY);
+
+ fixture.sendRequest(background6);
+ fixture.sendRequest(foreground6);
+
+ TmfTimeSynchSignal signal6 = new TmfTimeSynchSignal(this, new TmfTimestamp(fStartTime + ((fEndTime - fStartTime) / 8)));
+ fixture.broadcast(signal6);
+
+ try {
+ background6.waitForCompletion();
+ foreground6.waitForCompletion();
+ } catch (InterruptedException e) {
+ fail();
+ }
+
+ assertEquals(NB_EVENTS_TRACE, background6.getNbEvents());
+ assertEquals(NB_EVENTS_TRACE, foreground6.getNbEvents());
+ }
+
+ /**
+ * Four request, two foreground and two background
+ */
+ @Test
+ public void TestMultiRequest7() {
+ ForegroundRequest foreground7 = new ForegroundRequest(TmfTimeRange.ETERNITY);
+ ForegroundRequest foreground8 = new ForegroundRequest(fForegroundTimeRange);
+ BackgroundRequest background7 = new BackgroundRequest(TmfTimeRange.ETERNITY);
+ BackgroundRequest background8 = new BackgroundRequest(TmfTimeRange.ETERNITY);
+ fixture.sendRequest(foreground7);
+ fixture.sendRequest(foreground8);
+ fixture.sendRequest(background7);
+ fixture.sendRequest(background8);
+ try {
+ foreground7.waitForCompletion();
+ foreground8.waitForCompletion();
+ background7.waitForCompletion();
+ background8.waitForCompletion();
+ } catch (InterruptedException e) {
+ fail();
+ }
+ assertEquals(NB_EVENTS_TRACE, foreground7.getNbEvents());
+ assertEquals(NB_EVENTS_TIME_RANGE, foreground8.getNbEvents());
+ assertEquals(NB_EVENTS_TRACE, background7.getNbEvents());
+ assertEquals(NB_EVENTS_TRACE, background8.getNbEvents());
+ }
+
+ /**
+ * One long foreground request and one short foreground request, the short
+ * one should finish first
+ */
+ @Test
+ public void preemptedForegroundRequest() {
+ ForegroundRequest foreground9 = new ForegroundRequest(TmfTimeRange.ETERNITY);
+ TmfTimeRange shortTimeRange = new TmfTimeRange(new TmfTimestamp(fStartTime, ITmfTimestamp.NANOSECOND_SCALE, 0), new TmfTimestamp(fStartTime + ((fEndTime - fStartTime) / 16), ITmfTimestamp.NANOSECOND_SCALE, 0));
+ ForegroundRequest shortForeground = new ForegroundRequest(shortTimeRange);
+ fixture.sendRequest(foreground9);
+ fixture.sendRequest(shortForeground);
+ try {
+ shortForeground.waitForCompletion();
+ } catch (InterruptedException e) {
+ fail();
+ }
+ assertFalse(foreground9.isCompleted());
+ }
+
+ /**
+ * One long background request and one short foreground request, the
+ * foreground request should finish first
+ */
+ @Test
+ public void preemptedBackgroundRequest() {
+ BackgroundRequest background9 = new BackgroundRequest(TmfTimeRange.ETERNITY);
+ ForegroundRequest foreground10 = new ForegroundRequest(fForegroundTimeRange);
+ fixture.sendRequest(background9);
+ fixture.sendRequest(foreground10);
+ try {
+ foreground10.waitForCompletion();
+ } catch (InterruptedException e) {
+ fail();
+ }
+ assertTrue(foreground10.isCompleted());
+ assertFalse(background9.isCompleted());
+ }
+
+ /**
+ * Test if the scheduler is working as expected
+ */
+ @Test
+ public void executionOrder() {
+ List<String> expectedOrder = new LinkedList<String>();
+ expectedOrder.add("FOREGROUND1");
+ expectedOrder.add("FOREGROUND2");
+ expectedOrder.add("FOREGROUND3");
+ expectedOrder.add("FOREGROUND4");
+ expectedOrder.add("BACKGROUND1");
+ expectedOrder.add("FOREGROUND1");
+ expectedOrder.add("FOREGROUND2");
+ expectedOrder.add("FOREGROUND3");
+ expectedOrder.add("FOREGROUND4");
+ expectedOrder.add("BACKGROUND2");
+
+ fOrderList.clear();
+ fForegroundId = 0;
+ fBackgroundId = 0;
+
+ BackgroundRequest background1 = new BackgroundRequest(TmfTimeRange.ETERNITY);
+ BackgroundRequest background2 = new BackgroundRequest(TmfTimeRange.ETERNITY);
+
+ ForegroundRequest foreground1 = new ForegroundRequest(TmfTimeRange.ETERNITY);
+ ForegroundRequest foreground2 = new ForegroundRequest(TmfTimeRange.ETERNITY);
+ ForegroundRequest foreground3 = new ForegroundRequest(TmfTimeRange.ETERNITY);
+ ForegroundRequest foreground4 = new ForegroundRequest(TmfTimeRange.ETERNITY);
+
+ fixture.sendRequest(foreground1);
+ fixture.sendRequest(foreground2);
+ fixture.sendRequest(foreground3);
+ fixture.sendRequest(foreground4);
+ fixture.sendRequest(background1);
+ fixture.sendRequest(background2);
+ try {
+ foreground1.waitForCompletion();
+ foreground2.waitForCompletion();
+ foreground3.waitForCompletion();
+ foreground4.waitForCompletion();
+ background1.waitForCompletion();
+ background2.waitForCompletion();
+ } catch (InterruptedException e) {
+ fail();
+ }
+ assertEquals(expectedOrder, fOrderList.subList(0, expectedOrder.size()));
+ }
+
+ // ------------------------------------------------------------------------
+ // Helper methods
+ // ------------------------------------------------------------------------
+
+ private class BackgroundRequest extends TmfEventRequest {
+ private static final int CHUNK_SIZE = 0;
+ private int nbEvents = 0;
+ private String backgroundName;
+
+ BackgroundRequest(TmfTimeRange timeRange) {
+ super(fixture.getEventType(), timeRange,
+ TmfDataRequest.ALL_DATA,
+ CHUNK_SIZE,
+ ExecutionType.BACKGROUND);
+ backgroundName = getExecType().toString() + ++fBackgroundId;
+ }
+
+ @Override
+ public void handleData(final ITmfEvent event) {
+ super.handleData(event);
+ if (fOrderList.isEmpty() || !fOrderList.get(fOrderList.size() - 1).equals(backgroundName)) {
+ fOrderList.add(backgroundName);
+ }
+ ++nbEvents;
+ }
+
+ public int getNbEvents() {
+ return nbEvents;
+ }
+ }
+
+ private class ForegroundRequest extends TmfEventRequest {
+ private static final int CHUNK_SIZE = 0;
+ private int nbEvents = 0;
+ private String foregroundName;
+
+ ForegroundRequest(TmfTimeRange timeRange) {
+ super(fixture.getEventType(), timeRange,
+ TmfDataRequest.ALL_DATA,
+ CHUNK_SIZE,
+ ExecutionType.FOREGROUND);
+ foregroundName = getExecType().toString() + ++fForegroundId;
+ }
+
+ @Override
+ public void handleData(final ITmfEvent event) {
+ super.handleData(event);
+ if (fOrderList.isEmpty() || !fOrderList.get(fOrderList.size() - 1).equals(foregroundName)) {
+ fOrderList.add(foregroundName);
+ }
+ ++nbEvents;
+ }
+
+ public int getNbEvents() {
+ return nbEvents;
+ }
+ }
+}
\ No newline at end of file
* Contributors:
* Francois Chouinard - Initial API and implementation
* Francois Chouinard - Added support for pre-emption
+ * Simon Delisle - Added scheduler for requests
*******************************************************************************/
package org.eclipse.linuxtools.internal.tmf.core.request;
import java.util.Queue;
+import java.util.Timer;
+import java.util.TimerTask;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import org.eclipse.linuxtools.tmf.core.request.ITmfDataRequest.ExecutionType;
/**
- * A simple, straightforward request executor.
+ * The request scheduler works with 5 slots with a specific time. It has 4 slots
+ * for foreground requests and 1 slot for background requests, and it passes
+ * through all the slots (foreground first and background after).
+ *
+ * Example: if we have one foreground and one background request, the foreground
+ * request will be executed four times more often than the background request.
*
* @author Francois Chouinard
+ * @author Simon Delisle
* @version 1.1
*/
public class TmfRequestExecutor implements Executor {
+ // ------------------------------------------------------------------------
+ // Constants
+ // ------------------------------------------------------------------------
+
+ private static final long REQUEST_TIME = 100;
+ private static final int FOREGROUND_SLOT = 4;
+
// ------------------------------------------------------------------------
// Attributes
// ------------------------------------------------------------------------
// The request executor
- private final ExecutorService fExecutor = Executors.newFixedThreadPool(2);
+ private final ExecutorService fExecutor = Executors.newCachedThreadPool();
private final String fExecutorName;
// The request queues
- private final Queue<TmfEventThread> fHighPriorityTasks = new ArrayBlockingQueue<TmfEventThread>(100);
- private final Queue<TmfEventThread> fLowPriorityTasks = new ArrayBlockingQueue<TmfEventThread>(100);
+ private final Queue<TmfEventThread> fForegroundTasks = new ArrayBlockingQueue<TmfEventThread>(100);
+ private final Queue<TmfEventThread> fBackgroundTasks = new ArrayBlockingQueue<TmfEventThread>(100);
// The tasks
private TmfEventThread fActiveTask;
- private TmfEventThread fSuspendedTask;
+
+ private final Timer fTimer = new Timer();
+ private TimerTask fTimerTask;
+
+ private int fForegroundCycle = 0;
// ------------------------------------------------------------------------
// Constructors
if (TmfCoreTracer.isComponentTraced()) {
TmfCoreTracer.trace(fExecutor + " created"); //$NON-NLS-1$
}
+
+ // Initialize the timer for the schedSwitch
+ fTimerTask = new SchedSwitch();
+ fTimer.schedule(fTimerTask, 0, REQUEST_TIME);
}
/**
*/
@Deprecated
public synchronized int getNbPendingRequests() {
- return fHighPriorityTasks.size() + fLowPriorityTasks.size();
+ return fForegroundTasks.size() + fBackgroundTasks.size();
}
/**
// Add the thread to the appropriate queue
ExecutionType priority = thread.getExecType();
- (priority == ExecutionType.FOREGROUND ? fHighPriorityTasks : fLowPriorityTasks).offer(wrapper);
- // Schedule or preempt as appropriate
- if (fActiveTask == null) {
- scheduleNext();
- } else if (priority == ExecutionType.FOREGROUND && priority != fActiveTask.getExecType()) {
- fActiveTask.getThread().suspend();
- fSuspendedTask = fActiveTask;
+ if (priority == ExecutionType.FOREGROUND) {
+ fForegroundTasks.add(wrapper);
+ } else {
+ fBackgroundTasks.add(wrapper);
+ }
+ }
+
+ /**
+ * Timer task to trigger scheduleNext()
+ */
+ private class SchedSwitch extends TimerTask {
+
+ SchedSwitch() {
+ }
+
+ @Override
+ public void run() {
scheduleNext();
}
}
*/
protected synchronized void scheduleNext() {
if (!isShutdown()) {
- if ((fActiveTask = fHighPriorityTasks.poll()) != null) {
- fExecutor.execute(fActiveTask);
- } else if (fSuspendedTask != null) {
- fActiveTask = fSuspendedTask;
- fSuspendedTask = null;
- fActiveTask.getThread().resume();
- } else if ((fActiveTask = fLowPriorityTasks.poll()) != null) {
- fExecutor.execute(fActiveTask);
+ if (fActiveTask == null) {
+ schedule();
+ } else if (fActiveTask.getExecType() == ExecutionType.FOREGROUND) {
+ if (fActiveTask.getThread().isCompleted()) {
+ schedule();
+ } else {
+ if (hasTasks()) {
+ fActiveTask.getThread().suspend();
+ fForegroundTasks.add(fActiveTask);
+ schedule();
+ }
+ }
+
+ } else if (fActiveTask.getExecType() == ExecutionType.BACKGROUND) {
+ if (fActiveTask.getThread().isCompleted()) {
+ schedule();
+ } else {
+ if (hasTasks()) {
+ fActiveTask.getThread().suspend();
+ fBackgroundTasks.add(fActiveTask);
+ schedule();
+ }
+ }
}
}
}
fActiveTask.cancel();
}
- while ((fActiveTask = fHighPriorityTasks.poll()) != null) {
+ while ((fActiveTask = fForegroundTasks.poll()) != null) {
+ fActiveTask.cancel();
+ }
+ while ((fActiveTask = fBackgroundTasks.poll()) != null) {
fActiveTask.cancel();
}
}
}
+ // ------------------------------------------------------------------------
+ // Helper methods
+ // ------------------------------------------------------------------------
+
+ /**
+ * Determine which type of request (foreground or background) we schedule
+ * next
+ */
+ private void schedule() {
+ if (!fForegroundTasks.isEmpty()) {
+ scheduleNextForeground();
+ } else {
+ scheduleNextBackground();
+ }
+ }
+
+ /**
+ * Schedule the next foreground request
+ */
+ private void scheduleNextForeground() {
+ if (fForegroundCycle < FOREGROUND_SLOT || fBackgroundTasks.isEmpty()) {
+ ++fForegroundCycle;
+ fActiveTask = fForegroundTasks.poll();
+ executefActiveTask();
+ } else {
+ fActiveTask = null;
+ scheduleNextBackground();
+ }
+ }
+
+ /**
+ * Schedule the next background request
+ */
+ private void scheduleNextBackground() {
+ fForegroundCycle = 0;
+ if (!fBackgroundTasks.isEmpty()) {
+ fActiveTask = fBackgroundTasks.poll();
+ executefActiveTask();
+ }
+ }
+
+ /**
+ * Execute or resume the active task
+ */
+ private void executefActiveTask() {
+ if (fActiveTask.getThread().isPaused()) {
+ fActiveTask.getThread().resume();
+ } else {
+ fExecutor.execute(fActiveTask);
+ }
+ }
+
+ /**
+ * Check if the scheduler has tasks
+ */
+ private boolean hasTasks() {
+ return !(fForegroundTasks.isEmpty() && fBackgroundTasks.isEmpty());
+ }
+
// ------------------------------------------------------------------------
// Object
// ------------------------------------------------------------------------