/* Copyright (c) 2006, Sriram Srinivasan * * You may distribute this software under the terms of the license * specified in the file "License" */ package kilim; import java.util.LinkedList; /** * This is a basic FIFO Executor. It maintains a list of * runnable tasks and hands them out to WorkerThreads. Note that * we don't maintain a list of all tasks, but we will at some point * when we introduce monitoring/watchdog services. * Paused tasks are not GC'd because their PauseReasons ought to be * registered with some other live object. * */ public class Scheduler { public static volatile Scheduler defaultScheduler = null; public static int defaultNumberThreads; public LinkedList<WorkerThread> allThreads = new LinkedList<WorkerThread>(); public RingQueue<WorkerThread> waitingThreads = new RingQueue<WorkerThread>(10); protected volatile boolean shutdown = false; public RingQueue<Task> runnableTasks = new RingQueue<Task>(100); static { String s = System.getProperty("kilim.Scheduler.numThreads"); if (s != null) { try { defaultNumberThreads = Integer.parseInt(s); } catch(Exception e) {} } if (defaultNumberThreads == 0) { defaultNumberThreads = Runtime.getRuntime().availableProcessors(); } } protected Scheduler() {} public Scheduler(int numThreads) { for (int i = 0; i < numThreads; i++) { WorkerThread wt = new WorkerThread(this); allThreads.add(wt); addWaitingThread(wt); wt.start(); } } void addWaitingThread(WorkerThread wt) { synchronized (waitingThreads) { waitingThreads.put(wt); } } WorkerThread getWaitingThread() { synchronized(waitingThreads) { return waitingThreads.get(); } } /** * Schedule a task to run. It is the task's job to ensure that * it is not scheduled when it is runnable. */ public void schedule(Task t) { WorkerThread wt = null; synchronized(this) { assert t.running == true : "Task " + t + " scheduled even though running is false"; runnableTasks.put(t); } wt = getWaitingThread(); if (wt != null) { synchronized(wt) { wt.notify(); //TODO: Move to workerthread, because wait has moved. } } } public void shutdown() { shutdown = true; if (defaultScheduler == this) { defaultScheduler = null; } for (WorkerThread wt: allThreads) { synchronized(wt) { wt.notify(); } } } public boolean isShutdown() { return shutdown; } /** * This is called in the WorkerThread's stack. It transfers a runnable task to the given worker thread's * list of runnables. If the task prefers a different worker thread, then the search continues (after notifying * the other thread that it has a task to execute). * * @return */ void loadNextTask(WorkerThread wt) throws ShutdownException { while (true) { Task t = null; WorkerThread prefThread = null; /////////////// synchronized(this) { if (shutdown) throw new ShutdownException(); t = runnableTasks.get(); if (t == null) { // WorkerThread will add itself to waitingThreads in WorkerThread.getNextTask() break; } else { prefThread = t.preferredResumeThread; if (prefThread == null || prefThread == wt) { wt.addRunnableTask(t); break; // Supplied worker thread has work to do } else { // The task states a preferred thread which is not the supplied worker thread // Enqueue it and continue searching. prefThread.addRunnableTask(t); synchronized(prefThread) { prefThread.notify(); } } } } ///////////// } } public synchronized static Scheduler getDefaultScheduler() { if (defaultScheduler == null) { defaultScheduler = new Scheduler(defaultNumberThreads); } return defaultScheduler; } public static void setDefaultScheduler(Scheduler s) { defaultScheduler = s; } public void dump() { System.out.println(runnableTasks); // for (WorkerThread w: allThreads) { // w.dumpStack(); // } } /* public static boolean isRunnable(Task task) { Scheduler s = defaultScheduler; synchronized (s) { if (s.runnableTasks.contains(task)) { return true; } for (WorkerThread wt: s.allThreads) { if (wt.tasks.contains(task)) return true; } } return false; } */ }