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