⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tcb.java

📁 nachos操作系统框架
💻 JAVA
字号:
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.package nachos.machine;import nachos.security.*;import nachos.threads.KThread;import java.util.Vector;import java.security.PrivilegedAction;/** * A TCB simulates the low-level details necessary to create, context-switch, * and destroy Nachos threads. Each TCB controls an underlying JVM Thread * object. * * <p> * Do not use any methods in <tt>java.lang.Thread</tt>, as they are not * compatible with the TCB API. Most <tt>Thread</tt> methods will either crash * Nachos or have no useful effect. * * <p> * Do not use the <i>synchronized</i> keyword <b>anywhere</b> in your code. * It's against the rules, <i>and</i> it can easily deadlock nachos. */public final class TCB {    /**     * Allocate a new TCB.     */    public TCB() {    }         /**     * Give the TCB class the necessary privilege to create threads. This is     * necessary, because unlike other machine classes that need privilege, we     * want the kernel to be able to create TCB objects on its own.     *     * @param	privilege      	encapsulates privileged access to the Nachos     *				machine.     */    public static void givePrivilege(Privilege privilege) {	TCB.privilege = privilege;	privilege.tcb = new TCBPrivilege();    }        /**     * Causes the thread represented by this TCB to begin execution. The     * specified target is run in the thread.     */    public void start(Runnable target) {	/* We will not use synchronization here, because we're assuming that	 * either this is the first call to start(), or we're being called in	 * the context of another TCB. Since we only allow one TCB to run at a	 * time, no synchronization is necessary.	 *	 * The only way this assumption could be broken is if one of our	 * non-Nachos threads used the TCB code.	 */		/* Make sure this TCB has not already been started. If done is false,	 * then destroy() has not yet set javaThread back to null, so we can	 * use javaThread as a reliable indicator of whether or not start() has	 * already been invoked.	 */	Lib.assert(javaThread == null && !done);	/* Make sure there aren't too many running TCBs already. This	 * limitation exists in an effort to prevent wild thread usage.	 */	Lib.assert(runningThreads.size() < maxThreads);	isFirstTCB = (currentTCB == null);	/* Probably unnecessary sanity check: if this is not the first TCB, we	 * make sure that the current thread is bound to the current TCB. This	 * check can only fail if non-Nachos threads invoke start().	 */	if (!isFirstTCB)	    Lib.assert(currentTCB.javaThread == Thread.currentThread());	/* At this point all checks are complete, so we go ahead and start the	 * TCB. Whether or not this is the first TCB, it gets added to	 * runningThreads, and we save the target closure.	 */	runningThreads.add(this);	this.target = target;	if (!isFirstTCB) {	    /* If this is not the first TCB, we have to make a new Java thread	     * to run it. Creating Java threads is a privileged operation.	     */	    tcbTarget = new Runnable() {		    public void run() { threadroot(); }		};	    privilege.doPrivileged(new Runnable() {		    public void run() { javaThread = new Thread(tcbTarget); }		});	    /* The Java thread hasn't yet started, but we need to get it	     * blocking in yield(). We do this by temporarily turning off the	     * current TCB, starting the new Java thread, and waiting for it	     * to wake us up from threadroot(). Once the new TCB wakes us up,	     * it's safe to context switch to the new TCB.	     */	    currentTCB.running = false;	    	    this.javaThread.start();	    currentTCB.waitForInterrupt();	}	else {	    /* This is the first TCB, so we don't need to make a new Java	     * thread to run it; we just steal the current Java thread.	     */	    javaThread = Thread.currentThread();	    /* All we have to do now is invoke threadroot() directly. */	    threadroot();	}    }    /**     * Return the TCB of the currently running thread.     */    public static TCB currentTCB() {	return currentTCB;    }    /**     * Context switch between the current TCB and this TCB. This TCB will     * become the new current TCB. It is acceptable for this TCB to be the     * current TCB.     */    public void contextSwitch() {	/* Probably unnecessary sanity check: we make sure that the current	 * thread is bound to the current TCB. This check can only fail if	 * non-Nachos threads invoke start().	 */	Lib.assert(currentTCB.javaThread == Thread.currentThread());	// make sure AutoGrader.runningThread() called associateThread()	Lib.assert(currentTCB.associated);	currentTCB.associated = false;		// can't switch from a TCB to itself	if (this == currentTCB)	    return;	/* There are some synchronization concerns here. As soon as we wake up	 * the next thread, we cannot assume anything about static variables,	 * or about any TCB's state. Therefore, before waking up the next	 * thread, we must latch the value of currentTCB, and set its running	 * flag to false (so that, in case we get interrupted before we call	 * yield(), the interrupt will set the running flag and yield() won't	 * block).	 */	TCB previous = currentTCB;	previous.running = false;		this.interrupt();	previous.yield();    }        /**     * Destroy this TCB. This TCB must not be in use by the current thread.     * This TCB must also have been authorized to be destroyed by the     * autograder.     */    public void destroy() {	// make sure the current TCB is correct	Lib.assert(currentTCB != null &&		   currentTCB.javaThread == Thread.currentThread());	// can't destroy current thread	Lib.assert(this != currentTCB);	// thread must have started but not be destroyed yet	Lib.assert(javaThread != null && !done);	// ensure AutoGrader.finishingCurrentThread() called authorizeDestroy()	Lib.assert(nachosThread == toBeDestroyed);	toBeDestroyed = null;	this.done = true;	currentTCB.running = false;	this.interrupt();	currentTCB.waitForInterrupt();		this.javaThread = null;    }    /**     * Destroy all TCBs and exit Nachos. Same as <tt>Machine.terminate()</tt>.     */    public static void die() {	privilege.exit(0);    }    /**     * Test if the current JVM thread belongs to a Nachos TCB. The AWT event     * dispatcher is an example of a non-Nachos thread.     *     * @return	<tt>true</tt> if the current JVM thread is a Nachos thread.     */    public static boolean isNachosThread() {	return (currentTCB != null &&		Thread.currentThread() == currentTCB.javaThread);    }    private void threadroot() {	// this should be running the current thread	Lib.assert(javaThread == Thread.currentThread());	if (!isFirstTCB) {	    /* start() is waiting for us to wake it up, signalling that it's OK	     * to context switch to us. We leave the running flag false so that	     * we'll still run if a context switch happens before we go to	     * sleep. All we have to do is wake up the current TCB and then	     * wait to get woken up by contextSwitch() or destroy().	     */	    	    currentTCB.interrupt();	    this.yield();	}	else {	    /* start() called us directly, so we just need to initialize	     * a couple things.	     */	    	    currentTCB = this;	    running = true;	}	try {	    target.run();	    // no way out of here without going throw one of the catch blocks	    Lib.assertNotReached();	}	catch (ThreadDeath e) {	    // make sure this TCB is being destroyed properly	    if (!done) {		System.out.print("\nTCB terminated improperly!\n");		privilege.exit(1);	    }	    runningThreads.removeElement(this);	    if (runningThreads.isEmpty())		privilege.exit(0);	}	catch (Throwable e) {	    System.out.print("\n");	    e.printStackTrace();	    runningThreads.removeElement(this);	    if (runningThreads.isEmpty())		privilege.exit(1);	    else		die();	}    }    /**     * Invoked by threadroot() and by contextSwitch() when it is necessary to     * wait for another TCB to context switch to this TCB. Since this TCB     * might get destroyed instead, we check the <tt>done</tt> flag after     * waking up. If it is set, the TCB that woke us up is waiting for an     * acknowledgement in destroy(). Otherwise, we just set the current TCB to     * this TCB and return.     */    private void yield() {	waitForInterrupt();		if (done) {	    currentTCB.interrupt();	    throw new ThreadDeath();	}	currentTCB = this;    }    /**     * Waits on the monitor bound to this TCB until its <tt>running</tt> flag     * is set to <tt>true</tt>. <tt>waitForInterrupt()</tt> is used whenever a     * TCB needs to go to wait for its turn to run. This includes the ping-pong     * process of starting and destroying TCBs, as well as in context switching     * from this TCB to another. We don't rely on <tt>currentTCB</tt>, since it     * is updated by <tt>contextSwitch()</tt> before we get called.     */    private synchronized void waitForInterrupt() {	while (!running) {	    try { wait(); }	    catch (InterruptedException e) { }	}    }    /**     * Wake up this TCB by setting its <tt>running</tt> flag to <tt>true</tt>     * and signalling the monitor bound to it. Used in the ping-pong process of     * starting and destroying TCBs, as well as in context switching to this     * TCB.     */    private synchronized void interrupt() {	running = true;	notify();    }    private void associateThread(KThread thread) {	// make sure AutoGrader.runningThread() gets called only once per	// context switch	Lib.assert(!associated);	associated = true;	Lib.assert(thread != null);	if (nachosThread != null)	    Lib.assert(thread == nachosThread);	else	    nachosThread = thread;    }    private static void authorizeDestroy(KThread thread) {	// make sure AutoGrader.finishingThread() gets called only once per	// destroy	Lib.assert(toBeDestroyed == null);	toBeDestroyed = thread;    }    /**     * The maximum number of started, non-destroyed TCB's that can be in     * existence.     */    public static final int maxThreads = 250;    /**     * A reference to the currently running TCB. It is initialized to     * <tt>null</tt> when the <tt>TCB</tt> class is loaded, and then the first     * invocation of <tt>start(Runnable)</tt> assigns <tt>currentTCB</tt> a     * reference to the first TCB. After that, only <tt>yield()</tt> can     * change <tt>currentTCB</tt> to the current TCB, and only after     * <tt>waitForInterrupt()</tt> returns.     *     * <p>     * Note that <tt>currentTCB.javaThread</tt> will not be the current thread     * if the current thread is not bound to a TCB (this includes the threads     * created for the hardware simulation).     */    private static TCB currentTCB = null;    /**     * A vector containing all <i>running</i> TCB objects. It is initialized to     * an empty vector when the <tt>TCB</tt> class is loaded. TCB objects are     * added only in <tt>start(Runnable)</tt>, which can only be invoked once     * on each TCB object. TCB objects are removed only in each of the     * <tt>catch</tt> clauses of <tt>threadroot()</tt>, one of which is always     * invoked on thread termination. The maximum number of threads in     * <tt>runningThreads</tt> is limited to <tt>maxThreads</tt> by     * <tt>start(Runnable)</tt>. If <tt>threadroot()</tt> drops the number of     * TCB objects in <tt>runningThreads</tt> to zero, Nachos exits, so once     * the first TCB is created, this vector is basically never empty.     */    private static Vector runningThreads = new Vector();        private static Privilege privilege;    private static KThread toBeDestroyed = null;    /**     * <tt>true</tt> if and only if this TCB is the first TCB to start, the one     * started in <tt>Machine.main(String[])</tt>. Initialized by     * <tt>start(Runnable)</tt>, on the basis of whether <tt>currentTCB</tt>     * has been initialized.     */    private boolean isFirstTCB;    /**     * A reference to the Java thread bound to this TCB. It is initially     * <tt>null</tt>, assigned to a Java thread in <tt>start(Runnable)</tt>,     * and set to <tt>null</tt> again in <tt>destroy()</tt>.     */    private Thread javaThread = null;    /**     * <tt>true</tt> if and only if the Java thread bound to this TCB ought to     * be running. This is an entirely different condition from membership in     * <tt>runningThreads</tt>, which contains all TCB objects that have     * started and have not terminated. <tt>running</tt> is only <tt>true</tt>     * when the associated Java thread ought to run ASAP. When starting or     * destroying a TCB, this is temporarily true for a thread other than that     * of the current TCB.     */    private boolean running = false;    /**     * Set to <tt>true</tt> by <tt>destroy()</tt>, so that when     * <tt>waitForInterrupt()</tt> returns in the doomed TCB, <tt>yield()</tt>     * will know that the current TCB is doomed.     */    private boolean done = false;        private KThread nachosThread = null;    private boolean associated = false;    private Runnable target;    private Runnable tcbTarget;    private static class TCBPrivilege implements Privilege.TCBPrivilege {	public void associateThread(KThread thread) {	    Lib.assert(currentTCB != null);	    currentTCB.associateThread(thread);	}	public void authorizeDestroy(KThread thread) {	    TCB.authorizeDestroy(thread);	}    }}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -