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 */
013package org.mmtk.plan;
014
015import org.mmtk.utility.Log;
016import org.mmtk.utility.heap.HeapGrowthManager;
017import org.mmtk.utility.options.Options;
018import org.mmtk.vm.Monitor;
019import org.mmtk.vm.VM;
020
021import org.vmmagic.pragma.*;
022
023@Uninterruptible
024public class ControllerCollectorContext extends CollectorContext {
025
026  /** The lock to use to manage collection */
027  private Monitor lock;
028
029  /** The set of worker threads to use */
030  private final ParallelCollectorGroup workers;
031
032  /** Flag used to control the 'race to request' */
033  private boolean requestFlag;
034
035  /** The current request index */
036  private int requestCount;
037
038  /** The request index that was last completed */
039  private int lastRequestCount = -1;
040
041  /** Is there concurrent collection activity */
042  private boolean concurrentCollection = false;
043
044  /**
045   * Create a controller context.
046   *
047   * @param workers The worker group to use for collection.
048   */
049  public ControllerCollectorContext(ParallelCollectorGroup workers) {
050    this.workers = workers;
051  }
052
053  @Override
054  @Interruptible
055  public void initCollector(int id) {
056    super.initCollector(id);
057    lock = VM.newHeavyCondLock("CollectorControlLock");
058  }
059
060  /**
061   * Main execution loop.
062   */
063  @Override
064  @Unpreemptible
065  public void run() {
066    while (true) {
067      // Wait for a collection request.
068      if (Options.verbose.getValue() >= 5) Log.writeln("[STWController: Waiting for request...]");
069      waitForRequest();
070      if (Options.verbose.getValue() >= 5) Log.writeln("[STWController: Request recieved.]");
071
072      // The start time.
073      long startTime = VM.statistics.nanoTime();
074
075      if (concurrentCollection) {
076        if (Options.verbose.getValue() >= 5) Log.writeln("[STWController: Stopping concurrent collectors...]");
077        Plan.concurrentWorkers.abortCycle();
078        Plan.concurrentWorkers.waitForCycle();
079        Phase.clearConcurrentPhase();
080        // Collector must re-request concurrent collection in this case.
081        concurrentCollection = false;
082      }
083
084      // Stop all mutator threads
085      if (Options.verbose.getValue() >= 5) Log.writeln("[STWController: Stopping the world...]");
086      VM.collection.stopAllMutators();
087
088      // Was this user triggered?
089      boolean userTriggeredCollection = Plan.isUserTriggeredCollection();
090      boolean internalTriggeredCollection = Plan.isInternalTriggeredCollection();
091
092      // Clear the request
093      clearRequest();
094
095      // Trigger GC.
096      if (Options.verbose.getValue() >= 5) Log.writeln("[STWController: Triggering worker threads...]");
097      workers.triggerCycle();
098
099      // Wait for GC threads to complete.
100      workers.waitForCycle();
101      if (Options.verbose.getValue() >= 5) Log.writeln("[STWController: Worker threads complete!]");
102
103      // Heap growth logic
104      long elapsedTime = VM.statistics.nanoTime() - startTime;
105      HeapGrowthManager.recordGCTime(VM.statistics.nanosToMillis(elapsedTime));
106      if (VM.activePlan.global().lastCollectionFullHeap() && !internalTriggeredCollection) {
107        if (Options.variableSizeHeap.getValue() && !userTriggeredCollection) {
108          // Don't consider changing the heap size if the application triggered the collection
109          if (Options.verbose.getValue() >= 5) Log.writeln("[STWController: Considering heap size.]");
110          HeapGrowthManager.considerHeapSize();
111        }
112        HeapGrowthManager.reset();
113      }
114
115      // Reset the triggering information.
116      Plan.resetCollectionTrigger();
117
118      // Resume all mutators
119      if (Options.verbose.getValue() >= 5) Log.writeln("[STWController: Resuming mutators...]");
120      VM.collection.resumeAllMutators();
121
122      // Start threads that will perform concurrent collection work alongside mutators.
123      if (concurrentCollection) {
124        if (Options.verbose.getValue() >= 5) Log.writeln("[STWController: Triggering concurrent collectors...]");
125        Plan.concurrentWorkers.triggerCycle();
126      }
127    }
128  }
129
130  /**
131   * Request that concurrent collection is performed after this stop-the-world increment.
132   */
133  public void requestConcurrentCollection() {
134    concurrentCollection = true;
135  }
136
137  /**
138   * Request a collection.
139   */
140  public void request() {
141    if (requestFlag) {
142      return;
143    }
144    lock.lock();
145    if (!requestFlag) {
146      requestFlag = true;
147      requestCount++;
148      lock.broadcast();
149    }
150    lock.unlock();
151  }
152
153  /**
154   * Clear the collection request, making future requests incur an
155   * additional collection cycle.
156   */
157  private void clearRequest() {
158    lock.lock();
159    requestFlag = false;
160    lock.unlock();
161  }
162
163  /**
164   * Wait until a request is received.
165   */
166  private void waitForRequest() {
167    lock.lock();
168    lastRequestCount++;
169    while (lastRequestCount == requestCount) {
170      lock.await();
171    }
172    lock.unlock();
173  }
174}