/*
* 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 static org.jikesrvm.ArchitectureSpecific.VM_StackframeLayoutConstants.STACK_SIZE_NORMAL;
import org.jikesrvm.VM;
import org.jikesrvm.adaptive.OSR_Listener;
import org.jikesrvm.adaptive.measurements.VM_RuntimeMeasurements;
import org.jikesrvm.annotations.NoSubArchCompile;
import org.jikesrvm.memorymanagers.mminterface.MM_Interface;
import org.jikesrvm.objectmodel.VM_ObjectModel;
import org.jikesrvm.runtime.VM_ArchEntrypoints;
import org.jikesrvm.runtime.VM_Entrypoints;
import org.jikesrvm.runtime.VM_Magic;
import org.jikesrvm.runtime.VM_Time;
import org.jikesrvm.scheduler.VM_Lock;
import org.jikesrvm.scheduler.VM_Processor;
import org.jikesrvm.scheduler.VM_ProcessorLock;
import org.jikesrvm.scheduler.VM_Scheduler;
import org.jikesrvm.scheduler.VM_Synchronization;
import org.jikesrvm.scheduler.VM_Thread;
import org.vmmagic.pragma.Interruptible;
import org.vmmagic.pragma.LogicallyUninterruptible;
import org.vmmagic.pragma.NoInline;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.LocalAddress;
import org.vmmagic.unboxed.Offset;
/**
* A green thread's Java execution context
*/
@Uninterruptible
@NoSubArchCompile
public class VM_GreenThread extends VM_Thread {
/** Lock controlling the suspending of a thread */
private static final Offset suspendPendingOffset = VM_Entrypoints.suspendPendingField.getOffset();
/**
* Should this thread be suspended the next time it is considered
* for scheduling? NB int as we CAS to modify it
*/
private volatile int suspendPending;
/**
* This thread's successor on a queue.
*/
private VM_GreenThread next;
/**
* ID of processor to run this thread (cycles for load balance)
*/
public int chosenProcessorId;
/**
* A thread proxy. Either null or an object holding a reference to this class
* and sitting in two queues. When one queue dequeues the object they nullify
* the reference to this class in the thread proxy, thereby indicating to the
* other queue the thread is no longer in their queue.
*/
public VM_ThreadProxy threadProxy;
/**
* Object specifying the event the thread is waiting for.
* E.g., set of file descriptors for an I/O wait.
*/
VM_ThreadEventWaitData waitData;
/**
* Virtual processor that this thread wants to run on
* (null --> any processor is ok).
*/
public VM_Processor processorAffinity;
/**
* Create a thread with default stack and with the given name.
*/
public VM_GreenThread(String name) {
this(MM_Interface.newStack(STACK_SIZE_NORMAL, false),
null, // java.lang.Thread
name,
true, // daemon
true, // system
Thread.NORM_PRIORITY,
false);
}
/**
* Create a thread with the given stack and name. Used by
* {@link org.jikesrvm.memorymanagers.mminterface.VM_CollectorThread} and the
* boot image writer for the boot thread.
*/
public VM_GreenThread(byte[] stack, String name) {
this(stack,
null, // java.lang.Thread
name,
true, // daemon
true, // system
Thread.NORM_PRIORITY,
false);
}
/**
* Create a thread with ... called by java.lang.VMThread.create. System thread
* isn't set.
*/
public VM_GreenThread(Thread thread, long stacksize, String name, boolean daemon, int priority, boolean forSubArch) {
this(MM_Interface.newStack((stacksize <= 0) ? STACK_SIZE_NORMAL : (int)stacksize, false),
thread, name, daemon, false, priority, forSubArch);
}
/**
* Create a thread.
*/
protected VM_GreenThread(byte[] stack, Thread thread, String name, boolean daemon, boolean system, int priority, boolean forSubArch) {
super(stack, thread, name, daemon, system, priority, forSubArch);
// for load balancing
chosenProcessorId = (VM.runningVM ? VM_Processor.getCurrentProcessorId() : 0);
}
/*
* Queue support
*/
/**
* Get the next element after this thread in a thread queue
*/
public final VM_GreenThread getNext() {
return next;
}
/**
* Set the next element after this thread in a thread queue
*/
public final void setNext(VM_GreenThread next) {
this.next = next;
}
/**
* Update internal state of Thread and Scheduler to indicate that
* a thread is about to start
*/
@Override
protected final void registerThreadInternal() {
VM_GreenScheduler.registerThread(this);
}
/**
* Start execution of 'this' by putting it on the given queue.
* Precondition: If the queue is global, caller must have the appropriate mutex.
* @param q the VM_ThreadQueue on which to enqueue this thread.
*/
public final void start(VM_GreenThreadQueue q) {
registerThread();
q.enqueue(this);
}
/*
* block and unblock
*/
/**
* Thread is blocked on a heavyweight lock
* @see VM_Lock#lockHeavy(Object)
*/
public final void block(VM_ThreadQueue entering, VM_ProcessorLock mutex) {
yield(entering, mutex);
}
/**
* Unblock thread from heavyweight lock blocking
* @see VM_Lock#unlockHeavy(Object)
*/
public final void unblock() {
schedule();
}
/**
* Process a taken yieldpoint.
* May result in threadswitch, depending on state of various control
* flags on the processor object.
*/
public static void yieldpoint(int whereFrom, LocalAddress yieldpointServiceMethodFP) {
boolean threadSwitch = false;
boolean cbsOverrun = false;
VM_GreenProcessor p = VM_GreenProcessor.getCurrentProcessor();
int takeYieldpointVal = p.takeYieldpoint;
p.takeYieldpoint = 0;
// Process request for code-patch memory sync operation
if (VM.BuildForPowerPC && p.codePatchSyncRequested) {
p.codePatchSyncRequested = false;
// TODO: Is this sufficient? Ask Steve why we don't need to sync icache/dcache. --dave
// make sure not get stale data
VM_Magic.isync();
VM_Synchronization.fetchAndDecrement(VM_Magic.getJTOC(), VM_ArchEntrypoints.toSyncProcessorsField.getOffset(), 1);
}
// If thread is in critical section we can't switch right now, defer until later
if (!p.threadSwitchingEnabled()) {
if (p.threadSwitchPending != 1) {
p.threadSwitchPending = takeYieldpointVal;
}
return;
}
// Process timer interrupt event
if (p.timeSliceExpired != 0) {
p.timeSliceExpired = 0;
if (p.yieldForCBSCall || p.yieldForCBSMethod) {
/*
* CBS Sampling is still active from previous quantum.
* Note that fact, but leave all the other CBS parameters alone.
*/
cbsOverrun = true;
} else {
if (VM.CBSCallSamplesPerTick > 0) {
p.yieldForCBSCall = true;
p.takeYieldpoint = -1;
p.firstCBSCallSample++;
p.firstCBSCallSample = p.firstCBSCallSample % VM.CBSCallSampleStride;
p.countdownCBSCall = p.firstCBSCallSample;
p.numCBSCallSamples = VM.CBSCallSamplesPerTick;
}
if (VM.CBSMethodSamplesPerTick > 0) {
p.yieldForCBSMethod = true;
p.takeYieldpoint = -1;
p.firstCBSMethodSample++;
p.firstCBSMethodSample = p.firstCBSMethodSample % VM.CBSMethodSampleStride;
p.countdownCBSMethod = p.firstCBSMethodSample;
p.numCBSMethodSamples = VM.CBSMethodSamplesPerTick;
}
}
if (++p.interruptQuantumCounter >= VM.schedulingMultiplier) {
threadSwitch = true;
p.interruptQuantumCounter = 0;
// Check various scheduling requests/queues that need to be polled periodically
if (VM_Scheduler.debugRequested && VM_GreenScheduler.allProcessorsInitialized) {
// service "debug request" generated by external signal
VM_GreenScheduler.debuggerMutex.lock("looking at debugger queue");
if (VM_GreenScheduler.debuggerQueue.isEmpty()) {
// debugger already running
VM_GreenScheduler.debuggerMutex.unlock();
} else { // awaken debugger
VM_GreenThread t = VM_GreenScheduler.debuggerQueue.dequeue();
VM_GreenScheduler.debuggerMutex.unlock();
t.schedule();
}
}
if (VM_GreenScheduler.wakeupQueue.isReady()) {
VM_GreenScheduler.wakeupMutex.lock("looking at wakeup queue");
VM_GreenThread t = VM_GreenScheduler.wakeupQueue.dequeue();
VM_GreenScheduler.wakeupMutex.unlock();
if (t != null) {
t.schedule();
}
}
}
if (VM.BuildForAdaptiveSystem) {
VM_RuntimeMeasurements.takeTimerSample(whereFrom, yieldpointServiceMethodFP);
}
if (threadSwitch && !cbsOverrun && (p.yieldForCBSMethod || p.yieldForCBSCall)) {
// want to sample the current thread, not the next one to be scheduled
// So, defer actual threadswitch until we take all of our samples
p.threadSwitchWhenCBSComplete = true;
threadSwitch = false;
}
if (VM.BuildForAdaptiveSystem) {
threadSwitch |= OSR_Listener.checkForOSRPromotion(whereFrom, yieldpointServiceMethodFP);
}
if (threadSwitch) {
p.yieldForCBSMethod = false;
p.yieldForCBSCall = false;
p.threadSwitchWhenCBSComplete = false;
}
}
if (p.yieldForCBSCall) {
if (!(whereFrom == BACKEDGE || whereFrom == OSROPT)) {
if (--p.countdownCBSCall <= 0) {
if (VM.BuildForAdaptiveSystem) {
// take CBS sample
VM_RuntimeMeasurements.takeCBSCallSample(whereFrom, yieldpointServiceMethodFP);
}
p.countdownCBSCall = VM.CBSCallSampleStride;
p.numCBSCallSamples--;
if (p.numCBSCallSamples <= 0) {
p.yieldForCBSCall = false;
if (!p.yieldForCBSMethod) {
p.threadSwitchWhenCBSComplete = false;
threadSwitch = true;
}
}
}
}
if (p.yieldForCBSCall) {
p.takeYieldpoint = -1;
}
}
if (p.yieldForCBSMethod) {
if (--p.countdownCBSMethod <= 0) {
if (VM.BuildForAdaptiveSystem) {
// take CBS sample
VM_RuntimeMeasurements.takeCBSMethodSample(whereFrom, yieldpointServiceMethodFP);
}
p.countdownCBSMethod = VM.CBSMethodSampleStride;
p.numCBSMethodSamples--;
if (p.numCBSMethodSamples <= 0) {
p.yieldForCBSMethod = false;
if (!p.yieldForCBSCall) {
p.threadSwitchWhenCBSComplete = false;
threadSwitch = true;
}
}
}
if (p.yieldForCBSMethod) {
p.takeYieldpoint = 1;
}
}
// Process request to initiate GC by forcing a thread switch.
if (p.yieldToGCRequested) {
p.yieldToGCRequested = false;
p.yieldForCBSCall = false;
p.yieldForCBSMethod = false;
p.threadSwitchWhenCBSComplete = false;
p.takeYieldpoint = 0;
threadSwitch = true;
}
if (VM.BuildForAdaptiveSystem && p.yieldToOSRRequested) {
p.yieldToOSRRequested = false;
OSR_Listener.handleOSRFromOpt(yieldpointServiceMethodFP);
threadSwitch = true;
}
if (threadSwitch) {
timerTickYield(whereFrom);
}
VM_GreenThread myThread = VM_GreenScheduler.getCurrentThread();
if (VM.BuildForAdaptiveSystem && myThread.isWaitingForOsr) {
ArchitectureSpecific.OSR_PostThreadSwitch.postProcess(myThread);
}
}
/**
* Suspend execution of current thread, in favor of some other thread.
* Move this thread to a random virtual processor (for minimal load balancing)
* if this processor has other runnable work.
*
* @param whereFrom backedge, prologue, epilogue?
*/
public static void timerTickYield(int whereFrom) {
VM_GreenThread myThread = VM_GreenScheduler.getCurrentThread();
// thread switch
myThread.beingDispatched = true;
if (trace) VM_Scheduler.trace("VM_GreenThread", "timerTickYield() scheduleThread ", myThread.getIndex());
VM_GreenProcessor.getCurrentProcessor().scheduleThread(myThread);
morph(true);
}
/**
* Suspend execution of current thread, in favor of some other thread.
*/
@NoInline
public static void yield() {
VM_GreenThread myThread = VM_GreenScheduler.getCurrentThread();
myThread.beingDispatched = true;
VM_GreenProcessor.getCurrentProcessor().readyQueue.enqueue(myThread);
morph(false);
}
/**
* Suspend execution of current thread in favor of some other thread.
* @param q queue to put thread onto
* @param l lock guarding that queue (currently locked)
*/
@NoInline
public final void yield(VM_AbstractThreadQueue q, VM_ProcessorLock l) {
if (VM.VerifyAssertions) VM._assert(this == VM_GreenScheduler.getCurrentThread());
if (state == State.RUNNABLE)
changeThreadState(State.RUNNABLE, State.BLOCKED);
beingDispatched = true;
q.enqueue(this);
l.unlock();
morph(false);
}
/**
* For timed wait, suspend execution of current thread in favor of some other thread.
* Put a proxy for the current thread
* on a queue waiting a notify, and
* on a wakeup queue waiting for a timeout.
*
* @param q1 the {@link VM_ThreadProxyWaitingQueue} upon which to wait for notification
* @param l1 the {@link VM_ProcessorLock} guarding <code>q1</code> (currently locked)
* @param q2 the {@link VM_ThreadProxyWakeupQueue} upon which to wait for timeout
* @param l2 the {@link VM_ProcessorLock} guarding <code>q2</code> (currently locked)
*/
@NoInline
private static void yield(VM_ThreadProxyWaitingQueue q1, VM_ProcessorLock l1,
VM_ThreadProxyWakeupQueue q2, VM_ProcessorLock l2) {
VM_GreenThread myThread = VM_GreenScheduler.getCurrentThread();
myThread.beingDispatched = true;
q1.enqueue(myThread.threadProxy); // proxy has been cached before locks were obtained
q2.enqueue(myThread.threadProxy); // proxy has been cached before locks were obtained
l1.unlock();
l2.unlock();
morph(false);
}
static void morph() {
morph(false);
}
/**
* Current thread has been placed onto some queue. Become another thread.
* @param timerTick timer interrupted if true
*/
@LogicallyUninterruptible
static void morph(boolean timerTick) {
VM_Magic.sync(); // to ensure beingDispatched flag written out to memory
if (trace) VM_Scheduler.trace("VM_GreenThread", "morph ");
VM_GreenThread myThread = VM_GreenScheduler.getCurrentThread();
if (VM.VerifyAssertions) {
VM_GreenProcessor.getCurrentProcessor().failIfThreadSwitchingDisabled();
VM._assert(myThread.beingDispatched, "morph: not beingDispatched");
}
// become another thread
//
VM_GreenProcessor.getCurrentProcessor().dispatch(timerTick);
// respond to interrupt sent to this thread by some other thread
// NB this can create a stack trace, so is interruptible
if (myThread.throwInterruptWhenScheduled) {
myThread.postExternalInterrupt();
}
}
/**
* Suspend execution of current thread in favor of some other thread.
* @param q queue to put thread onto (must be processor-local, ie.
* not guarded with a lock)
*/
@NoInline
public static void yield(VM_AbstractThreadQueue q) {
VM_GreenThread myThread = VM_GreenScheduler.getCurrentThread();
myThread.beingDispatched = true;
q.enqueue(myThread);
morph(false);
}
/**
* Thread model dependant sleep
* @param millis
* @param ns
*/
@Interruptible
@Override
protected final void sleepInternal(long millis, int ns) throws InterruptedException {
wakeupNanoTime = VM_Time.nanoTime() + (millis * (long)1e6) + ns;
// cache the proxy before obtaining lock
VM_ThreadProxy proxy = new VM_ThreadProxy(this, wakeupNanoTime);
if(sleepImpl(proxy)) {
throw new InterruptedException("sleep interrupted");
}
}
/**
* Uninterruptible portion of going to sleep
* @return were we interrupted prior to going to sleep
*/
private boolean sleepImpl(VM_ThreadProxy proxy) {
if (isInterrupted()) {
// we were interrupted before putting this thread to sleep
return true;
}
VM_GreenScheduler.wakeupMutex.lock("wakeup mutex for sleep");
this.threadProxy = proxy;
yield(VM_GreenScheduler.wakeupQueue, VM_GreenScheduler.wakeupMutex);
return false;
}
/**
* Support for Java {@link java.lang.Object#wait()} synchronization primitive.
*
* @param o the object synchronized on
*/
@Override
@Interruptible
protected final Throwable waitInternal(Object o) {
return waitInternal2(o, false, 0L);
}
/**
* Support for Java {@link java.lang.Object#wait()} synchronization primitive.
*
* @param o the object synchronized on
* @param millis the number of milliseconds to wait for notification
*/
@Override
@Interruptible
protected final Throwable waitInternal(Object o, long millis) {
return waitInternal2(o, true, millis);
}
/**
* Combine the two outer waitInternal into one bigger one
* @param o the object to wait upon
* @param hasTimeout have a timeout ?
* @param millis timeout value
* @return any exceptions created along the way
*/
@Interruptible
private Throwable waitInternal2(Object o, boolean hasTimeout, long millis) {
// Check early otherwise we'll fail an assert when creating the heavy lock
if (!VM_ObjectModel.holdsLock(o, VM_Scheduler.getCurrentThread())) {
return new IllegalMonitorStateException("waiting on " + o);
}
// get lock for object
VM_GreenLock l = (VM_GreenLock)VM_ObjectModel.getHeavyLock(o, true);
// this thread is supposed to own the lock on o
if (l.getOwnerId() != getLockingId()) {
return new IllegalMonitorStateException("waiting on " + o);
}
// Get proxy and set wakeup time
VM_ThreadProxy proxy;
if (!hasTimeout) {
proxy = new VM_ThreadProxy(this);
} else {
wakeupNanoTime = VM_Time.nanoTime() + millis * (long)1e6;
proxy = new VM_ThreadProxy(this, wakeupNanoTime);
}
// carry on to uninterruptible portion
Throwable t = waitImpl(o, l, hasTimeout, millis, proxy);
if (t == proxyInterruptException) {
// Create a proper stack trace
t = new InterruptedException("wait interrupted");
}
return t;
}
/**
* Uninterruptible portion of waiting
*/
private Throwable waitImpl(Object o, VM_GreenLock l, boolean hasTimeout, long millis, VM_ThreadProxy proxy) {
// Check thread isn't already in interrupted state
if (isInterrupted()) {
// it is so throw either thread death (from stop) or interrupted exception
if (VM.VerifyAssertions && (state != State.JOINING))
changeThreadState(State.RUNNABLE, State.RUNNABLE);
clearInterrupted();
if(causeOfThreadDeath == null) {
return proxyInterruptException;
} else {
return causeOfThreadDeath;
}
} else {
// non-interrupted wait
Throwable rethrow = null;
if (state != State.JOINING) {
if (hasTimeout) {
changeThreadState(State.RUNNABLE, State.TIMED_WAITING);
} else {
changeThreadState(State.RUNNABLE, State.WAITING);
}
}
// allow an entering thread a chance to get the lock
l.mutex.lock("performing Object.wait"); // until unlock(), thread-switching fatal
VM_Thread n = l.entering.dequeue();
if (n != null) n.schedule();
if (hasTimeout) {
VM_GreenScheduler.wakeupMutex.lock("performing timed Object.wait");
}
// squirrel away lock state in current thread
waitObject = l.getLockedObject();
waitCount = l.getRecursionCount();
// cache the proxy before obtaining lock
threadProxy = proxy;
// release l and simultaneously put t on l's waiting queue
l.setOwnerId(0);
if (!hasTimeout) {
try {
yield(l.waiting, l.mutex); // thread-switching benign
} catch (Throwable thr) {
rethrow = thr; // An InterruptedException. We'll rethrow it after regaining the lock on o.
}
} else {
try {
yield(l.waiting,
l.mutex,
VM_GreenScheduler.wakeupQueue,
VM_GreenScheduler.wakeupMutex); // thread-switching benign
} catch (Throwable thr) {
rethrow = thr;
}
}
if (state != State.JOINING && rethrow == null) {
if (hasTimeout) {
changeThreadState(State.TIMED_WAITING, State.RUNNABLE);
} else {
changeThreadState(State.WAITING, State.RUNNABLE);
}
}
// regain lock
VM_ObjectModel.genericLock(o);
waitObject = null;
if (waitCount != 1) { // reset recursion count
VM_Lock l2 = VM_ObjectModel.getHeavyLock(o, true);
l2.setRecursionCount(waitCount);
}
return rethrow;
}
}
/**
* Support for Java {@link java.lang.Object#notify()} synchronization primitive.
*
* @param o the object synchronized on
* @param lock the heavy weight lock
*/
@Override
protected final void notifyInternal(Object o, VM_Lock lock) {
VM_GreenLock l = (VM_GreenLock)lock;
l.mutex.lock("notify mutex"); // until unlock(), thread-switching fatal
VM_GreenThread t = l.waiting.dequeue();
if (t != null) {
l.entering.enqueue(t);
}
l.mutex.unlock(); // thread-switching benign
}
/**
* Support for Java {@link java.lang.Object#notify()} synchronization primitive.
*
* @param o the object synchronized on
* @param lock the heavy weight lock
*/
@Override
protected final void notifyAllInternal(Object o, VM_Lock lock) {
VM_GreenLock l = (VM_GreenLock)lock;
l.mutex.lock("notifyAll mutex"); // until unlock(), thread-switching fatal
VM_GreenThread t = l.waiting.dequeue();
while (t != null) {
l.entering.enqueue(t);
t = l.waiting.dequeue();
}
l.mutex.unlock(); // thread-switching benign
}
/**
* Put given thread onto the IO wait queue.
* @param waitData the wait data specifying the file descriptor(s)
* to wait for.
*/
public static void ioWaitImpl(VM_ThreadIOWaitData waitData) {
VM_GreenThread myThread = VM_GreenScheduler.getCurrentThread();
myThread.waitData = waitData;
myThread.changeThreadState(State.RUNNABLE, State.IO_WAITING);
yield(VM_GreenProcessor.getCurrentProcessor().ioQueue);
myThread.changeThreadState(State.IO_WAITING, State.RUNNABLE);
}
/**
* Put given thread onto the SubArch wait queue.
* @param waitData the wait data specifying the subarch threadID
* to wait for
*/
public static void subArchWaitImpl(VM_ThreadSubArchWaitData waitData) {
VM_GreenThread myThread = VM_GreenScheduler.getCurrentThread();
myThread.waitData = waitData;
myThread.changeThreadState(State.RUNNABLE, State.SUBARCH_WAITING);
yield(VM_GreenProcessor.getCurrentProcessor().subArchQueue);
myThread.changeThreadState(State.SUBARCH_WAITING, State.RUNNABLE);
}
/**
* Put given thread onto the process wait queue.
* @param waitData the wait data specifying which process to wait for
* @param process the <code>VM_Process</code> object associated
* with the process
*/
public static void processWaitImpl(VM_ThreadProcessWaitData waitData, VM_Process process) {
VM_GreenThread myThread = VM_GreenScheduler.getCurrentThread();
myThread.waitData = waitData;
myThread.changeThreadState(State.RUNNABLE, State.PROCESS_WAITING);
// Note that we have to perform the wait on the pthread
// that created the process, which may involve switching
// to a different VM_Processor.
VM_GreenProcessor creatingProcessor = process.getCreatingProcessor();
VM_ProcessorLock queueLock = creatingProcessor.processWaitQueueLock;
queueLock.lock("wait for process");
// This will throw InterruptedException if the thread
// is interrupted while on the queue.
myThread.yield(creatingProcessor.processWaitQueue, queueLock);
myThread.changeThreadState(State.PROCESS_WAITING, State.RUNNABLE);
}
/**
* Thread model dependent part of stopping/interrupting a thread
*/
@Override
protected final void killInternal() {
// remove this thread from wakeup and/or waiting queue
VM_ThreadProxy p = threadProxy;
if (p != null) {
// If the thread has a proxy, then (presumably) it is either
// doing a sleep() or a wait(), both of which are interruptible,
// so let morph() know that it should throw the
// external interrupt object.
this.throwInterruptWhenScheduled = true;
VM_GreenThread t = p.unproxy(); // t == this or t == null
if (t != null) {
t.schedule();
}
}
// TODO!! handle this thread executing native code
}
/**
* Thread model dependent part of thread suspension
*/
@Override
protected final void suspendInternal() {
if(VM_Synchronization.tryCompareAndSwap(this, suspendPendingOffset, 0, 1)) {
// successful change from no suspend pending to suspend pending
} else {
// TODO: in what cases do we want to allow suspending a suspended thread?
}
if (this == VM_GreenScheduler.getCurrentThread()) yield();
}
/**
* Thread model dependent part of thread resumption
*/
@Override
protected final void resumeInternal() {
if (VM_Synchronization.tryCompareAndSwap(this, suspendPendingOffset, 1, 0)) {
// we cleared the fact a thread suspend is pending, so thread was never
// removed from runnable queue. There's no work to do here.
} else {
// thread was actually dequeued so re-queue it again
VM_GreenProcessor.getCurrentProcessor().scheduleThread(this);
}
}
/**
* Suspend thread if a suspend is pending. Called by processor dispatch loop.
* Thread will be dequeued and not run if this returns true.
*
* @return whether the thread had a suspend pending
*/
final boolean suspendIfPending() {
if (suspendPending == 1) {
if (VM_Synchronization.tryCompareAndSwap(this, suspendPendingOffset, 1, 0)) {
// we turned the suspendPending flag off
return true;
} else {
// swap failed, so it must have been resumed prior to being suspended
}
}
return false;
}
/**
* Put this thread on ready queue for subsequent execution on a future
* timeslice.
* Assumption: VM_Thread.contextRegisters are ready to pick up execution
* ie. return to a yield or begin thread startup code
*/
@Override
public final void schedule() {
if (trace) VM_Scheduler.trace("VM_GreenThread", "schedule", getIndex());
if (state == State.BLOCKED)
changeThreadState(VM_Thread.State.BLOCKED, State.RUNNABLE);
VM_GreenProcessor.getCurrentProcessor().scheduleThread(this);
}
/**
* Give a string of information on how a thread is set to be scheduled
*/
@Override
@Interruptible
public String getThreadState() {
return VM_GreenScheduler.getThreadState(this);
}
/**
* Is this thread suitable for putting on a queue?
* @return whether the thread is terminated
*/
final boolean isQueueable() {
return state != State.TERMINATED;
}
}