/* * $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 java.io.PrintWriter; import org.jnode.annotation.Inline; import org.jnode.annotation.Internal; import org.jnode.annotation.KernelSpace; import org.jnode.annotation.LoadStatics; import org.jnode.annotation.MagicPermission; import org.jnode.annotation.NoFieldAlignments; import org.jnode.annotation.Uninterruptible; import org.jnode.util.NumberUtils; import org.jnode.vm.CpuID; import org.jnode.vm.MathSupport; import org.jnode.vm.Unsafe; import org.jnode.vm.BaseVmArchitecture; import org.jnode.vm.VmMagic; import org.jnode.vm.VmSystem; import org.jnode.vm.classmgr.VmIsolatedStatics; import org.jnode.vm.classmgr.VmSharedStatics; import org.jnode.vm.compiler.GCMapIterator; import org.jnode.vm.compiler.NativeCodeCompiler; import org.jnode.vm.facade.VmUtils; import org.jnode.vm.objects.VmSystemObject; import org.jnode.vm.performance.PerformanceCounters; import org.vmmagic.unboxed.Address; import org.vmmagic.unboxed.ObjectReference; import org.vmmagic.unboxed.Word; /** * Abstract processor wrapper. * * @author Ewout Prangsma (epr@users.sourceforge.net) */ @NoFieldAlignments @Uninterruptible @MagicPermission public abstract class VmProcessor extends VmSystemObject implements org.jnode.vm.facade.VmProcessor { /** * The thread switch indicator KEEP THIS THE FIRST FIELD!!! */ private volatile Word threadSwitchIndicator; /** * Reference to myself; used in assembly code. */ final VmProcessor me; /** * The current thread on this processor. */ protected volatile VmThread currentThread; /** * The isolated statics table of the current thread (int[]). */ private volatile Object isolatedStaticsTable; /** * The isolated statics table of the current thread. */ private volatile VmIsolatedStatics isolatedStatics; /** * The next thread to schedule on this processor. */ volatile VmThread nextThread; /** * Stack end of current thread. This field is used by the native code. */ protected volatile Address stackEnd; /** * Stack end of kernel stack. This field is used by the native code. */ protected volatile Address kernelStackEnd; /** * The identifier of this processor. */ private int id; /** * The identifier of this processor as string. */ private String idString; /** * IRQ manager for this processor. */ private IRQManager irqMgr; /** * Address of threadSwitchIndicator. */ private Address tsiAddress; /** * The scheduler that is used. */ private final VmScheduler scheduler; /** * The kernel debugger that is used. */ private final KernelDebugger kernelDebugger; /** * The architecture of this processor. */ private final BaseVmArchitecture architecture; /** * The idle thread. */ private IdleThread idleThread; private int lockCount; /** * CPU identification. */ private transient CpuID cpuId; /** * The statics table (int[]). */ private volatile Object staticsTable; /** * The processor speed indication. */ private float jnodeMips; private int lastThreadPriority; private int sameThreadPriorityCount; /** * The data specific to this processor used by the heap manager. */ private final Object heapData; /** * Per processor MathSupport memory structures. */ private final MathSupport mathSupport = new MathSupport(); /** * Per processor GC map iterators. The index in the array is based on the * index in the VmArchitecture#getCompilers array. */ private final GCMapIterator[] gcMapIterators; /** * Per processor native code compiler id. The index in the array is based on * the index in the VmArchitecture#getCompilers array. */ private final int[] compilerIds; /** * Indicate the a thread switch is needed. */ public static final int TSI_SWITCH_NEEDED = 0x0001; /** * Indicate that the system is ready for thread switching. */ public static final int TSI_SYSTEM_READY = 0x0002; /** * Indicate the a thread switch is in progress. */ public static final int TSI_SWITCH_ACTIVE = 0x0004; /** * Indicate the a thread switch cannot occur. */ public static final int TSI_BLOCK_SWITCH = 0x0008; /** * Indicate the a thread switch is requested. */ public static final int TSI_SWITCH_REQUESTED = TSI_SWITCH_NEEDED | TSI_SYSTEM_READY; /** * Get the processor that the current thread is running on. * * @return the current processor */ @Inline @org.jnode.annotation.Uninterruptible @KernelSpace public static VmProcessor current() { return VmMagic.currentProcessor(); } /** * Initialize this instance. * * @param id * @param architecture */ public VmProcessor(int id, BaseVmArchitecture architecture, VmSharedStatics sharedStatics, VmIsolatedStatics isolatedStatics, VmScheduler scheduler) { this.id = id; this.idString = formatId(id); this.me = this; this.architecture = architecture; this.scheduler = scheduler; this.kernelDebugger = new KernelDebugger(scheduler); this.staticsTable = sharedStatics.getTable(); this.isolatedStatics = isolatedStatics; this.isolatedStaticsTable = isolatedStatics.getTable(); this.currentThread = createThread(isolatedStatics); this.heapData = VmUtils.getVm().getHeapManager().createProcessorHeapData(this); final NativeCodeCompiler[] compilers = architecture.getCompilers(); final int compilerCount = compilers.length; this.gcMapIterators = new GCMapIterator[compilerCount]; this.compilerIds = new int[compilerCount]; for (int i = 0; i < compilerCount; i++) { compilerIds[i] = compilers[i].getMagic(); gcMapIterators[i] = compilers[i].createGCMapIterator(); } } /** * Gets the architecture of this processor. * * @return the architecture of this processor */ @Inline @KernelSpace public final BaseVmArchitecture getArchitecture() { return architecture; } /** * Gets the current thread on this processor. * * @return The current thread on this processor * @throws org.vmmagic.pragma.UninterruptiblePragma */ @Inline @Uninterruptible public final VmThread getCurrentThread() { return currentThread; } /** * Gets the identifier of this processor. * * @return Returns the id. */ public final int getId() { return this.id; } /** * {@inheritDoc} */ @Uninterruptible @KernelSpace public final String getIdString() { return this.idString; } /** * Sets the id of this processor. * * @param id */ protected final void setId(int id) { this.id = id; this.idString = formatId(id); } /** * Create an ID string for the given id. * * @param id * @return the ID string */ protected String formatId(int id) { return "0x" + NumberUtils.hex(id, 2); } /** * Block any yieldpoints on this processor. * <p/> * FIXME: should not be public, but GC still uses it */ @Inline @Internal @Uninterruptible public final void disableReschedule(boolean claimSchedulerLock) { getTSIAddress().atomicOr(Word.fromIntSignExtend(TSI_BLOCK_SWITCH)); lockCount++; if (claimSchedulerLock) { scheduler.lock(); } } /** * Unblock any yieldpoints on this processor. * <p/> * FIXME: should not be public, but GC still uses it */ @Inline @Internal @Uninterruptible public final void enableReschedule(boolean releaseSchedulerLock) { if (releaseSchedulerLock) { scheduler.unlock(); } lockCount--; if (lockCount == 0) { getTSIAddress() .atomicAnd(Word.fromIntSignExtend(~TSI_BLOCK_SWITCH)); } } /** * Is this processor busy switching threads. * * @return true or false */ public final boolean isThreadSwitchActive() { return (!threadSwitchIndicator.and( Word.fromIntZeroExtend(TSI_SWITCH_ACTIVE)).isZero()); } /** * Give up the current cpu-time, and add the current thread to the back of * the ready queue. * * @param ignorePriority If true, the thread is always added to the back of the list, * regarding its priority. * @throws org.vmmagic.pragma.UninterruptiblePragma */ @Uninterruptible final void yield(boolean ignorePriority) { final VmThread t = this.currentThread; t.setYieldingState(); scheduler.addToReadyQueue(t, ignorePriority, "proc.yield"); threadSwitchIndicator = threadSwitchIndicator.or(Word .fromIntZeroExtend(TSI_SWITCH_NEEDED)); if (threadSwitchIndicator.NE(Word .fromIntZeroExtend(TSI_SWITCH_REQUESTED))) { Unsafe.debug("Yield with invalid tsi: " + threadSwitchIndicator); architecture.getStackReader().debugStackTrace(); Unsafe.die("yield"); } Unsafe.yieldPoint(); } /** * Given the current cpu-time. The current thread is not added to any queue. * * @throws org.vmmagic.pragma.UninterruptiblePragma */ @Uninterruptible final void suspend(boolean releaseSchedulerLock) { if (lockCount != 1) { Unsafe.debug("Suspend with invalid lockCount: "); Unsafe.debug(lockCount); architecture.getStackReader().debugStackTrace(); Unsafe.die("suspend"); } final VmThread t = this.currentThread; t.setYieldingState(); threadSwitchIndicator = threadSwitchIndicator.or(Word .fromIntZeroExtend(TSI_SWITCH_NEEDED)); enableReschedule(releaseSchedulerLock); if (threadSwitchIndicator.NE(Word .fromIntZeroExtend(TSI_SWITCH_REQUESTED))) { Unsafe.debug("Suspend with invalid tsi: "); Unsafe.debug(threadSwitchIndicator); architecture.getStackReader().debugStackTrace(50); Unsafe.die("VmProcessor#suspend"); } Unsafe.yieldPoint(); } /** * This method is called by the "yieldpoint" interrupt handler (see "vm-ints.asm") * with interrupts disabled. Try to keep it as short and as fast as possible! * * @throws org.vmmagic.pragma.UninterruptiblePragma */ @LoadStatics @KernelSpace @Uninterruptible final void reschedule() { // Unsafe.debug("R"); this.nextThread = null; try { // Get the current thread final VmThread current = currentThread; this.nextThread = current; // Process kernel debugger data if (Unsafe.isKdbEnabled()) { kernelDebugger.processAllKdbInput(); } // Dispatch interrupts if we already have an IRQ manager. final IRQManager irqMgr = this.irqMgr; if (irqMgr != null) { irqMgr.dispatchInterrupts(current); } // Add the current thread to the ready queue, if the state // is running. if (current.isRunning()) { scheduler.addToReadyQueue(current, false, getIdString()); } else { // Screen.debug("<Non-running thread in reschedule "+ // current.getThreadState() + " // called "+ current.asThread().getName() + "/>"); // Unsafe.die(); } // Determine the new thread. // Should we wakeup a sleeping thread? VmThread newThread = scheduler.popFirstSleepingThread(); // Take the first thread from the ready queue if (newThread == null) { newThread = scheduler.popFirstReadyThread(); } // It no other thread want to run, that means that the idle thread // has been halted, we give up. if (newThread == null) { Unsafe.debug("No Threads to run!\n"); scheduler.dump(); Unsafe.die("No thread to run in reschedule"); } newThread.wakeUpByScheduler(); this.nextThread = newThread; final int priority = newThread.priority; if (priority == lastThreadPriority) { sameThreadPriorityCount++; if ((priority > Thread.NORM_PRIORITY) && ((sameThreadPriorityCount % 2500) == 0)) { Unsafe.debug("Maybe stuck in high priority: "); Unsafe.debug(newThread.getName()); if (sameThreadPriorityCount > 100000) { getArchitecture().getStackReader().debugStackTrace( current); Unsafe.die("Probably deadlock in high priority thread"); } } } else { lastThreadPriority = priority; sameThreadPriorityCount = 0; } } catch (Throwable ex) { try { ex.printStackTrace(); } catch (Throwable ex2) { Unsafe.debug("Exception in Exception in Scheduler"); /* Ignore */ } Unsafe.die("Exception in reschedule"); } } /** * Create a new thread. * * @return The new thread */ protected abstract VmThread createThread(VmIsolatedStatics isolatedStatics); /** * Create a new thread. * * @param javaThread * @return The new thread */ public final VmThread createThread(Thread javaThread) { return createThread(getIsolatedStatics(), javaThread); } /** * Create a new thread. * * @param javaThread * @return The new thread */ public abstract VmThread createThread(VmIsolatedStatics isolatedStatics, Thread javaThread); /** * Gets the IRQ counters array. * * @return The irq counter array */ @KernelSpace @Uninterruptible protected abstract int[] getIrqCounters(); /** * Mark the system as ready for thread switching. */ @Internal public final void systemReadyForThreadSwitch() { if (idleThread == null) { idleThread = new IdleThread(); idleThread.start(); } getTSIAddress().atomicOr(Word.fromIntSignExtend(TSI_SYSTEM_READY)); } /** * Gets the address of the threadSwitchIndicator field in this object. <b>It is * assumed this field is the first field of this class!</b> * * @return The address of the thread switch indicator */ protected final Address getTSIAddress() { if (tsiAddress.isZero()) { tsiAddress = ObjectReference.fromObject(this).toAddress(); } return tsiAddress; } /** * Gets my IRQ manager. * * @return The irq manager */ public final synchronized IRQManager getIRQManager() { if (irqMgr == null) { irqMgr = architecture.createIRQManager(this); } return irqMgr; } /** * Gets the CPU identification of this processor. * * @return The CPU id. */ public final CpuID getCPUID() { if (cpuId == null) { cpuId = loadCPUID(); } return cpuId; } /** * Load the CPU id. * * @return CpuID */ protected abstract CpuID loadCPUID(); /** * Set the CPU id. * * @param id The new cpu id */ protected final void setCPUID(CpuID id) { this.cpuId = id; } /** * @return Returns the shared statics table (int[]). */ @Inline @Internal public final Object getSharedStaticsTable() { return this.staticsTable; } /** * @return Returns the isolated statics table (int[]). */ @Inline @Internal public final Object getIsolatedStaticsTable() { return this.isolatedStaticsTable; } /** * @param staticsTable The staticsTable to set. */ final void setStaticsTable(Object staticsTable) { this.staticsTable = staticsTable; } /** * Calculate the processor speed in "JNodeMips" and delay loops. */ @Internal public final void calibrate() { this.jnodeMips = VmSystem.calculateJNodeMips(); final String num = NumberUtils.toString(jnodeMips, 2); System.out.println("Processor " + getId() + ": " + getCPUID().getName() + ", " + num + " JNodeMIPS"); } /** * Gets the processor speed indication. * * @return the processor speed in "JNodeMips". */ public final float getJNodeMips() { return jnodeMips; } /** * {@inheritDoc} */ public abstract void dumpStatistics(PrintWriter out); /** * Gets the isolates statics table of the current thread. * * @return Returns the isolatedStatics. */ public final VmIsolatedStatics getIsolatedStatics() { return isolatedStatics; } /** * Gets the processor specific heap data. * * @return Returns the heapData. */ @Inline public final Object getHeapData() { return heapData; } /** * Gets the GC map iterator for the given native code compiler. * * @param compiler * @return the iterator */ @Inline public final GCMapIterator getGCMapIterator(NativeCodeCompiler compiler) { final int magic = compiler.getMagic(); for (int i = 0;; i++) { if (compilerIds[i] == magic) { return gcMapIterators[i]; } } } /** * @return Returns the mathSupport. */ @Inline @Internal public final MathSupport getMathSupport() { return mathSupport; } /** * Gets the performance counter accessor of this processor. * * @return the accessor. */ public abstract PerformanceCounters getPerformanceCounters(); /** * @param isolatedStatics the isolatedStatics to set */ final void setIsolatedStatics(VmIsolatedStatics isolatedStatics) { final Object table = isolatedStatics.getTable(); this.isolatedStatics = isolatedStatics; this.isolatedStaticsTable = table; } /** * @return the idleThread */ protected final IdleThread getIdleThread() { return idleThread; } /** * @return the scheduler */ @Uninterruptible @KernelSpace public final VmScheduler getScheduler() { return scheduler; } }