/*
* 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;
import org.jikesrvm.ArchitectureSpecific;
import org.jikesrvm.SubordinateArchitecture;
import org.jikesrvm.VM;
import org.jikesrvm.VM_Constants;
import org.jikesrvm.annotations.NoSubArchCompile;
import org.jikesrvm.memorymanagers.mminterface.MM_ProcessorContext;
import org.jikesrvm.runtime.VM_Entrypoints;
import org.jikesrvm.runtime.VM_Magic;
import org.jikesrvm.scheduler.greenthreads.VM_GreenThread;
import org.vmmagic.pragma.Entrypoint;
import org.vmmagic.pragma.Inline;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.pragma.UninterruptibleNoWarn;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.LocalAddress;
import org.vmmagic.unboxed.Offset;
/**
* The context a thread runs within. For green threads we multiplex execution of
* large number of VM_Threads on small number of native kernel threads. For
* native threads the mapping is one-to-one.
*/
@Uninterruptible
public abstract class VM_Processor extends MM_ProcessorContext implements VM_Constants {
/*
* definitions for VP status for implementation of jni
*/
/** VP is in Java code */
public static final int IN_JAVA = 1;
/** VP is in native code */
public static final int IN_NATIVE = 2;
/** VP is blocked in native code */
public static final int BLOCKED_IN_NATIVE = 3;
/*
* NOTE: The order of field declarations determines
* the layout of the fields in the processor object
* For IA32, it is valuable (saves code space) to
* declare the most frequently used fields first so that
* they can be accessed with 8 bit immediates.
* On PPC, we have plenty of bits of immediates in
* load/store instructions, so it doesn't matter.
*/
/*
* BEGIN FREQUENTLY ACCESSED INSTANCE FIELDS
*/
/**
* Should the next executed yieldpoint be taken?
* Can be true for a variety of reasons. See VM_Thread.yieldpoint
* <p>
* To support efficient sampling of only prologue/epilogues
* we also encode some extra information into this field.
* 0 means that the yieldpoint should not be taken.
* >0 means that the next yieldpoint of any type should be taken
* <0 means that the next prologue/epilogue yieldpoint should be taken
*/
@Entrypoint
public int takeYieldpoint;
/**
* Thread currently running on this processor. NB for native threads this
* field could be final
*/
@Entrypoint
public VM_Thread activeThread;
/**
* cached activeThread.stackLimit;
* removes 1 load from stackoverflow sequence.
*/
@Entrypoint
public LocalAddress activeThreadStackLimit;
/**
* Cache the results of activeThread.getLockingId()
* for use in monitor operations.
*/
@Entrypoint
public int threadId;
/* --------- BEGIN IA-specific fields. NOTE: NEED TO REFACTOR --------- */
// On powerpc, these values are in dedicated registers,
// we don't have registers to burn on IA32, so we indirect
// through the PR register to get them instead.
/**
* Base pointer of JTOC (VM_Statics.slots)
* TODO: the JTOC doesn't move so this field is redundant
*/
public Address jtoc;
/**
* FP for current frame, saved in the prologue of every method
*/
Address framePointer;
/**
* "hidden parameter" for interface invocation thru the IMT
*/
int hiddenSignatureId;
/**
* "hidden parameter" from ArrayIndexOutOfBounds trap to C trap handler
*/
int arrayIndexTrapParam;
/* --------- END IA-specific fields. NOTE: NEED TO REFACTOR --------- */
// More GC fields
//
/** count live objects during gc */
public int large_live;
/** count live objects during gc */
public int small_live;
/** used for instrumentation in allocators */
public long totalBytesAllocated;
/** used for instrumentation in allocators */
public long totalObjectsAllocated;
/** used for instrumentation in allocators */
public long synchronizedObjectsAllocated;
/**
* Has the current time slice expired for this virtual processor?
* This is set by the C time slicing code which is driven either
* by a timer interrupt or by a dedicated pthread in a nanosleep loop.
* Is set approximately once every VM.interruptQuantum ms except when
* GC is in progress.
*/
@Entrypoint
public int timeSliceExpired;
/**
* Is the next taken yieldpoint in response to a request to
* schedule a GC?
*/
public boolean yieldToGCRequested;
/**
* Is the next taken yieldpoint in response to a request to perform OSR?
*/
public boolean yieldToOSRRequested;
/**
* Is CBS enabled for 'call' yieldpoints?
*/
public boolean yieldForCBSCall;
/**
* Is CBS enabled for 'method' yieldpoints?
*/
public boolean yieldForCBSMethod;
/**
* Should we threadswitch when all CBS samples are taken for this window?
*/
public boolean threadSwitchWhenCBSComplete;
/**
* Number of CBS samples to take in this window
*/
public int numCBSCallSamples;
/**
* Number of call yieldpoints between CBS samples
*/
public int countdownCBSCall;
/**
* round robin starting point for CBS samples
*/
public int firstCBSCallSample;
/**
* Number of CBS samples to take in this window
*/
public int numCBSMethodSamples;
/**
* Number of counter ticks between CBS samples
*/
public int countdownCBSMethod;
/**
* round robin starting point for CBS samples
*/
public int firstCBSMethodSample;
/* --------- BEGIN PPC-specific fields. NOTE: NEED TO REFACTOR --------- */
/**
* flag indicating this processor needs to execute a memory synchronization sequence
* Used for code patching on SMP PowerPCs.
*/
public boolean codePatchSyncRequested;
/* --------- END PPC-specific fields. NOTE: NEED TO REFACTOR --------- */
/**
* For builds using counter-based sampling. This field holds a
* processor-specific counter so that it can be updated efficiently
* on SMP's.
*/
public int processor_cbs_counter;
// How many times timer interrupt has occurred since last thread switch
public int interruptQuantumCounter = 0;
/**
* END FREQUENTLY ACCESSED INSTANCE FIELDS
*/
/**
* Identity of this processor.
* Note: 1. VM_Scheduler.processors[id] == this processor
* 2. id must be non-zero because it is used in
* VM_ProcessorLock ownership tests
*/
public final int id;
/**
* Has this processor's pthread initialization completed yet?
* A value of:
* false means "cpu is still executing C code (on a C stack)"
* true means "cpu is now executing vm code (on a vm stack)"
*/
public boolean isInitialized;
/**
* number of processor locks currently held (for assertion checking)
*/
private int lockCount;
private final String[] lockReasons = VM.VerifyAssertions ? new String[100] : null;
@NoSubArchCompile
public void registerLock(String reason) {
VM_Magic.setObjectAtOffset(lockReasons, Offset.fromIntSignExtend(lockCount<<2), reason);
lockCount ++;
}
@NoSubArchCompile
public void registerUnlock() {
lockCount --;
VM._assert(lockCount >= 0);
}
@NoSubArchCompile
protected void checkLockCount(int i) {
if (lockCount != i) {
VM.sysWrite("Error lock count not ", i);
VM.sysWriteln(" but ", lockCount);
for (int j=0; j < lockCount; j++) {
VM.sysWrite("Processor lock ", j);
VM.sysWriteln(" acquired for ", lockReasons[j]);
}
VM_Scheduler.dumpStack();
}
}
/**
* Status of the processor.
* Always one of IN_JAVA, IN_NATIVE or BLOCKED_IN_NATIVE.
*/
public int vpStatus;
/**
* pthread_id (AIX's) for use by signal to wakeup
* sleeping idle thread (signal accomplished by pthread_kill call)
*
* CRA, Maria
*/
public int pthread_id;
/*
* book keeping for thick locks, a range of thick locks is held per processor
*/
/** last lock in VM_Lock available for this processor */
public int lastLockIndex;
/** next lock in VM_Lock available for this processor */
public int nextLockIndex;
/** a free lock */
public VM_Lock freeLock;
/** number of free locks */
public int freeLocks;
/** number of lock allocation operations on processor */
public int locksAllocated;
/** number of lock free operations on processor */
public int locksFreed;
// to handle contention for processor locks
//
VM_ProcessorLock awaitingProcessorLock;
VM_Processor contenderLink;
/**
* Scratch area for use for gpr <=> fpr transfers by PPC baseline compiler.
* Used to transfer x87 to SSE registers on IA32
*/
@SuppressWarnings({"unused", "CanBeFinal", "UnusedDeclaration"})
//accessed via VM_EntryPoints
private double scratchStorage;
/**
* Create data object to be associated with an o/s kernel thread
* (aka "virtual cpu" or "pthread").
* @param id id that will be returned by getCurrentProcessorId() for
* this processor.
*/
protected VM_Processor(int id) {
// presave JTOC register contents
// (so lintel compiler can us JTOC for scratch)
if (VM.BuildForIA32 && VM.runningVM) this.jtoc = VM_Magic.getJTOC();
this.id = id;
this.lastLockIndex = -1;
this.vpStatus = IN_JAVA;
}
/**
* Request the thread executing on the processor to take the next executed yieldpoint
* and initiate a GC
*/
public void requestYieldToGC() {
takeYieldpoint = 1;
yieldToGCRequested = true;
}
/**
* Request the thread executing on the processor to take the next executed yieldpoint
* and issue memory synchronization instructions
*/
public void requestPostCodePatchSync() {
if (VM.BuildForPowerPC) {
takeYieldpoint = 1;
codePatchSyncRequested = true;
} else {
if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
}
}
/**
* Get processor that's being used to run the current java thread.
*/
@Inline
@UninterruptibleNoWarn
public static VM_Processor getCurrentProcessor() {
if (!VM_Magic.runningOnSubArch()) {
return ArchitectureSpecific.VM_ProcessorLocalState.getCurrentProcessor();
} else {
return SubordinateArchitecture.VM_ProcessorLocalState.getCurrentProcessor();
}
}
/**
* Get processor that's being used to run the current java thread.
*/
@Inline
public static VM_Thread getCurrentThread() {
return getCurrentProcessor().activeThread;
}
/**
* Is it ok to switch to a new VM_Thread in this processor? NB only has
* meaning with green threads
*/
public abstract boolean threadSwitchingEnabled();
/**
* Become next "ready" thread.
* Note: This method is ONLY intended for use by VM_Thread.
* @param timerTick timer interrupted if true
*/
public abstract void dispatch(boolean timerTick);
/**
* Get id of processor that's being used to run the current java thread.
*/
@Inline
public static int getCurrentProcessorId() {
return getCurrentProcessor().id;
}
//---------------------//
// Garbage Collection //
//---------------------//
@NoSubArchCompile
public boolean unblockIfBlockedInC() {
int newState, oldState;
boolean result = true;
Offset offset = VM_Entrypoints.vpStatusField.getOffset();
do {
oldState = VM_Magic.prepareInt(this, offset);
if (oldState != BLOCKED_IN_NATIVE) {
result = false;
break;
}
newState = IN_NATIVE;
} while (!(VM_Magic.attemptInt(this, offset, oldState, newState)));
return result;
}
/**
* sets the VP status to BLOCKED_IN_NATIVE if it is currently IN_NATIVE (ie C)
* returns true if BLOCKED_IN_NATIVE
*/
@NoSubArchCompile
public boolean lockInCIfInC() {
int oldState;
Offset offset = VM_Entrypoints.vpStatusField.getOffset();
do {
oldState = VM_Magic.prepareInt(this, offset);
if (VM.VerifyAssertions) VM._assert(oldState != BLOCKED_IN_NATIVE);
if (oldState != IN_NATIVE) {
if (VM.VerifyAssertions) VM._assert(oldState == IN_JAVA);
return false;
}
} while (!(VM_Magic.attemptInt(this, offset, oldState, BLOCKED_IN_NATIVE)));
return true;
}
/**
* Disable thread switching in this processor.
* @param reason for disabling thread switching
*/
public abstract void disableThreadSwitching(String reason);
/**
* Enable thread switching in this processor.
*/
public abstract void enableThreadSwitching();
/**
* Fail if thread switching is disabled on this processor
*/
public abstract void failIfThreadSwitchingDisabled();
@NoSubArchCompile
public void dumpLocks() {
VM.sysWrite(" processor ");
VM.sysWriteInt(id);
VM.sysWrite(": ");
VM.sysWriteInt(locksAllocated);
VM.sysWrite(" locks allocated, ");
VM.sysWriteInt(locksFreed);
VM.sysWrite(" locks freed, ");
VM.sysWriteInt(freeLocks);
VM.sysWrite(" free looks, ");
int unallocated = lastLockIndex - nextLockIndex + 1;
VM.sysWriteInt(unallocated);
VM.sysWrite(" unallocated slots\n");
}
public abstract void transferThread(VM_Thread t);
}