aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Delisle2013-06-05 16:20:41 (EDT)
committerAlexandre Montplaisir2013-07-12 15:35:37 (EDT)
commitcac6bb9eb77a7329e78b43161438475723075127 (patch)
tree120f6b4c0c9b78c935da15586192f0d0c2537fec
parent062888d198fd24e0075251244a05f4aad11903b2 (diff)
downloadorg.eclipse.linuxtools-cac6bb9eb77a7329e78b43161438475723075127.zip
org.eclipse.linuxtools-cac6bb9eb77a7329e78b43161438475723075127.tar.gz
org.eclipse.linuxtools-cac6bb9eb77a7329e78b43161438475723075127.tar.bz2
tmf: Introduce a scheduler for event requestsrefs/changes/82/13882/12
The request scheduler will increase the responsiveness of user actions. It 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. Change-Id: I82dc1da60eaad92c47e015e052a69473119d7a43 Signed-off-by: Simon Delisle <simon.delisle@ericsson.com> Signed-off-by: Alexandre Montplaisir <alexmonthy@voxpopuli.im> Reviewed-on: https://git.eclipse.org/r/13882 Reviewed-by: Marc-Andre Laperle <marc-andre.laperle@ericsson.com> Reviewed-by: Matthew Khouzam <matthew.khouzam@ericsson.com> IP-Clean: Matthew Khouzam <matthew.khouzam@ericsson.com> Tested-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
-rw-r--r--lttng/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/request/AllTests.java3
-rw-r--r--lttng/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/request/TmfSchedulerTest.java432
-rw-r--r--lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/component/TmfEventThread.java37
-rw-r--r--lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/request/TmfRequestExecutor.java155
4 files changed, 582 insertions, 45 deletions
diff --git a/lttng/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/request/AllTests.java b/lttng/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/request/AllTests.java
index 44ecb49..6fda1f9 100644
--- a/lttng/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/request/AllTests.java
+++ b/lttng/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/request/AllTests.java
@@ -25,7 +25,8 @@ import org.junit.runners.Suite;
TmfCoalescedEventRequestTest.class,
TmfDataRequestTest.class,
TmfEventRequestTest.class,
- TmfRequestExecutorTest.class
+ TmfRequestExecutorTest.class,
+ TmfSchedulerTest.class
})
public class AllTests {
diff --git a/lttng/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/request/TmfSchedulerTest.java b/lttng/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/request/TmfSchedulerTest.java
new file mode 100644
index 0000000..8573765
--- /dev/null
+++ b/lttng/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/request/TmfSchedulerTest.java
@@ -0,0 +1,432 @@
+/*******************************************************************************
+ * 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
diff --git a/lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/component/TmfEventThread.java b/lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/component/TmfEventThread.java
index d18aaba..62b2d45 100644
--- a/lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/component/TmfEventThread.java
+++ b/lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/component/TmfEventThread.java
@@ -12,6 +12,8 @@
package org.eclipse.linuxtools.internal.tmf.core.component;
+import java.util.concurrent.CountDownLatch;
+
import org.eclipse.linuxtools.internal.tmf.core.Activator;
import org.eclipse.linuxtools.internal.tmf.core.TmfCoreTracer;
import org.eclipse.linuxtools.tmf.core.component.ITmfDataProvider;
@@ -57,13 +59,10 @@ public class TmfEventThread implements Runnable {
/**
* The thread execution state
*/
- private volatile boolean isPaused = false;
private volatile boolean isCompleted = false;
- /**
- * The synchronization object
- */
- private final Object object = new Object();
+ /** Latch indicating if the thread is currently paused (>0 means paused) */
+ private CountDownLatch pausedLatch = new CountDownLatch(0);
// ------------------------------------------------------------------------
// Constructor
@@ -132,7 +131,14 @@ public class TmfEventThread implements Runnable {
* @return The request execution state
*/
public boolean isRunning() {
- return fRequest.isRunning() && !isPaused;
+ return fRequest.isRunning() && !isPaused();
+ }
+
+ /**
+ * @return The request execution state
+ */
+ public synchronized boolean isPaused(){
+ return (pausedLatch.getCount() > 0);
}
/**
@@ -170,16 +176,7 @@ public class TmfEventThread implements Runnable {
TmfCoreTracer.traceRequest(fRequest, "read first event"); //$NON-NLS-1$
while (event != null && !fProvider.isCompleted(fRequest, event, nbRead)) {
- if (isPaused) {
- try {
- while (isPaused) {
- synchronized (object) {
- object.wait();
- }
- }
- } catch (InterruptedException e) {
- }
- }
+ pausedLatch.await();
TmfCoreTracer.traceEvent(fProvider, fRequest, event);
if (fRequest.getDataType().isInstance(event)) {
@@ -217,7 +214,7 @@ public class TmfEventThread implements Runnable {
* Suspend the thread
*/
public synchronized void suspend() {
- isPaused = true;
+ pausedLatch = new CountDownLatch(1);
TmfCoreTracer.traceRequest(fRequest, "SUSPENDED"); //$NON-NLS-1$
}
@@ -225,10 +222,7 @@ public class TmfEventThread implements Runnable {
* Resume the thread
*/
public synchronized void resume() {
- isPaused = false;
- synchronized (object) {
- object.notifyAll();
- }
+ pausedLatch.countDown();
TmfCoreTracer.traceRequest(fRequest, "RESUMED"); //$NON-NLS-1$
}
@@ -240,5 +234,4 @@ public class TmfEventThread implements Runnable {
fRequest.cancel();
}
}
-
}
diff --git a/lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/request/TmfRequestExecutor.java b/lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/request/TmfRequestExecutor.java
index 83097f3..591d7bd 100644
--- a/lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/request/TmfRequestExecutor.java
+++ b/lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/request/TmfRequestExecutor.java
@@ -9,11 +9,14 @@
* 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;
@@ -24,28 +27,45 @@ import org.eclipse.linuxtools.internal.tmf.core.component.TmfEventThread;
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
@@ -60,6 +80,10 @@ public class TmfRequestExecutor implements Executor {
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);
}
/**
@@ -82,7 +106,7 @@ public class TmfRequestExecutor implements Executor {
*/
@Deprecated
public synchronized int getNbPendingRequests() {
- return fHighPriorityTasks.size() + fLowPriorityTasks.size();
+ return fForegroundTasks.size() + fBackgroundTasks.size();
}
/**
@@ -127,14 +151,24 @@ public class TmfRequestExecutor implements Executor {
// 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();
}
}
@@ -144,14 +178,29 @@ public class TmfRequestExecutor implements Executor {
*/
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();
+ }
+ }
}
}
}
@@ -164,7 +213,10 @@ public class TmfRequestExecutor implements Executor {
fActiveTask.cancel();
}
- while ((fActiveTask = fHighPriorityTasks.poll()) != null) {
+ while ((fActiveTask = fForegroundTasks.poll()) != null) {
+ fActiveTask.cancel();
+ }
+ while ((fActiveTask = fBackgroundTasks.poll()) != null) {
fActiveTask.cancel();
}
@@ -175,6 +227,65 @@ public class TmfRequestExecutor implements Executor {
}
// ------------------------------------------------------------------------
+ // 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
// ------------------------------------------------------------------------