001 /*
002 * This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 * This file is licensed to You under the Eclipse Public License (EPL);
005 * You may not use this file except in compliance with the License. You
006 * may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 * See the COPYRIGHT.txt file distributed with this work for information
011 * regarding copyright ownership.
012 */
013 package org.jikesrvm.mm.mminterface;
014
015 import org.jikesrvm.VM;
016 import org.jikesrvm.mm.mmtk.Collection;
017 import org.jikesrvm.scheduler.RVMThread;
018 import org.jikesrvm.scheduler.Monitor;
019 import org.vmmagic.pragma.Unpreemptible;
020 import org.mmtk.plan.Plan;
021
022 /**
023 * Handshake handles mutator requests to initiate a collection, and
024 * wait for a collection to complete. It implements the process of
025 * suspending all mutator threads executing in Java and starting all
026 * the GC threads (CollectorThreads) for the processors that will
027 * be participating in a collection. This may not be all processors,
028 * if we exclude those executing in native code.
029 *
030 * Because the threading strategy within RVM is currently under
031 * revision, the logic here is also changing and somewhat "messy".
032 *
033 * @see CollectorThread
034 */
035 public class Handshake {
036
037 /***********************************************************************
038 *
039 * Class variables
040 */
041 public static final int verbose = 0;
042
043 /***********************************************************************
044 *
045 * Instance variables
046 */
047 private Monitor lock;
048 protected boolean requestFlag;
049 public int gcTrigger; // reason for this GC
050 private int collectorThreadsParked;
051
052 public Handshake() {
053 reset();
054 }
055 public void boot() {
056 lock = new Monitor();
057 }
058
059 /**
060 * Call this if you know that a GC request has already been made and you'd like
061 * to wait on that GC to finish - presumably because you're trying to allocate
062 * and cannot reasonably do so before GC is done. Note, there CANNOT be a
063 * GC safe point between when you realize that there is already a GC request and
064 * when you call this method!
065 */
066 @Unpreemptible
067 public void waitForGCToFinish() {
068 if (verbose >= 1) VM.sysWriteln("GC Message: Handshake.requestAndAwaitCompletion - yielding");
069 /* allow a gc thread to run */
070 RVMThread t=RVMThread.getCurrentThread();
071 t.assertAcceptableStates(RVMThread.IN_JAVA,
072 RVMThread.IN_JAVA_TO_BLOCK);
073 RVMThread.observeExecStatusAtSTW(t.getExecStatus());
074 t.block(RVMThread.gcBlockAdapter);
075 if (verbose >= 1) VM.sysWriteln("GC Message: Handshake.requestAndAwaitCompletion - mutator running");
076 }
077
078 /**
079 * Called by mutators to request a garbage collection and wait for
080 * it to complete.
081 *
082 * Waiting is actually just yielding the processor to schedule the
083 * collector thread, which will disable further thread switching on
084 * the processor until it has completed the collection.
085 */
086 @Unpreemptible
087 public void requestAndAwaitCompletion(int why) {
088 request(why);
089 waitForGCToFinish();
090 }
091
092 /**
093 * Called by mutators to request an asynchronous garbage collection.
094 * After initiating a GC (if one is not already initiated), the
095 * caller continues until it yields to the GC. It may thus make
096 * this call at an otherwise unsafe point.
097 */
098 @Unpreemptible("Change state of thread possibly context switching if generating exception")
099 public void requestAndContinue(int why) {
100 request(why);
101 }
102
103 @Unpreemptible
104 public void reset() {
105 if (lock!=null) {
106 lock.lockNoHandshake();
107 }
108 gcTrigger = Collection.UNKNOWN_GC_TRIGGER;
109 requestFlag = false;
110 if (lock!=null) {
111 lock.unlock();
112 }
113 }
114 @Unpreemptible
115 void parkCollectorThread() {
116 lock.lockNoHandshake();
117 collectorThreadsParked++;
118 lock.broadcast();
119 if (verbose>=1) VM.sysWriteln("GC Thread #",RVMThread.getCurrentThreadSlot()," parked.");
120 while (!requestFlag) {
121 if (verbose>=1) VM.sysWriteln("GC Thread #",RVMThread.getCurrentThreadSlot()," waiting for request.");
122 lock.waitWithHandshake();
123 }
124 if (verbose>=1) VM.sysWriteln("GC Thread #",RVMThread.getCurrentThreadSlot()," got request, unparking.");
125 collectorThreadsParked--;
126 lock.unlock();
127 }
128
129 /**
130 * Called by mutators to request a garbage collection.
131 *
132 * @return true if the completion flag is not already set.
133 */
134 @Unpreemptible("Becoming another thread interrupts the current thread, avoid preemption in the process")
135 private boolean request(int why) {
136 if (verbose>=1) VM.sysWriteln("Thread #",RVMThread.getCurrentThreadSlot()," is trying to make a GC request");
137 lock.lockNoHandshake();
138 if (verbose>=1) VM.sysWriteln("Thread #",RVMThread.getCurrentThreadSlot()," acquired the lock for making a GC request");
139 if (why > gcTrigger) gcTrigger = why;
140 if (requestFlag) {
141 if (verbose >= 1) {
142 VM.sysWriteln("GC Message: mutator: already in progress");
143 }
144 } else {
145 // first mutator initiates collection by making all gc threads
146 // runnable at high priority
147 if (verbose >= 1) VM.sysWriteln("GC Message: Handshake - mutator: initiating collection");
148
149 if (!RVMThread.threadingInitialized) {
150 VM.sysWrite("GC required before system fully initialized");
151 VM.sysWriteln("Specify larger than default heapsize on command line");
152 RVMThread.dumpStack();
153 VM.shutdown(VM.EXIT_STATUS_MISC_TROUBLE);
154 }
155
156 requestFlag = true;
157 Plan.setCollectionTriggered();
158 lock.broadcast();
159 }
160 lock.unlock();
161 return true;
162 }
163 }
164