blob: 2f4a47bc98c0d9d7ec61cbceb4a74ea1a5c5255d [file] [log] [blame]
/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* 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/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.mmtk.utility.heap;
import org.mmtk.plan.Plan;
import org.mmtk.policy.Space;
import org.mmtk.utility.Constants;
import org.mmtk.utility.options.ProtectOnRelease;
import org.mmtk.utility.options.Options;
import org.mmtk.vm.Lock;
import org.mmtk.vm.VM;
import org.vmmagic.pragma.*;
import org.vmmagic.unboxed.*;
/**
* This class manages the allocation of pages for a space. When a
* page is requested by the space both a page budget and the use of
* virtual address space are checked. If the request for space can't
* be satisfied (for either reason) a GC may be triggered.<p>
*
* This class is abstract, and is subclassed with monotone and
* freelist variants, which reflect monotonic and ad hoc space usage
* respectively. Monotonic use is easier to manage, but is obviously
* more restrictive (useful for copying collectors which allocate
* monotonically before freeing the entire space and starting over).
*/
@Uninterruptible
public abstract class PageResource implements Constants {
/****************************************************************************
*
* Class variables
*/
protected static final boolean ZERO_ON_RELEASE = false; // debugging
private static final Lock classLock;
private static long cumulativeCommitted = 0;
/****************************************************************************
*
* Instance variables
*/
// page budgeting
protected int reserved;
protected int committed;
protected int required;
private final int pageBudget;
protected final boolean contiguous;
protected final Space space;
protected Address start; // only for contiguous
// locking
private final Lock gcLock; // used during GC
private final Lock mutatorLock; // used by mutators
/****************************************************************************
*
* Initialization
*/
static {
classLock = VM.newLock("PageResource");
Options.protectOnRelease = new ProtectOnRelease();
}
/**
* Constructor
*
* @param pageBudget The budget of pages available to this memory
* manager before it must poll the collector.
* @param space The space to which this resource is attached
*/
private PageResource(int pageBudget, Space space, boolean contiguous) {
this.pageBudget = pageBudget;
this.contiguous = contiguous;
this.space = space;
gcLock = VM.newLock(space.getName() + ".gcLock");
mutatorLock = VM.newLock(space.getName() + ".mutatorLock");
}
/**
* Constructor for discontiguous spaces
*
* @param pageBudget The budget of pages available to this memory
* manager before it must poll the collector.
* @param space The space to which this resource is attached
*/
PageResource(int pageBudget, Space space) {
this(pageBudget, space, false);
}
/**
* Constructor for contiguous spaces
*
* @param pageBudget The budget of pages available to this memory
* manager before it must poll the collector.
* @param space The space to which this resource is attached
*/
PageResource(int pageBudget, Space space, Address start) {
this(pageBudget, space, true);
this.start = start;
}
/**
* Return the number of available physical pages for this resource.
*
* Note: This just considers physical pages (ie virtual memory pages
* allocated for use by this resource). This calculation is orthogonal
* to and does not consider any restrictions on the number of pages
* this resource may actually use at any time (ie the number of
* committed and reserved pages).<p>
*
* Note: The calculation is made on the assumption that all space that
* could be assigned to this resource would be assigned to this resource
* (ie the unused discontiguous space could just as likely be assigned
* to another competing resource).
*
* @return The number of available physical pages for this resource.
*/
public abstract int getAvailablePhysicalPages();
/**
* Reserve pages.<p>
*
* The role of reserving pages is that it allows the request to be
* noted as pending (the difference between committed and reserved
* indicates pending requests). If the request would exceed the
* page budget then the caller must poll in case a GC is necessary.
*
* @param pages The number of pages requested
* @return True if the page budget could satisfy the request.
*/
@Inline
public final boolean reservePages(int pages) {
lock();
required += adjustForMetaData(pages);
reserved = committed + required;
boolean satisfied = reserved <= pageBudget;
unlock();
return satisfied;
}
/**
* Remove a request to the space.
*
* @param pages The number of pages in the request.
*/
@Inline
public final void clearRequest(int pages) {
lock();
required -= adjustForMetaData(pages);
unlock();
}
/**
* Reserve pages unconditionally.<p>
*
* An example of where this is useful is in cases where it is
* desirable to put some space aside as head-room. By
* unconditionally reserving the pages, the pages are counted
* against the collectors budget. When the space is actually
* needed, the pages can be unconditionally released, freeing
* the pages for other purposes.
*
* @param pages The number of pages to be unconditionally
* reserved.
*/
public final void unconditionallyReservePages(int pages) {
lock();
committed += pages;
reserved += pages;
unlock();
}
/**
* Release pages unconditionally.<p>
*
* This call allows pages to be unconditionally removed from
* the collectors page budget.
*
* @see #unconditionallyReservePages
* @param pages The number of pages to be unconditionally
* released.
*/
public final void unconditionallyReleasePages(int pages) {
lock();
committed -= pages;
reserved -= pages;
unlock();
}
abstract Address allocPages(int pages);
/**
* Adjust a page request to include metadata requirements for a request
* of the given size. This must be a pure function, that is it does not
* depend on the state of the PageResource.
*
* @param pages The size of the pending allocation in pages
* @return The number of required pages, inclusive of any metadata
*/
public abstract int adjustForMetaData(int pages);
/**
* Allocate pages in virtual memory, returning zero on failure.<p>
*
* If the request cannot be satisfied, zero is returned and it
* falls to the caller to trigger the GC.
*
* Call <code>allocPages</code> (subclass) to find the pages in
* virtual memory. If successful then commit the pending page
* request and return the address of the first page.
*
* @param pages The number of pages requested
* @return The address of the first of <code>pages</code> pages, or
* zero on failure.
*/
@Inline
public final Address getNewPages(int pages) {
return allocPages(pages);
}
/**
* Commit pages to the page budget. This is called after
* successfully determining that the request can be satisfied by
* both the page budget and virtual memory. This simply accounts
* for the descrepency between <code>committed</code> and
* <code>reserved</code> while the request was pending.
*
* This *MUST* be called by each PageResource during the
* allocPages, and the caller must hold the lock.
*
* @param requestedPages The number of pages from this request
* @param totalPages The number of pages
*/
protected void commitPages(int requestedPages, int totalPages) {
int predictedPages = adjustForMetaData(requestedPages);
int delta = totalPages - predictedPages;
required -= predictedPages;
reserved += delta;
committed += totalPages;
if (!Plan.gcInProgress())
addToCommitted(totalPages); // only count mutator pages
}
/**
* Return the number of reserved pages
*
* @return The number of reserved pages.
*/
public final int reservedPages() { return reserved; }
/**
* Return the number of committed pages
*
* @return The number of committed pages.
*/
public final int committedPages() { return committed; }
/**
* Return the number of required pages
*
* @return The number of required pages.
*/
public final int requiredPages() { return required; }
/**
* Return the cumulative number of committed pages
*
* @return The cumulative number of committed pages.
*/
public static long cumulativeCommittedPages() { return cumulativeCommitted; }
/**
* Add to the total cumulative committed page count.
*
* @param pages The number of pages to be added.
*/
private static void addToCommitted(int pages) {
classLock.acquire();
cumulativeCommitted += pages;
classLock.release();
}
/**
* Acquire the appropriate lock depending on whether the context is
* GC or mutator.
*/
protected final void lock() {
if (Plan.gcInProgress())
gcLock.acquire();
else
mutatorLock.acquire();
}
/**
* Release the appropriate lock depending on whether the context is
* GC or mutator.
*/
protected final void unlock() {
if (Plan.gcInProgress())
gcLock.release();
else
mutatorLock.release();
}
}