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.mmtk;
014
015 import org.mmtk.plan.Plan;
016 import org.mmtk.plan.CollectorContext;
017 import org.mmtk.plan.MutatorContext;
018 import org.mmtk.utility.options.Options;
019
020 import org.jikesrvm.ArchitectureSpecific;
021 import org.jikesrvm.VM;
022 import org.jikesrvm.classloader.Atom;
023 import org.jikesrvm.classloader.RVMMethod;
024 import org.jikesrvm.compilers.common.CompiledMethod;
025 import org.jikesrvm.compilers.common.CompiledMethods;
026 import org.jikesrvm.mm.mminterface.Selected;
027 import org.jikesrvm.mm.mminterface.CollectorThread;
028 import org.jikesrvm.runtime.Magic;
029 import org.jikesrvm.scheduler.RVMThread;
030 import org.jikesrvm.scheduler.FinalizerThread;
031 import org.vmmagic.pragma.Inline;
032 import org.vmmagic.pragma.Interruptible;
033 import org.vmmagic.pragma.Uninterruptible;
034 import org.vmmagic.pragma.UninterruptibleNoWarn;
035 import org.vmmagic.pragma.Unpreemptible;
036 import org.vmmagic.unboxed.Address;
037
038 @Uninterruptible
039 public class Collection extends org.mmtk.vm.Collection implements org.mmtk.utility.Constants,
040 org.jikesrvm.Constants {
041
042 /****************************************************************************
043 *
044 * Class variables
045 */
046
047 /** The fully qualified name of the collector thread. */
048 private static Atom collectorThreadAtom;
049 /** The string "run". */
050 private static Atom runAtom;
051
052 /***********************************************************************
053 *
054 * Initialization
055 */
056
057 /**
058 * Initialization that occurs at <i>build</i> time. The values of
059 * statics at the completion of this routine will be reflected in
060 * the boot image. Any objects referenced by those statics will be
061 * transitively included in the boot image.
062 *
063 * This is called from MemoryManager.
064 */
065 @Interruptible
066 public static void init() {
067 collectorThreadAtom = Atom.findOrCreateAsciiAtom("Lorg/jikesrvm/mm/mminterface/CollectorThread;");
068 runAtom = Atom.findOrCreateAsciiAtom("run");
069 }
070
071 /**
072 * Triggers a collection.
073 *
074 * @param why the reason why a collection was triggered. 0 to
075 * <code>TRIGGER_REASONS - 1</code>.
076 */
077 @Unpreemptible("Becoming another thread interrupts the current thread, avoid preemption in the process")
078 public final void triggerCollection(int why) {
079 triggerCollectionStatic(why);
080 }
081
082 /**
083 * Joins a collection.
084 */
085 @Unpreemptible("Becoming another thread interrupts the current thread, avoid preemption in the process")
086 public final void joinCollection() {
087 if (Options.verbose.getValue() >= 4) {
088 VM.sysWriteln("Entered Collection.joinCollection(). Stack:");
089 RVMThread.dumpStack();
090 }
091
092 while (Plan.isCollectionTriggered()) {
093 CollectorThread.handshake.waitForGCToFinish();
094 }
095 checkForOutOfMemoryError(true);
096 }
097
098 /**
099 * Triggers a collection.
100 *
101 * @param why the reason why a collection was triggered. 0 to
102 * <code>TRIGGER_REASONS - 1</code>.
103 */
104 @Unpreemptible("Change state of thread possibly context switching if generating exception")
105 public static void triggerCollectionStatic(int why) {
106 if (VM.VerifyAssertions) VM._assert((why >= 0) && (why < TRIGGER_REASONS));
107
108 if (Options.verbose.getValue() >= 4) {
109 VM.sysWriteln("Entered Collection.triggerCollection(). Stack:");
110 RVMThread.dumpStack();
111 }
112
113 checkForOutOfMemoryError(false);
114
115 if (why == EXTERNAL_GC_TRIGGER) {
116 if (Options.verbose.getValue() == 1 || Options.verbose.getValue() == 2)
117 VM.sysWrite("[Forced GC]");
118 } else if (why == INTERNAL_PHASE_GC_TRIGGER) {
119 if (Options.verbose.getValue() == 1 || Options.verbose.getValue() == 2)
120 VM.sysWrite("[Phase GC]");
121 } else {
122 RVMThread.getCurrentThread().reportCollectionAttempt();
123 }
124
125 CollectorThread.collect(CollectorThread.handshake, why);
126 checkForOutOfMemoryError(true);
127
128 if (Options.verbose.getValue() >= 4) {
129 VM.sysWriteln("Leaving Collection.triggerCollection().");
130 }
131 }
132
133 /**
134 * Check if there is an out of memory error waiting.
135 */
136 @Inline
137 @Unpreemptible("Exceptions may possibly cause yields")
138 private static void checkForOutOfMemoryError(boolean afterCollection) {
139 RVMThread myThread = RVMThread.getCurrentThread();
140 OutOfMemoryError oome = myThread.getOutOfMemoryError();
141 if (oome != null && (!afterCollection || !myThread.physicalAllocationFailed())) {
142 if (Options.verbose.getValue() >= 4) {
143 VM.sysWriteln("Throwing OutOfMemoryError in Collection.triggerCollection().");
144 }
145 myThread.clearOutOfMemoryError();
146 myThread.resetCollectionAttempts();
147 throw oome;
148 }
149 }
150
151 /**
152 * The maximum number collection attempts across threads.
153 */
154 public int maximumCollectionAttempt() {
155 int max = 1;
156 RVMThread.acctLock.lockNoHandshake();
157 for(int t=0; t < RVMThread.numThreads; t++) {
158 RVMThread thread = RVMThread.threads[t];
159 int current = thread.getCollectionAttempt();
160 if (current > max) max = current;
161 }
162 RVMThread.acctLock.unlock();
163 return max + CollectorThread.collectionAttemptBase;
164 }
165
166 /**
167 * Report that the the physical allocation has succeeded.
168 */
169 public void reportAllocationSuccess() {
170 RVMThread myThread = RVMThread.getCurrentThread();
171 myThread.clearOutOfMemoryError();
172 myThread.resetCollectionAttempts();
173 myThread.clearPhysicalAllocationFailed();
174 }
175
176 /**
177 * Report that a physical allocation has failed.
178 */
179 public void reportPhysicalAllocationFailed() {
180 RVMThread.getCurrentThread().setPhysicalAllocationFailed();
181 }
182
183 /**
184 * Does the VM consider this an emergency allocation, where the normal
185 * heap size rules can be ignored.
186 */
187 public boolean isEmergencyAllocation() {
188 return RVMThread.getCurrentThread().emergencyAllocation();
189 }
190
191 /**
192 * Trigger an asynchronous collection, checking for memory
193 * exhaustion first.
194 */
195 @Unpreemptible("Becoming another thread interrupts the current thread, avoid preemption in the process")
196 public final void triggerAsyncCollection(int why) {
197 if (Options.verbose.getValue() >= 1) {
198 if (why == INTERNAL_PHASE_GC_TRIGGER) {
199 VM.sysWrite("[Async-Phase GC]");
200 } else {
201 VM.sysWrite("[Async GC]");
202 }
203 }
204 CollectorThread.asyncCollect(CollectorThread.handshake, why);
205 }
206
207 /**
208 * Determine whether a collection cycle has fully completed (this is
209 * used to ensure a GC is not in the process of completing, to
210 * avoid, for example, an async GC being triggered on the switch
211 * from GC to mutator thread before all GC threads have switched.
212 *
213 * @return True if GC is not in progress.
214 */
215 @Uninterruptible
216 public final boolean noThreadsInGC() {
217 return CollectorThread.noThreadsInGC();
218 }
219
220 /**
221 * Prepare a mutator for a collection.
222 *
223 * @param m the mutator to prepare
224 */
225 public final void prepareMutator(MutatorContext m) {
226 /*
227 * The collector threads of processors currently running threads
228 * off in JNI-land cannot run.
229 */
230 RVMThread t = ((Selected.Mutator) m).getThread();
231 t.monitor().lockNoHandshake();
232 // are these the only unexpected states?
233 t.assertUnacceptableStates(RVMThread.IN_JNI,RVMThread.IN_NATIVE);
234 int execStatus = t.getExecStatus();
235 // these next assertions are not redundant given the ability of the
236 // states to change asynchronously, even when we're holding the lock, since
237 // the thread may change its own state. of course that shouldn't happen,
238 // but having more assertions never hurts...
239 if (VM.VerifyAssertions) VM._assert(execStatus != RVMThread.IN_JNI);
240 if (VM.VerifyAssertions) VM._assert(execStatus != RVMThread.IN_NATIVE);
241 if (execStatus == RVMThread.BLOCKED_IN_JNI) {
242 if (false) {
243 VM.sysWriteln("for thread #",t.getThreadSlot()," setting up JNI stack scan");
244 VM.sysWriteln("thread #",t.getThreadSlot()," has top java fp = ",t.jniEnv.topJavaFP());
245 }
246
247 /* thread is blocked in C for this GC.
248 Its stack needs to be scanned, starting from the "top" java
249 frame, which has been saved in the running threads JNIEnv. Put
250 the saved frame pointer into the threads saved context regs,
251 which is where the stack scan starts. */
252 t.contextRegisters.setInnermost(Address.zero(), t.jniEnv.topJavaFP());
253 }
254 t.monitor().unlock();
255 }
256
257 /**
258 * Prepare a collector for a collection.
259 *
260 * @param c the collector to prepare
261 */
262 public final void prepareCollector(CollectorContext c) {
263 RVMThread t = ((Selected.Collector) c).getThread();
264 if (false) {
265 VM.sysWriteln("prepareCollector called for ",t.getThreadSlot());
266 }
267 int execStatus = t.getExecStatus();
268 if (VM.VerifyAssertions) VM._assert(execStatus == RVMThread.IN_JAVA);
269 Address fp = Magic.getFramePointer();
270 while (true) {
271 Address caller_ip = Magic.getReturnAddress(fp);
272 Address caller_fp = Magic.getCallerFramePointer(fp);
273 if (Magic.getCallerFramePointer(caller_fp).EQ(ArchitectureSpecific.StackframeLayoutConstants.STACKFRAME_SENTINEL_FP))
274 VM.sysFail("prepareMutator (participating): Could not locate CollectorThread.run");
275 int compiledMethodId = Magic.getCompiledMethodID(caller_fp);
276 CompiledMethod compiledMethod = CompiledMethods.getCompiledMethod(compiledMethodId);
277 RVMMethod method = compiledMethod.getMethod();
278 Atom cls = method.getDeclaringClass().getDescriptor();
279 Atom name = method.getName();
280 if (name == runAtom && cls == collectorThreadAtom) {
281 if (false) {
282 VM.sysWriteln("preparing GC thread ",RVMThread.getCurrentThreadSlot()," with ip = ",caller_ip);
283 VM.sysWriteln("preparing GC thread ",RVMThread.getCurrentThreadSlot()," with fp = ",caller_fp);
284 }
285 t.contextRegisters.setInnermost(caller_ip, caller_fp);
286 break;
287 }
288 fp = caller_fp;
289 }
290 }
291
292 /**
293 * Rendezvous with all other processors, returning the rank
294 * (that is, the order this processor arrived at the barrier).
295 */
296 public final int rendezvous(int where) {
297 return CollectorThread.gcBarrier.rendezvous(where);
298 }
299
300 // REVIEW: what are the semantics of this method in a concurrent collector?
301 /** @return The number of active collector threads */
302 public final int activeGCThreads() {
303 return CollectorThread.numCollectors();
304 }
305
306 /**
307 * @return The ordinal ID of the running collector thread w.r.t.
308 * the set of active collector threads (zero based)
309 */
310 public final int activeGCThreadOrdinal() {
311 return Magic.threadAsCollectorThread(RVMThread.getCurrentThread()).getGCOrdinal() - CollectorThread.GC_ORDINAL_BASE;
312 }
313
314 private static RVMThread.SoftHandshakeVisitor mutatorFlushVisitor =
315 new RVMThread.SoftHandshakeVisitor() {
316 @Uninterruptible
317 public boolean checkAndSignal(RVMThread t) {
318 // PNT: maybe we should return false if it's a GC thread?
319 t.flushRequested=true;
320 return true;
321 }
322 @Uninterruptible
323 public void notifyStuckInNative(RVMThread t) {
324 t.flush();
325 t.flushRequested=false;
326 }
327 };
328
329 /**
330 * Request each mutator flush remembered sets. This method
331 * will trigger the flush and then yield until all processors have
332 * flushed.
333 */
334 @UninterruptibleNoWarn("This method is really unpreemptible, since it involves blocking")
335 public void requestMutatorFlush() {
336 Selected.Mutator.get().flush();
337 RVMThread.softHandshake(mutatorFlushVisitor);
338 }
339
340 /***********************************************************************
341 *
342 * Finalizers
343 */
344
345 /**
346 * Schedule the finalizerThread, if there are objects to be
347 * finalized and the finalizerThread is on its queue (ie. currently
348 * idle). Should be called at the end of GC after moveToFinalizable
349 * has been called, and before mutators are allowed to run.
350 */
351 @Uninterruptible
352 public static void scheduleFinalizerThread() {
353 int finalizedCount = FinalizableProcessor.countReadyForFinalize();
354 if (finalizedCount > 0) {
355 FinalizerThread.schedule();
356 }
357 }
358 }
359