/* * 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.memorymanagers.mminterface; import org.jikesrvm.VM; import org.jikesrvm.mm.mmtk.Collection; import org.jikesrvm.mm.mmtk.Lock; import org.jikesrvm.scheduler.VM_Scheduler; import org.jikesrvm.scheduler.greenthreads.VM_GreenScheduler; import org.jikesrvm.scheduler.greenthreads.VM_GreenThread; import org.vmmagic.pragma.LogicallyUninterruptible; import org.vmmagic.pragma.Uninterruptible; /** * VM_Handshake handles mutator requests to initiate a collection, and * wait for a collection to complete. It implements the process of * suspending all mutator threads executing in Java and starting all * the GC threads (VM_CollectorThreads) for the processors that will * be participating in a collection. This may not be all processors, * if we exclude those executing in native code. * * Because the threading strategy within RVM is currently under * revision, the logic here is also changing and somewhat "messy". * * @see VM_CollectorThread */ public class VM_Handshake { /*********************************************************************** * * Class variables */ public static final int verbose = 0; static final int LOCKOUT_GC_WORD = 0x0CCCCCCC; /*********************************************************************** * * Instance variables */ private Lock lock = new Lock("handshake"); protected boolean requestFlag; protected boolean completionFlag; public int gcTrigger; // reason for this GC public VM_Handshake() { reset(); } /** * Called by mutators to request a garbage collection and wait for * it to complete. * * Waiting is actually just yielding the processor to schedule the * collector thread, which will disable further thread switching on * the processor until it has completed the collection. */ @LogicallyUninterruptible @Uninterruptible public void requestAndAwaitCompletion(int why) { if (request(why)) { if (verbose >= 1) VM.sysWriteln("GC Message: VM_Handshake.requestAndAwaitCompletion - yielding"); /* allow a gc thread to run */ VM_Scheduler.yield(); complete(); if (verbose >= 1) VM.sysWriteln("GC Message: VM_Handshake.requestAndAwaitCompletion - mutator running"); } } /** * Called by mutators to request an asynchronous garbage collection. * After initiating a GC (if one is not already initiated), the * caller continues until it yields to the GC. It may thus make * this call at an otherwise unsafe point. */ @Uninterruptible public void requestAndContinue(int why) { request(why); } @Uninterruptible public void reset() { gcTrigger = Collection.UNKNOWN_GC_TRIGGER; requestFlag = false; completionFlag = false; } /** * Initiates a garbage collection. Called from requestAndAwaitCompletion * by the first mutator thread to request a collection using the * current VM_Handshake object. * * The sequence of events that start a collection is initiated by the * calling mutator, and it then yields, waiting for the collection * to complete. * * While mutators are executing, all the GC threads (VM_CollectorThreads) * reside on a single system queue, VM_Scheduler.collectorQueue. This * method determines which processors will participate in the collection, * dequeues the GC threads associated with those processors, and * schedules them for executing on their respective processors. * (Most of the time, all processors and thus all GC threads participate, * but this is changing as the RVM thread startegy changes.) * * The collection actually starts when all participating GC threads * arrive at the first rendezvous in VM_CollectorThreads run method, * and suspend thread switching on their processors. * * While collection is in progress, mutators are not explicitly waiting * for the collection. They reside in the thread dispatch queues of their * processors, until the collector threads re-enable thread switching. */ @Uninterruptible private void initiateCollection() { /* check that scheduler initialization is complete */ if (!VM_GreenScheduler.allProcessorsInitialized) { VM.sysWrite("GC required before system fully initialized"); VM.sysWriteln("Specify larger than default heapsize on command line"); VM_Scheduler.dumpStack(); VM.shutdown(VM.EXIT_STATUS_MISC_TROUBLE); } /* wait for preceding GC to complete */ if (verbose >= 2) { VM.sysWrite("GC Message: VM_Handshake.initiateCollection before waiting"); VM_GreenScheduler.collectorQueue.dump(); } int maxCollectorThreads = waitForPrecedingGC(); /* Acquire global lockout field inside the boot record. Will be * released when gc completes. */ VM_CollectorThread.gcThreadRunning = true; /* reset counter for collector threads arriving to participate in * the collection */ VM_CollectorThread.participantCount[0] = 0; /* reset rendezvous counters to 0, the decision about which * collector threads will participate has moved to the run method * of CollectorThread */ VM_CollectorThread.gcBarrier.resetRendezvous(); /* Deque and schedule collector threads on ALL RVM Processors. */ if (verbose >= 1) { VM.sysWriteln("GC Message: VM_Handshake.initiateCollection: scheduling collector threads"); } VM_GreenScheduler.collectorMutex.lock("handshake collector mutex"); if (VM_GreenScheduler.collectorQueue.length() != maxCollectorThreads) { VM.sysWriteln("GC Error: Expected ", maxCollectorThreads, " GC threads. Found ", VM_GreenScheduler.collectorQueue.length()); } while (VM_GreenScheduler.collectorQueue.length() > 0) { VM_GreenThread t = VM_GreenScheduler.collectorQueue.dequeue(); t.schedule(); } VM_GreenScheduler.collectorMutex.unlock(); } /** * Wait for all GC threads to complete previous collection cycle. * * @return The number of GC threads. */ @Uninterruptible private int waitForPrecedingGC() { /* * Get the number of GC threads. Include NativeDaemonProcessor * collector thread in the count. If it exists, check for null to * allow builds without a NativeDaemon (see VM_Scheduler) */ int maxCollectorThreads = VM_GreenScheduler.numProcessors; /* Wait for all gc threads to finish preceeding collection cycle */ if (verbose >= 1) { VM.sysWrite("GC Message: VM_Handshake.initiateCollection "); VM.sysWriteln("checking if previous collection is finished"); } int count = 0; while (true) { VM_GreenScheduler.collectorMutex.lock("waiting for preceeding GC"); int len = VM_GreenScheduler.collectorQueue.length(); if (count++ == 100000) { VM.sysWriteln("GC Warning: WAITED LONG TIME FOR PRECEEDING GC TO FINISH"); VM.sysWriteln("GC Warning: len = ", len); VM.sysWriteln("GC Warning: maxCollTh = ", maxCollectorThreads); // VM_Scheduler.collectorQueue.dump(); } VM_GreenScheduler.collectorMutex.unlock(); if (len < maxCollectorThreads) { if (verbose >= 1) { VM.sysWrite("GC Message: VM_Handshake.initiateCollection waiting for previous collection to finish"); } lock.release(); // release lock so other threads can make progress VM_Scheduler.yield(); lock.acquire(); // acquire lock to make progress } else { break; } } return maxCollectorThreads; } @Uninterruptible private void complete() { for (int i = 1; i <= VM_GreenScheduler.numProcessors; i++) { VM_GreenScheduler.processors[i].unblockIfBlockedInC(); } } /** * Called by mutators to request a garbage collection. If the * completionFlag is already set, return false. Else, if the * requestFlag is not yet set (ie this is the first mutator to * request this collection) then initiate the collection sequence * * @return true if the completion flag is not already set. */ @Uninterruptible private boolean request(int why) { lock.acquire(); if (completionFlag) { if (verbose >= 1) { VM.sysWriteln("GC Message: mutator: already completed"); } lock.release(); return false; } if (why > gcTrigger) gcTrigger = why; if (requestFlag) { if (verbose >= 1) { VM.sysWriteln("GC Message: mutator: already in progress"); } } else { // first mutator initiates collection by making all gc threads // runnable at high priority if (verbose >= 1) VM.sysWriteln("GC Message: VM_Handshake - mutator: initiating collection"); requestFlag = true; initiateCollection(); } lock.release(); return true; } /** * Set the completion flag that indicates the collection has * completed. Called by a collector thread after the collection has * completed. It currently does not do a "notify" on waiting * mutator threads, since they are in VM_Processor thread queues, * waiting for the collector thread to re-enable thread switching. * * @see VM_CollectorThread */ @Uninterruptible void notifyCompletion() { lock.acquire(); if (verbose >= 1) { VM.sysWriteln("GC Message: VM_Handshake.notifyCompletion"); } complete(); completionFlag = true; lock.release(); } }