From 306586b1c60b0e5bf65d37d32b2a70fd6ee1055b Mon Sep 17 00:00:00 2001 From: Simon Delisle Date: Wed, 5 Jun 2013 16:20:41 -0400 Subject: [PATCH] tmf: Introduce a scheduler for event requests 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 Signed-off-by: Alexandre Montplaisir Reviewed-on: https://git.eclipse.org/r/13882 Reviewed-by: Marc-Andre Laperle Reviewed-by: Matthew Khouzam IP-Clean: Matthew Khouzam Tested-by: Matthew Khouzam --- .../tmf/core/tests/request/AllTests.java | 3 +- .../core/tests/request/TmfSchedulerTest.java | 432 ++++++++++++++++++ .../tmf/core/component/TmfEventThread.java | 37 +- .../tmf/core/request/TmfRequestExecutor.java | 155 ++++++- 4 files changed, 582 insertions(+), 45 deletions(-) create mode 100644 org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/request/TmfSchedulerTest.java diff --git a/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/request/AllTests.java b/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/request/AllTests.java index 44ecb49c27..6fda1f9de9 100644 --- a/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/request/AllTests.java +++ b/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/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 index 0000000000..8573765caa --- /dev/null +++ b/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 fOrderList = Collections.synchronizedList(new ArrayList()); + 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 expectedOrder = new LinkedList(); + 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/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/component/TmfEventThread.java b/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/component/TmfEventThread.java index d18aaba823..62b2d455ce 100644 --- a/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/component/TmfEventThread.java +++ b/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/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/request/TmfRequestExecutor.java b/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/request/TmfRequestExecutor.java index 83097f3ee8..591d7bda85 100644 --- a/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/request/TmfRequestExecutor.java +++ b/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 fHighPriorityTasks = new ArrayBlockingQueue(100); - private final Queue fLowPriorityTasks = new ArrayBlockingQueue(100); + private final Queue fForegroundTasks = new ArrayBlockingQueue(100); + private final Queue fBackgroundTasks = new ArrayBlockingQueue(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 // ------------------------------------------------------------------------ -- 2.34.1