/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Common Public License (CPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/cpl1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.scheduler.greenthreads;
import org.jikesrvm.ArchitectureSpecific;
import org.jikesrvm.VM;
import org.jikesrvm.SubordinateArchitecture.VM_SubArchBootRecord;
import org.jikesrvm.annotations.NoSubArchCompile;
import org.jikesrvm.classloader.VM_TypeReference;
import org.jikesrvm.memorymanagers.mminterface.MM_Constants;
import org.jikesrvm.memorymanagers.mminterface.VM_CollectorThread;
import org.jikesrvm.memorymanagers.mminterface.VM_ConcurrentCollectorThread;
import org.jikesrvm.osr.OSR_ObjectHolder;
import org.jikesrvm.runtime.VM_BootRecord;
import org.jikesrvm.runtime.VM_Entrypoints;
import org.jikesrvm.runtime.VM_Magic;
import static org.jikesrvm.runtime.VM_SysCall.sysCall;
import org.jikesrvm.scheduler.VM_DebuggerThread;
import org.jikesrvm.scheduler.VM_FinalizerThread;
import org.jikesrvm.scheduler.VM_Lock;
import org.jikesrvm.scheduler.VM_ProcessorLock;
import org.jikesrvm.scheduler.VM_Scheduler;
import org.jikesrvm.scheduler.VM_Thread;
import org.vmmagic.pragma.Interruptible;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.Address;
/**
* Global variables used to implement virtual machine thread scheduler.
* - virtual cpus
* - threads
* - queues
* - locks
*/
@Uninterruptible
public final class VM_GreenScheduler extends VM_Scheduler {
/** Index of initial processor in which "VM.boot()" runs. */
public static final int PRIMORDIAL_PROCESSOR_ID = 1;
/** Index of thread in which "VM.boot()" runs */
// A processors id is its index in the processors array & a threads
// id is its index in the threads array. id's start at 1, so that
// id 0 can be used in locking to represent an unheld lock
/**
* Maximum number of VM_Processor's that we can support.
*/
public static final int MAX_PROCESSORS = 12; // allow processors = 1 to 12
/**
* Maximum number of SubArch Processors that we can support.
*/
public static final int MAX_SUBARCH_PROCESSORS = 12; // allow processors = 1 to 12
/** Type of processor */
private static final VM_TypeReference greenProcessorType =
VM_TypeReference.findOrCreate(VM_GreenProcessor.class);
/** Type of subarch processor */
private static final VM_TypeReference greenSubArchProcessorType =
VM_TypeReference.findOrCreate(VM_GreenSubArchProcessor.class);
/** Type of thread */
private static final VM_TypeReference greenThreadType =
VM_TypeReference.findOrCreate(VM_GreenThread.class);
/** Maximum number of VM_Thread's that we can support. */
public static final int LOG_MAX_THREADS = 14;
public static final int MAX_THREADS = 1 << LOG_MAX_THREADS;
/** Flag for controlling virtual-to-physical processor binding. */
public static final int NO_CPU_AFFINITY = -1;
/** Scheduling quantum in milliseconds: interruptQuantum * interruptQuantumMultiplier */
public static int schedulingQuantum = 10;
// Virtual cpu's.
//
/**
* Physical cpu to which first virtual processor is bound (remainder are bound
* sequentially)
*/
public static int cpuAffinity = NO_CPU_AFFINITY;
/** Total number of virtual processors to be used */
public static int numProcessors = 1;
/** Array of all virtual processors (slot 0 always empty) */
public static VM_GreenProcessor[] processors;
/** Have all processors completed initialization? */
public static boolean allProcessorsInitialized;
/** Total number of subArch processors to be used */
public static int numSubArchProcessors = 0;
/** Array of all subArch processors (slot 0 always empty) */
public static VM_GreenSubArchProcessor[] subArchProcessors;
/** Have all SubArchprocessors completed initialization? */
public static boolean allSubArchProcessorsInitialized;
// Thread execution.
//
/** threads waiting to wake up from a sleep() */
static final VM_ThreadProxyWakeupQueue wakeupQueue = new VM_ThreadProxyWakeupQueue();
/** mutex controlling access to wake up queue */
static final VM_ProcessorLock wakeupMutex = new VM_ProcessorLock();
/** thread waiting to service debugging requests */
public static final VM_GreenThreadQueue debuggerQueue = new VM_GreenThreadQueue();
public static final VM_ProcessorLock debuggerMutex = new VM_ProcessorLock();
/** collector threads waiting to be resumed */
public static final VM_GreenThreadQueue collectorQueue = new VM_GreenThreadQueue();
public static final VM_ProcessorLock collectorMutex = new VM_ProcessorLock();
/** Concurrent worker threads wait here when idle */
public static final VM_GreenThreadQueue concurrentCollectorQueue = new VM_GreenThreadQueue();
public static final VM_ProcessorLock concurrentCollectorMutex = new VM_ProcessorLock();
/** Finalizer thread waits here when idle */
public static final VM_GreenThreadQueue finalizerQueue = new VM_GreenThreadQueue();
public static final VM_ProcessorLock finalizerMutex = new VM_ProcessorLock();
/** Collectors wait here while waiting for flushes */
public static final VM_GreenThreadQueue flushMutatorContextsQueue = new VM_GreenThreadQueue();
public static final VM_ProcessorLock flushMutatorContextsMutex = new VM_ProcessorLock();
public static int flushedMutatorCount = 0;
/** How many extra procs (not counting primordial) ? */
private static int NUM_EXTRA_PROCS = 0;
/**
* Initialize boot image.
*/
@Override
@Interruptible
@NoSubArchCompile
protected void initInternal() {
threadAllocationIndex = PRIMORDIAL_THREAD_INDEX;
// Enable us to dump a Java Stack from the C trap handler to aid in debugging things that
// show up as recursive use of hardware exception registers (eg the long-standing lisp bug)
VM_BootRecord.the_boot_record.dumpStackAndDieOffset = VM_Entrypoints.dumpStackAndDieMethod.getOffset();
// allocate initial processor list
//
// first slot unused, then primordial, then extra
processors = new VM_GreenProcessor[2 + NUM_EXTRA_PROCS];
processors[PRIMORDIAL_PROCESSOR_ID] = new VM_GreenProcessor(PRIMORDIAL_PROCESSOR_ID);
for (int i = 1; i <= NUM_EXTRA_PROCS; i++) {
processors[PRIMORDIAL_PROCESSOR_ID + i] = new VM_GreenProcessor(PRIMORDIAL_PROCESSOR_ID + i);
}
// allocate lock structures
//
VM_Lock.init();
}
/**
* Begin multi-threaded vm operation.
*/
/** Scheduler specific boot up */
@Interruptible
@Override
@NoSubArchCompile
protected void bootInternal() {
if (VM.VerifyAssertions) VM._assert(1 <= numProcessors && numProcessors <= MAX_PROCESSORS);
if (VM.TraceThreads) {
trace("VM_Scheduler.boot", "numProcessors =", numProcessors);
}
// Create a VM_Processor object for each virtual cpu that we'll be running.
// Note that the VM_Processor object for the primordial processor
// (the virtual cpu in whose context we are currently running)
// was already created in the boot image by init(), above.
//
VM_GreenProcessor[] origProcs = processors;
processors = new VM_GreenProcessor[1 + numProcessors];
for (int i = PRIMORDIAL_PROCESSOR_ID; i <= numProcessors; i++) {
VM_GreenProcessor p = (i < origProcs.length) ? origProcs[i] : null;
if (p == null) {
processors[i] = new VM_GreenProcessor(i);
} else {
processors[i] = p;
if (VM.BuildForIA32) {
// TODO: the JTOC doesn't move so this field is redundant
p.jtoc = VM_Magic.getJTOC(); // only needed for EXTRA_PROCS
}
}
}
// Create one one idle thread per processor.
//
for (int i = 0; i < numProcessors; ++i) {
int pid = i + 1;
VM_GreenThread t = new VM_IdleThread(processors[pid], pid != PRIMORDIAL_PROCESSOR_ID);
processors[pid].idleQueue.enqueue(t);
}
// JNI support
terminated = false;
// the one we're running on
processors[PRIMORDIAL_PROCESSOR_ID].isInitialized = true;
// Create virtual cpu's.
//
sysCall.sysCreateThreadSpecificDataKeys();
if (!VM.withoutInterceptBlockingSystemCalls) {
/// We now insist on this happening, by using LD_PRELOAD on platforms
/// that support it. Do it here for backup.
// Enable spoofing of blocking native select calls
System.loadLibrary("syswrap");
}
sysCall.sysInitializeStartupLocks(numProcessors);
if (cpuAffinity != NO_CPU_AFFINITY) {
sysCall.sysVirtualProcessorBind(cpuAffinity + PRIMORDIAL_PROCESSOR_ID - 1); // bind it to a physical cpu
}
for (int i = PRIMORDIAL_PROCESSOR_ID; ++i <= numProcessors;) {
// create VM_Thread for virtual cpu to execute
//
VM_GreenThread target = processors[i].idleQueue.dequeue();
// Create a virtual cpu and wait for execution to enter the target's
// code/stack.
// This is done with GC disabled to ensure that the garbage collector
// doesn't move code or stack before the C startoff function has a
// chance to transfer control into the VM image.
//
if (VM.TraceThreads) {
trace("VM_Scheduler.boot", "starting processor id", i);
}
processors[i].activeThread = target;
processors[i].activeThreadStackLimit = target.stackLimit;
target.registerThread(); // let scheduler know that thread is active.
if (VM.BuildForPowerPC) {
// NOTE: It is critical that we acquire the tocPointer explicitly
// before we start the SysCall sequence. This prevents
// the opt compiler from generating code that passes the AIX
// sys toc instead of the RVM jtoc. --dave
Address toc = VM_Magic.getTocPointer();
sysCall.sysVirtualProcessorCreate(toc,
VM_Magic.objectAsAddress(processors[i]),
VM_Magic.localAddressAsAddress(target.contextRegisters.ip),
VM_Magic.localAddressAsAddress(target.contextRegisters.getInnermostFramePointer()));
if (cpuAffinity != NO_CPU_AFFINITY) {
sysCall.sysVirtualProcessorBind(cpuAffinity + i - 1); // bind it to a physical cpu
}
} else if (VM.BuildForIA32) {
sysCall.sysVirtualProcessorCreate(VM_Magic.getTocPointer(),
VM_Magic.objectAsAddress(processors[i]),
VM_Magic.localAddressAsAddress(target.contextRegisters.ip),
VM_Magic.localAddressAsAddress(target.contextRegisters.getInnermostFramePointer()));
} else if (VM.VerifyAssertions) {
VM._assert(VM.NOT_REACHED);
}
}
// wait for everybody to start up
//
sysCall.sysWaitForVirtualProcessorInitialization();
allProcessorsInitialized = true;
// for (int i = PRIMORDIAL_PROCESSOR_ID; i <= numProcessors; ++i)
// processors[i].enableThreadSwitching();
VM_GreenProcessor.getCurrentProcessor().enableThreadSwitching();
// Start interrupt driven timeslicer to improve threading fairness and responsiveness.
//
schedulingQuantum = VM.interruptQuantum * VM.schedulingMultiplier;
if (VM.TraceThreads) {
VM.sysWrite(" schedulingQuantum " + schedulingQuantum);
VM.sysWrite(" = VM.interruptQuantum " + VM.interruptQuantum);
VM.sysWrite(" * VM.schedulingMultiplier " + VM.schedulingMultiplier);
VM.sysWriteln();
}
sysCall.sysVirtualProcessorEnableTimeSlicing(VM.interruptQuantum);
// Allow virtual cpus to commence feeding off the work queues.
//
sysCall.sysWaitForMultithreadingStart();
if (VM.BuildForAdaptiveSystem) {
OSR_ObjectHolder.boot();
}
// Start collector threads on each VM_Processor.
for (int i = 0; i < numProcessors; ++i) {
VM_GreenThread t = VM_CollectorThread.createActiveCollectorThread(processors[1 + i]);
t.start(processors[1 + i].readyQueue);
}
if (MM_Constants.NEEDS_CONCURRENT_WORKERS) {
// Start concurrent collector threads on each VM_Processor.
for (int i = 0; i < numProcessors; ++i) {
VM_GreenThread t = VM_ConcurrentCollectorThread.createConcurrentCollectorThread(processors[1 + i]);
t.start(processors[1 + i].readyQueue);
}
}
// Start the G.C. system.
// Create the VM_FinalizerThread
VM_FinalizerThread tt = new VM_FinalizerThread();
tt.makeDaemon(true);
tt.start();
// Store VM_Processor in pthread
sysCall.sysStashVmProcessorInPthread(VM_GreenProcessor.getCurrentProcessor());
}
/**
* Begin multi-threaded vm operation.
*/
/** Scheduler specific boot up */
@Interruptible
@NoSubArchCompile
public static void bootSubArch() {
// wait for C part of subarch initiation to complete (should be done by now anyway)
while (!VM_SubArchBootRecord.isSubArchStarted()) {
VM.sysWriteln("Waiting for SubArch to start");
yield(); // TODO - does this do any good?
}
// Get the number of subarch processors
numSubArchProcessors = VM_SubArchBootRecord.getNumSubArchProcs();
if (VM.VerifyAssertions) VM._assert(numSubArchProcessors <= MAX_SUBARCH_PROCESSORS);
// Create a VM_SubArchProcessor object for each subarch proc.
subArchProcessors = new VM_GreenSubArchProcessor[1 + numSubArchProcessors];
for (int i = PRIMORDIAL_PROCESSOR_ID; i <= numSubArchProcessors; i++) {
subArchProcessors[i] = new VM_GreenSubArchProcessor(i);
}
// Create one one idle thread per processor.
//
for (int i = 0; i < numSubArchProcessors; ++i) {
int pid = i + 1;
VM_GreenThread t = new VM_SubArchIdleThread(subArchProcessors[pid]);
subArchProcessors[pid].idleQueue.enqueue(t);
}
for (int i = PRIMORDIAL_PROCESSOR_ID; ++i <= numSubArchProcessors;) {
// create VM_Thread for virtual cpu to execute
//
VM_GreenThread target = subArchProcessors[i].idleQueue.dequeue();
// Create a virtual cpu and wait for execution to enter the target's
// code/stack.
// This is done with GC disabled to ensure that the garbage collector
// doesn't move code or stack before the C startoff function has a
// chance to transfer control into the VM image.
//
if (VM.TraceThreads) {
trace("VM_Scheduler.boot", "starting subarch processor id", i);
}
subArchProcessors[i].activeThread = target;
subArchProcessors[i].activeThreadStackLimit = target.stackLimit;
target.registerThread(); // let scheduler know that thread is active.
}
allSubArchProcessorsInitialized = true;
// TODO - Start collector threads on each subarch VM_Processor.
}
/**
* Terminate all the pthreads that belong to the VM
* This path is used when the VM is taken down by an external pthread via
* the JNI call DestroyJavaVM. All pthreads in the VM must eventually reach this
* method from VM_Thread.terminate() for the termination to proceed and for control
* to return to the pthread that calls DestroyJavaVM
* Going by the order in processor[], the pthread for each processor will join with
* the next one, and the external pthread calling DestroyJavaVM will join with the
* main pthread of the VM (see libjni.C)
*
* Note: the NativeIdleThread's don't need to be terminated since they don't have
* their own pthread; they run on the external pthreads that had called CreateJavaVM
* or AttachCurrentThread.
*/
@NoSubArchCompile
public static void processorExit(int rc) {
// trace("VM_Scheduler", ("Exiting with " + numProcessors + " pthreads."));
// set flag to get all idle threads to exit to VM_Thread.terminate()
terminated = true;
// TODO:
// Get the collector to free system memory: no more allocation beyond this point
// Terminate the pthread: each processor waits for the next one
// find the pthread to wait for
VM_GreenProcessor myVP = VM_GreenProcessor.getCurrentProcessor();
VM_GreenProcessor VPtoWaitFor = null;
for (int i = 1; i < numProcessors; i++) {
if (processors[i] == myVP) {
VPtoWaitFor = processors[i + 1];
break;
}
}
// each join with the expected pthread
if (VPtoWaitFor != null) {
sysCall.sysPthreadJoin(VPtoWaitFor.pthread_id);
}
// then exit myself with pthread_exit
sysCall.sysPthreadExit();
// does not return
if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
}
/**
* Get the current executing thread on this VM_Processor
*/
public static VM_GreenThread getCurrentThread() {
return (VM_GreenThread)VM_GreenProcessor.getCurrentProcessor().activeThread;
}
/**
* Number of available processors
* @see Runtime#availableProcessors()
*/
@Override
protected int availableProcessorsInternal() {
return numProcessors;
}
/**
* Schedule concurrent worker threads that are not already running.
* @see org.jikesrvm.mm.mmtk.Collection
*/
@Override
@NoSubArchCompile
protected void scheduleConcurrentCollectorThreadsInternal() {
concurrentCollectorMutex.lock("scheduling concurrent collector threads");
while (!concurrentCollectorQueue.isEmpty()) {
concurrentCollectorQueue.dequeue().schedule();
}
concurrentCollectorMutex.unlock();
}
/**
* Schedule the finalizer thread if its not already running
* @see org.jikesrvm.mm.mmtk.Collection
*/
@Override
@NoSubArchCompile
protected void scheduleFinalizerInternal() {
boolean alreadyScheduled = finalizerQueue.isEmpty();
if (!alreadyScheduled) {
VM_GreenThread t = finalizerQueue.dequeue();
VM_GreenProcessor.getCurrentProcessor().scheduleThread(t);
}
}
/**
* Request all mutators flush their context.
* @see org.jikesrvm.mm.mmtk.Collection
*/
@Override
@NoSubArchCompile
protected void requestMutatorFlushInternal() {
flushMutatorContextsMutex.lock("requesting mutator flush");
flushedMutatorCount = 0;
for(int i=0; i < numProcessors; i++) {
processors[i+1].flushRequested = true;
}
VM_GreenScheduler.getCurrentThread().yield(flushMutatorContextsQueue, flushMutatorContextsMutex);
}
/**
* Number of VM_Processors
*/
@Override
protected int getNumberOfProcessorsInternal() {
return numProcessors;
}
/**
* Returns if the VM is ready for a garbage collection.
*
* @return True if the RVM is ready for GC, false otherwise.
*/
@Override
@NoSubArchCompile
public boolean gcEnabledInternal() {
/* This test is based upon a review of the code and trial-and-error */
return VM_GreenProcessor.getCurrentProcessor().threadSwitchingEnabled() &&
allProcessorsInitialized;
}
/**
* Dump state of virtual machine.
*/
@Override
@NoSubArchCompile
public void dumpVirtualMachineInternal() {
VM_GreenProcessor processor;
VM.sysWrite("\n-- Processors --\n");
for (int i = 1; i <= numProcessors; ++i) {
processor = processors[i];
processor.dumpProcessorState();
}
// system queues
VM.sysWrite("\n-- System Queues -- \n");
VM.sysWrite(" wakeupQueue: ");
wakeupQueue.dump();
VM.sysWrite(" debuggerQueue: ");
debuggerQueue.dump();
VM.sysWrite(" collectorQueue: ");
collectorQueue.dump();
VM.sysWrite(" finalizerQueue: ");
finalizerQueue.dump();
VM.sysWrite("\n-- Threads --\n");
for (int i = 1; i < threads.length; ++i) {
if (threads[i] != null) {
threads[i].dumpWithPadding(30);
VM.sysWrite("\n");
}
}
VM.sysWrite("\n");
VM.sysWrite("\n-- Locks available --\n");
for (int i = PRIMORDIAL_PROCESSOR_ID; i <= numProcessors; ++i) {
processor = processors[i];
processor.dumpLocks();
}
VM.sysWrite("\n");
VM.sysWrite("\n-- Locks in use --\n");
VM_Lock.dumpLocks();
VM.sysWriteln("Dumping stack of active thread\n");
dumpStack();
VM.sysWriteln("Attempting to dump the stack of all other live threads");
VM.sysWriteln("This is somewhat risky since if the thread is running we're going to be quite confused");
VM_GreenProcessor.getCurrentProcessor().disableThreadSwitching("disabled by scheduler to dump stack");
for (int i = 1; i < threads.length; ++i) {
VM_Thread thr = threads[i];
if (thr != null && thr != VM_Scheduler.getCurrentThread() && thr.isAlive()) {
thr.dump();
if (thr.contextRegisters != null)
dumpStack(thr.contextRegisters.getInnermostFramePointer());
}
}
VM_GreenProcessor.getCurrentProcessor().enableThreadSwitching();
}
/** Start the debugger thread */
@Override
@Interruptible
@NoSubArchCompile
protected void startDebuggerThreadInternal() {
// Create one debugger thread.
VM_GreenThread t = new VM_DebuggerThread();
t.start(VM_GreenScheduler.debuggerQueue);
}
/** Scheduler specific sysExit shutdown */
@Override
@Interruptible
@NoSubArchCompile
protected void sysExitInternal() {
VM_Wait.disableIoWait(); // we can't depend on thread switching being enabled
}
//---------------------------//
// Low level output locking. //
//---------------------------//
@Override
@NoSubArchCompile
protected void lockOutputInternal() {
if (VM_GreenScheduler.numProcessors == 1) return;
VM_GreenProcessor.getCurrentProcessor().disableThreadSwitching("disabled by scheduler to lock output");
do {
int processorId = VM_Magic.prepareInt(VM_Magic.getJTOC(), VM_Entrypoints.outputLockField.getOffset());
if (processorId != 0) {
// expect 0 but got another processor's ID
continue;
}
// Attempt atomic swap of outputLock to out processor ID
if(!VM_Magic.attemptInt(VM_Magic.getJTOC(),
VM_Entrypoints.outputLockField.getOffset(),
0, VM_GreenProcessor.getCurrentProcessorId())) {
continue;
}
} while (false);
VM_Magic.isync(); // TODO!! is this really necessary?
}
@Override
@NoSubArchCompile
protected void unlockOutputInternal() {
if (VM_GreenScheduler.numProcessors == 1) return;
VM_Magic.sync(); // TODO!! is this really necessary?
if (true) {
outputLock = 0; // TODO!! this ought to work, but doesn't?
} else {
do {
int processorId = VM_Magic.prepareInt(VM_Magic.getJTOC(), VM_Entrypoints.outputLockField.getOffset());
if (VM.VerifyAssertions && processorId != VM_GreenProcessor.getCurrentProcessorId()) {
VM.sysExit(VM.EXIT_STATUS_SYSFAIL);
}
if (VM_Magic.attemptInt(VM_Magic.getJTOC(), VM_Entrypoints.outputLockField.getOffset(), processorId, 0)) {
break;
}
} while (true);
}
VM_GreenProcessor.getCurrentProcessor().enableThreadSwitching();
}
/**
* Give a string of information on how a thread is set to be scheduled
*/
@Interruptible
@NoSubArchCompile
static String getThreadState(VM_GreenThread t) {
// scan per-processor queues
//
for (int i = 0; i < processors.length; ++i) {
VM_GreenProcessor p = processors[i];
if (p == null) continue;
if (p.transferQueue.contains(t)) return "runnable (incoming) on processor " + i;
if (p.readyQueue.contains(t)) return "runnable on processor " + i;
if (p.ioQueue.contains(t)) return "waitingForIO (" + p.ioQueue.getWaitDescription(t) + ") on processor " + i;
if (p.subArchQueue.contains(t)) return "running on SubArch Processor status - (" + p.subArchQueue.getWaitDescription(t) + ")";
if (p.processWaitQueue.contains(t)) {
return "waitingForProcess (" + p.processWaitQueue.getWaitDescription(t) + ") on processor " + i;
}
if (p.idleQueue.contains(t)) return "waitingForIdleWork on processor " + i;
}
// scan global queues
//
if (wakeupQueue.contains(t)) return "sleeping";
if (debuggerQueue.contains(t)) return "waitingForDebuggerWork";
if (collectorQueue.contains(t)) return "waitingForCollectorWork";
String lockState = VM_Lock.getThreadState(t);
if (lockState != null) {
return lockState;
}
// not in any queue
//
for (int i = 0; i < processors.length; ++i) {
VM_GreenProcessor p = processors[i];
if (p == null) continue;
if (p.activeThread == t) {
return "running on processor " + i;
}
}
return "unknown";
}
/**
* Update internal state of Scheduler to indicate that
* a thread is about to start
*/
@NoSubArchCompile
static void registerThread(VM_GreenThread thread) {
threadCreationMutex.lock("thread registration");
numActiveThreads += 1;
if (thread.isDaemonThread()) numDaemons += 1;
threadCreationMutex.unlock();
}
/**
* Schedule another thread
*/
@Override
protected void yieldInternal() {
VM_GreenThread.yield();
}
@Override
@NoSubArchCompile
protected void suspendDebuggerThreadInternal() {
debugRequested = false;
debuggerMutex.lock("debugger queue mutex");
VM_GreenScheduler.getCurrentThread().yield(debuggerQueue, debuggerMutex);
}
/**
* Suspend a concurrent worker: it will resume when the garbage collector notifies.
*/
@Override
@NoSubArchCompile
protected void suspendConcurrentCollectorThreadInternal() {
concurrentCollectorMutex.lock("suspend concurrent collector thread mutex");
VM_GreenScheduler.getCurrentThread().yield(concurrentCollectorQueue, concurrentCollectorMutex);
}
/**
* suspend the finalizer thread: it will resume when the garbage collector
* places objects on the finalizer queue and notifies.
*/
@Override
@NoSubArchCompile
protected void suspendFinalizerThreadInternal() {
finalizerMutex.lock("suspend finalizer mutex");
VM_GreenScheduler.getCurrentThread().yield(finalizerQueue, finalizerMutex);
}
/**
* Schedule thread waiting on l to give it a chance to acquire the lock
* @param lock the lock to allow other thread chance to acquire
*/
@Override
protected void yieldToOtherThreadWaitingOnLockInternal(VM_Lock lock) {
VM_GreenLock l = (VM_GreenLock)lock;
VM_GreenScheduler.getCurrentThread().yield(l.entering, l.mutex); // thread-switching benign
}
/**
* Is it safe to start forcing garbage collects for stress testing?
*/
@Override
@NoSubArchCompile
protected boolean safeToForceGCsInternal() {
return VM_GreenScheduler.allProcessorsInitialized &&
VM_GreenProcessor.getCurrentProcessor().threadSwitchingEnabled();
}
/**
* Set up the initial thread and processors as part of boot image writing
* @return the boot thread
*/
@Override
@Interruptible
@NoSubArchCompile
protected VM_Thread setupBootThreadInternal() {
int initProc = PRIMORDIAL_PROCESSOR_ID;
byte[] stack = new byte[ArchitectureSpecific.VM_ArchConstants.STACK_SIZE_BOOT];
VM_GreenThread startupThread = new VM_Scheduler.ThreadModel(stack, "Jikes_RVM_Boot_Thread");
numDaemons++;
processors[initProc].activeThread = startupThread;
return startupThread;
}
/**
* Get the type of the thread (to avoid guarded inlining..)
*/
@Override
@Interruptible
protected VM_TypeReference getThreadTypeInternal() {
return greenThreadType;
}
/**
* Get the type of the processor (to avoid guarded inlining..)
*/
@Override
@Interruptible
protected VM_TypeReference getProcessorTypeInternal() {
return greenProcessorType;
}
}