/*
 *  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.mmtk.plan.stickyms;

import org.mmtk.plan.TransitiveClosure;
import org.mmtk.plan.marksweep.MS;
import org.mmtk.policy.Space;
import org.mmtk.utility.Log;
import org.mmtk.utility.deque.SharedDeque;
import org.mmtk.utility.options.Options;
import org.mmtk.utility.sanitychecker.SanityChecker;
import org.mmtk.vm.VM;

import org.vmmagic.pragma.*;
import org.vmmagic.unboxed.ObjectReference;

/**
 * This class implements the global state of a simple sticky mark bits collector,
 * based a simple on mark-sweep collector.  The sticky mark bits algorithm is
 * due to Demmers et al. (http://doi.acm.org/10.1145/96709.96735), and allows
 * generational collection to be performed in a non-moving heap by overloading
 * the role of mark bits to also indicate whether an object is new (nursery) or
 * not.  Thus nursery objects are identified by a bit in their header, not by
 * where they lie within the address space.  While Demmers et al. did their work
 * in a conservative collector, here we have an exact collector, so we can use
 * a regular write barrier, and don't need to use page protection etc.
 *
 * All plans make a clear distinction between <i>global</i> and
 * <i>thread-local</i> activities, and divides global and local state
 * into separate class hierarchies.  Global activities must be
 * synchronized, whereas no synchronization is required for
 * thread-local activities.  There is a single instance of Plan (or the
 * appropriate sub-class), and a 1:1 mapping of PlanLocal to "kernel
 * threads" (aka CPUs or in Jikes RVM, Processors).  Thus instance
 * methods of PlanLocal allow fast, unsychronized access to functions such as
 * allocation and collection.
 *
 * The global instance defines and manages static resources
 * (such as memory and virtual memory resources).  This mapping of threads to
 * instances is crucial to understanding the correctness and
 * performance properties of MMTk plans.
 */
@Uninterruptible
public class StickyMS extends MS {

  /****************************************************************************
   * Constants
   */
  /** If true, then new PLOS objects are collected at each nursery GC */
  static final boolean NURSERY_COLLECT_PLOS = true;
  /** If true then we only do full heap GCs---so we're like MarkSweep (+ write barrier) */
  static final boolean MAJOR_GC_ONLY = false;

  /****************************************************************************
   * Class variables
   */

  public static int SCAN_NURSERY = 1;

  /****************************************************************************
   * Instance variables
   */
  /* status fields */
  /** will the next collection collect the whole heap? */
  public boolean nextGCWholeHeap = false;
  /** will this collection collect the whole heap */
  public boolean collectWholeHeap = nextGCWholeHeap;

  /* Remset pool */
  public final SharedDeque modPool = new SharedDeque("msgen mod objects", metaDataSpace, 1);

  /****************************************************************************
   * Static initialization
   */
  {
    msSpace.isAgeSegregatedSpace();  /* this space is to be collected generationally */
  }

  /*****************************************************************************
   *
   * Collection
   */

  /**
   * A user-triggered GC has been initiated.
   */
  public void userTriggeredGC() {
    nextGCWholeHeap |= Options.fullHeapSystemGC.getValue();
  }

  /**
   * Force the next collection to be full heap.
   */
  @Override
  public void forceFullHeapCollection() {
    nextGCWholeHeap = true;
  }

  /**
   * Perform a (global) collection phase.
   *
   * @param phaseId Collection phase to execute.
   */
  @Inline
  @Override
  public final void collectionPhase(short phaseId) {

    if (phaseId == INITIATE) {
      collectWholeHeap = MAJOR_GC_ONLY || emergencyCollection || nextGCWholeHeap;
      nextGCWholeHeap = false;
      super.collectionPhase(phaseId);
      return;
    }

    if (!collectWholeHeap) {
      if (phaseId == PREPARE) {
        if (NURSERY_COLLECT_PLOS)
          ploSpace.prepare(false);
        msTrace.prepare();
        msSpace.prepare(false);
        return;
      }

      if (phaseId == RELEASE) {
        msTrace.release();
        msSpace.release();
        modPool.reset();
        if (NURSERY_COLLECT_PLOS)
          ploSpace.release(collectWholeHeap);
        nextGCWholeHeap = (getPagesAvail() < Options.nurserySize.getMinNursery());
        return;
      }
    }

    super.collectionPhase(phaseId);
  }

  /*****************************************************************************
   *
   * Accounting
   */

  /**
   * Print pre-collection statistics. In this class we prefix the output
   * indicating whether the collection was full heap or not.
   */
  @Override
  public void printPreStats() {
    if ((Options.verbose.getValue() >= 1) && (collectWholeHeap))
      Log.write("[Full heap]");
    super.printPreStats();
  }

  /**
   * @return Is current GC only collecting objects allocated since last GC.
   */
  public final boolean isCurrentGCNursery() {
    return !collectWholeHeap;
  }

  /**
   * @return Is last GC a full collection?
   */
  public final boolean isLastGCFull() {
    return collectWholeHeap;
  }

  /**
   * Return the expected reference count. For non-reference counting
   * collectors this becomes a true/false relationship.
   * @param object The object to check.
   * @param sanityRootRC The number of root references to the object.
   *
   * @return The expected (root excluded) reference count.
   */
  public int sanityExpectedRC(ObjectReference object, int sanityRootRC) {
    Space space = Space.getSpaceForObject(object);

    // Immortal spaces
    if (space == StickyMS.immortalSpace || space == StickyMS.vmSpace) {
      return space.isReachable(object) ? SanityChecker.ALIVE : SanityChecker.DEAD;
    }

    // Mature space (nursery collection)
    if (VM.activePlan.global().isCurrentGCNursery() && space != StickyMS.msSpace) {
      return SanityChecker.UNSURE;
    }

    return super.sanityExpectedRC(object, sanityRootRC);
  }

  /**
   * Register specialized methods.
   */
  @Interruptible
  protected void registerSpecializedMethods() {
    TransitiveClosure.registerSpecializedScan(SCAN_NURSERY, StickyMSNurseryTraceLocal.class);
    super.registerSpecializedMethods();
  }
}
