/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.vm.scheduler;
import org.jnode.annotation.Inline;
import org.jnode.annotation.Internal;
import org.jnode.annotation.KernelSpace;
import org.jnode.annotation.MagicPermission;
import org.jnode.annotation.Uninterruptible;
import org.jnode.vm.Unsafe;
import org.jnode.vm.BaseVmArchitecture;
import org.jnode.vm.VmMagic;
import org.jnode.vm.VmStackReader;
import org.jnode.vm.VmSystem;
import org.jnode.vm.facade.VmThreadVisitor;
import org.jnode.vm.facade.VmUtils;
/**
* Thread scheduler. This scheduler is used by all processors in the system, so
* all access to data structures are protected by processor locks.
*
* @author Ewout Prangsma (epr@users.sourceforge.net)
*/
@MagicPermission
public final class VmScheduler {
/**
* Reference to current architecture.
*/
private final BaseVmArchitecture architecture;
/**
* Lock for the allThreadsQueue.
*/
private final ProcessorLock allThreadsLock;
/**
* Queue holding all threads.
*/
private final VmThreadQueue.AllThreadsQueue allThreadsQueue;
/**
* My ready queue.
*/
private final VmThreadQueue.ScheduleQueue readyQueue;
/**
* My sleep queue.
*/
private final VmThreadQueue.SleepQueue sleepQueue;
/**
* Lock used to protect the ready and sleep queue.
*/
private final ProcessorLock queueLock;
/**
* Default constructor.
*/
public VmScheduler(BaseVmArchitecture architecture) {
this.architecture = architecture;
this.allThreadsLock = new ProcessorLock();
this.allThreadsQueue = new VmThreadQueue.AllThreadsQueue("scheduler-all");
this.queueLock = new ProcessorLock();
this.readyQueue = new VmThreadQueue.ScheduleQueue("scheduler-ready");
this.sleepQueue = new VmThreadQueue.SleepQueue("scheduler-sleep");
}
/**
* Call the visitor for all live threads.
*
* @param visitor
*/
@Uninterruptible
final VmThread getThreadById(int id) {
// final SpinLock lock = vm.allThreadsLock;
VmThreadQueueEntry e = this.allThreadsQueue.first;
while (e != null) {
if (e.thread.getId() == id) {
return e.thread;
}
e = e.next;
}
return null;
}
/**
* Register a thread in the list of all live threads.
*
* @param thread
*/
final void registerThread(VmThread thread) {
if (VmUtils.isWritingImage()) {
allThreadsQueue.add(thread, "Vm");
} else {
allThreadsLock.lock();
try {
allThreadsQueue.add(thread, "Vm");
} finally {
allThreadsLock.unlock();
}
}
}
/**
* Remove the given thread from the list of all threads.
*
* @param thread
*/
final void unregisterThread(VmThread thread) {
allThreadsLock.lock();
try {
allThreadsQueue.remove(thread);
//todo recent change, more testing needed
//remove the thread from readyQueue and sleepQueue too
readyQueue.remove(thread);
sleepQueue.remove(thread);
} finally {
allThreadsLock.unlock();
}
}
/**
* Call the visitor for all live threads.
*
* @param visitor
*/
@Internal
public final boolean visitAllThreads(VmThreadVisitor visitor) {
allThreadsLock.lock();
try {
return allThreadsQueue.visit(visitor);
} finally {
allThreadsLock.unlock();
}
}
/**
* Add the given thread to the ready queue to the scheduler and remove it
* from the sleep queue (if it still was on the sleep queue).
*
* @param thread
* @param ignorePriority If true, the thread is always added to the back of the list,
* regarding its priority.
* @param caller
* @throws org.vmmagic.pragma.UninterruptiblePragma
*/
@KernelSpace
@Uninterruptible
final void addToReadyQueue(VmThread thread, boolean ignorePriority,
String caller) {
try {
// Get access to queues
queueLock.lock();
if (thread.isRunning() || thread.isYielding()) {
sleepQueue.remove(thread);
readyQueue.add(thread, ignorePriority, caller);
} else {
Unsafe
.debug("Thread must be in running state to add to ready queue, not ");
Unsafe.debug(thread.getThreadState());
architecture.getStackReader().debugStackTrace();
Unsafe.die("addToReadyQueue");
}
} finally {
// Release access to queues
queueLock.unlock();
}
}
/**
* Add the given thread to the sleep queue to this scheduler.
*
* @param thread
* @throws org.vmmagic.pragma.UninterruptiblePragma
*/
@Uninterruptible
final void addToSleepQueue(VmThread thread) {
try {
// Get access to queues
queueLock.lock();
sleepQueue.add(thread, null);
} finally {
// Release access to queues
queueLock.unlock();
}
}
/**
* Gets the first thread from the ready queue. If such a thread is
* available, it is removed from the ready queue.
*
* @return
*/
@KernelSpace
@Uninterruptible
final VmThread popFirstReadyThread() {
try {
// Get access to queues
queueLock.lock();
final VmThread newThread = readyQueue.first(VmMagic.currentProcessor());
if (newThread != null) {
readyQueue.remove(newThread);
return newThread;
}
return null;
} finally {
// Release access to queues
queueLock.unlock();
}
}
/**
* Gets the first thread from the sleep queue that is ready to be woken up.
* If such a thread is available, it is removed from the sleep queue.
*
* @return
*/
@KernelSpace
@Uninterruptible
final VmThread popFirstSleepingThread() {
try {
// Get access to queues
queueLock.lock();
final VmThread newThread = sleepQueue.first(VmMagic.currentProcessor());
if (newThread != null) {
final long curTime = VmSystem.currentKernelMillis();
if (newThread.canWakeup(curTime)) {
sleepQueue.remove(newThread);
return newThread;
}
}
return null;
} finally {
// Release access to queues
queueLock.unlock();
}
}
/**
* Dump the state of the scheduler to the unsafe debug stream.
*/
@KernelSpace
@Uninterruptible
final void dump() {
try {
// Get access to queues
queueLock.lock();
readyQueue.dump(false, null);
sleepQueue.dump(false, null);
} finally {
// Release access to queues
queueLock.unlock();
}
}
/**
* Lock the queues for access by the current processor.
*/
@Inline
@Uninterruptible
final void lock() {
queueLock.lock();
}
/**
* Unlock the queues.
*/
@Inline
@Uninterruptible
final void unlock() {
queueLock.unlock();
}
/**
* @return The queue containing all ready threads.
*/
@Inline
final VmThreadQueue.ScheduleQueue getReadyQueue() {
return readyQueue;
}
/**
* @return The queue containing all sleeping threads.
*/
@Inline
final VmThreadQueue.SleepQueue getSleepQueue() {
return sleepQueue;
}
/**
* @return The queue containing all threads.
*/
@Inline
final VmThreadQueue.AllThreadsQueue getAllThreadsQueue() {
return allThreadsQueue;
}
/**
* @return The {@link VmStackReader} from the current architecture.
*/
@Inline
final VmStackReader getStackReader() {
return architecture.getStackReader();
}
}