tmf: Introduce a scheduler for event requests
authorSimon Delisle <simon.delisle@ericsson.com>
Wed, 5 Jun 2013 20:20:41 +0000 (16:20 -0400)
committerAlexandre Montplaisir <alexmonthy@voxpopuli.im>
Fri, 12 Jul 2013 19:35:37 +0000 (15:35 -0400)
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>
org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/request/AllTests.java
org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/request/TmfSchedulerTest.java [new file with mode: 0644]
org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/component/TmfEventThread.java
org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/request/TmfRequestExecutor.java

index 44ecb49c270a739a057b3876deb6295210d16b2d..6fda1f9de92af913a7a3677ea79dbbd69e3d9f6b 100644 (file)
@@ -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/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/request/TmfSchedulerTest.java b/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/request/TmfSchedulerTest.java
new file mode 100644 (file)
index 0000000..8573765
--- /dev/null
@@ -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
index d18aaba823ec8fdce0cf8c0e6bcb02099ab38130..62b2d455ceca8b7ae3adf3e3cceb765e6bba96ef 100644 (file)
@@ -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();
         }
     }
-
 }
index 83097f3ee822b49114929323e2f890a24609a01d..591d7bda856133a2266239c1be19cc24299febb7 100644 (file)
@@ -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();
         }
 
@@ -174,6 +226,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
     // ------------------------------------------------------------------------
This page took 0.035379 seconds and 5 git commands to generate.