package org.atomnuke.kernel; import java.util.concurrent.TimeUnit; import org.atomnuke.task.manager.TaskManager; import org.atomnuke.util.TimeValue; import org.atomnuke.util.remote.AtomicCancellationRemote; import org.atomnuke.util.remote.CancellationRemote; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author zinic */ public class GenericKernelDelegate implements Runnable { private static final Logger LOG = LoggerFactory.getLogger(GenericKernelDelegate.class); private static final long ONE_MILLISECOND_IN_NANOS = 1000000; private final CancellationRemote crawlerCancellationRemote; private final TaskManager taskManager; private int drainMagnitude; public GenericKernelDelegate(TaskManager taskManager) { this.crawlerCancellationRemote = new AtomicCancellationRemote(); this.taskManager = taskManager; drainMagnitude = 1; } public TaskManager taskManager() { return taskManager; } public CancellationRemote cancellationRemote() { return crawlerCancellationRemote; } private boolean shouldContinue() { final boolean taskManagerStopped = taskManager.state() == TaskManager.State.DESTROYED; final boolean canceled = crawlerCancellationRemote.canceled(); return !canceled && !taskManagerStopped; } @Override public void run() { // Run until canceled while (shouldContinue()) { sleep(tick()); } } private void sleep(TimeValue sleepTill) { final long totalNanoseconds = sleepTill.value(TimeUnit.NANOSECONDS); try { if (totalNanoseconds > ONE_MILLISECOND_IN_NANOS) { millisecondGrainularSleep(totalNanoseconds); } else { microsecondGrainularSleep(totalNanoseconds); } } catch (InterruptedException ie) { LOG.warn("KernelDelegate interrupted. Shutting down right now.", ie); crawlerCancellationRemote.cancel(); } } private void millisecondGrainularSleep(final long totalNanoseconds) throws InterruptedException { final long milliseconds = totalNanoseconds / ONE_MILLISECOND_IN_NANOS; final int nanoseconds = (int) (totalNanoseconds % ONE_MILLISECOND_IN_NANOS); Thread.sleep(milliseconds, nanoseconds); } private void microsecondGrainularSleep(final long totalNanoseconds) { long sleepTime = totalNanoseconds - System.nanoTime(); do { final long then = System.nanoTime(); Thread.yield(); sleepTime -= System.nanoTime() - then; } while (sleepTime > 0); } private TimeValue tick() { // Sleep till the next polling time or for a couple of milliseconds if (taskManager.state() == TaskManager.State.DRAINING) { drainMagnitude += drainMagnitude == 1000 ? 0 : 1; // Compute the time we'll be yielding final long millisecondsToYield = 2 * drainMagnitude; // Log this since yielding is usually a bad thing LOG.error("Execution queue too large to continue polling. Yielding till " + millisecondsToYield + " to allow queue to drain."); return new TimeValue(millisecondsToYield, TimeUnit.MILLISECONDS); } drainMagnitude -= drainMagnitude == 0 ? 1 : 0; return taskManager.scheduleTasks(); } }