/*
* 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.policy.Space;
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 {
/****************************************************************************
*
* Class variables
*/
/** lock protecting count of total cumulative pages committed */
private static final Lock classLock;
/** cumulative count of pages ever committed */
private static long cumulativeCommitted = 0;
/****************************************************************************
*
* Instance variables
*/
// page budgeting
/**
*
*/
protected int reserved;
protected int committed;
protected final boolean contiguous;
protected final boolean growable;
protected final Space space;
/** only for contiguous spaces */
protected Address start;
// locking
private final Lock lock;
// zeroing
protected boolean zeroNT;
protected boolean zeroConcurrent;
protected ConcurrentZeroingContext zeroingContext;
/****************************************************************************
*
* Initialization
*/
static {
classLock = VM.newLock("PageResource");
Options.protectOnRelease = new ProtectOnRelease();
}
/**
* @param space The space to which this resource is attached
* @param contiguous whether the space is contiguous or discontiguous
*/
private PageResource(Space space, boolean contiguous, boolean growable) {
this.contiguous = contiguous;
this.growable = growable;
this.space = space;
lock = VM.newLock(space.getName() + ".lock");
}
/**
* Constructor for discontiguous spaces.
*
* @param space The space to which this resource is attached
*/
PageResource(Space space) {
this(space, false, true);
}
/**
* Constructor for contiguous spaces.
*
* @param space The space to which this resource is attached
* @param start start address of the space
*/
PageResource(Space space, Address start) {
this(space, true, VM.HEAP_LAYOUT_64BIT);
this.start = start;
}
/**
* Return the number of available physical pages for this resource.
* This includes all pages currently unused by this resource's page
* cursor. If the resource is using discontiguous space it also includes
* currently unassigned discontiguous space.<p>
*
* 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();
/**
* @return The underlying space.
*/
public Space getSpace() {
return space;
}
/**
* 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 The actual number of pages reserved (including metadata, etc.)
*/
@Inline
public final int reservePages(int pages) {
lock();
pages = adjustForMetaData(pages);
reserved += pages;
unlock();
return pages;
}
/**
* Remove a request to the space.
*
* @param reservedPages The number of pages returned due to the request.
*/
@Inline
public final void clearRequest(int reservedPages) {
lock();
reserved -= reservedPages;
unlock();
}
/**
* Update the zeroing approach for this page resource.
*
* @param nontemporal whether to use non-temporal instructions for zeroing
* @param concurrent whether to do the zeroing concurrently
*/
@Interruptible
public void updateZeroingApproach(boolean nontemporal, boolean concurrent) {
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!concurrent || contiguous);
this.zeroNT = nontemporal;
this.zeroConcurrent = concurrent;
if (concurrent) {
this.zeroingContext = new ConcurrentZeroingContext(this);
VM.collection.spawnCollectorContext(zeroingContext);
}
}
/**
* Skip concurrent zeroing (fall back to bulk zeroing).
*/
public void skipConcurrentZeroing() {
// Clearing this flag has the effect of reverting back to bulk zeroing.
zeroConcurrent = false;
}
/**
* Trigger concurrent zeroing.
*/
public void triggerConcurrentZeroing() {
zeroConcurrent = true;
this.zeroingContext.trigger();
}
/**
* The entry point for the concurrent zeroing context.
*/
public void concurrentZeroing() {
VM.assertions.fail("This PageResource does not implement concurrent zeroing");
}
abstract Address allocPages(int reservedPages, int requiredPages, boolean zeroed);
/**
* 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 pagesReserved The number of pages reserved by the initial request
* @param pages The number of pages requested
* @param zeroed If true allocated pages are zeroed.
* @return The address of the first of <code>pages</code> pages, or
* zero on failure.
*/
@Inline
public final Address getNewPages(int pagesReserved, int pages, boolean zeroed) {
return allocPages(pagesReserved, pages, zeroed);
}
/**
* 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 discrepancy 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 reservedPages The number of pages initially reserved due to this request
* @param actualPages The number of pages actually allocated.
*/
protected void commitPages(int reservedPages, int actualPages) {
int delta = actualPages - reservedPages;
reserved += delta;
committed += actualPages;
if (VM.activePlan.isMutator()) {
// only count mutator pages
addToCommitted(actualPages);
}
}
/**
* 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 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 lock.
*/
protected final void lock() {
lock.acquire();
}
/**
* Release the lock.
*/
protected final void unlock() {
lock.release();
}
}