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.utility.heap;
014
015import org.mmtk.policy.Space;
016import org.mmtk.utility.options.ProtectOnRelease;
017import org.mmtk.utility.options.Options;
018
019import org.mmtk.vm.Lock;
020import org.mmtk.vm.VM;
021
022import org.vmmagic.pragma.*;
023import org.vmmagic.unboxed.*;
024
025/**
026 * This class manages the allocation of pages for a space.  When a
027 * page is requested by the space both a page budget and the use of
028 * virtual address space are checked.  If the request for space can't
029 * be satisfied (for either reason) a GC may be triggered.<p>
030 *
031 * This class is abstract, and is subclassed with monotone and
032 * freelist variants, which reflect monotonic and ad hoc space usage
033 * respectively.  Monotonic use is easier to manage, but is obviously
034 * more restrictive (useful for copying collectors which allocate
035 * monotonically before freeing the entire space and starting over).
036 */
037@Uninterruptible
038public abstract class PageResource {
039
040  /****************************************************************************
041   *
042   * Class variables
043   */
044
045  /** lock protecting count of total cumulative pages committed */
046  private static final Lock classLock;
047  /** cumulative count of pages ever committed */
048  private static long cumulativeCommitted = 0;
049
050
051  /****************************************************************************
052   *
053   * Instance variables
054   */
055
056  // page budgeting
057
058  /**
059   *
060   */
061  protected int reserved;
062  protected int committed;
063
064  protected final boolean contiguous;
065  protected final Space space;
066
067  /** only for contiguous spaces */
068  protected Address start;
069
070  // locking
071  private final Lock lock;
072
073  // zeroing
074  protected boolean zeroNT;
075  protected boolean zeroConcurrent;
076  protected ConcurrentZeroingContext zeroingContext;
077
078  /****************************************************************************
079   *
080   * Initialization
081   */
082  static {
083    classLock = VM.newLock("PageResource");
084    Options.protectOnRelease = new ProtectOnRelease();
085  }
086
087  /**
088   * @param space The space to which this resource is attached
089   * @param contiguous whether the space is contiguous or discontiguous
090   */
091  private PageResource(Space space, boolean contiguous) {
092    this.contiguous = contiguous;
093    this.space = space;
094    lock = VM.newLock(space.getName() + ".lock");
095  }
096
097  /**
098   * Constructor for discontiguous spaces.
099   *
100   * @param space The space to which this resource is attached
101   */
102  PageResource(Space space) {
103    this(space, false);
104  }
105
106  /**
107   * Constructor for contiguous spaces.
108   *
109   * @param space The space to which this resource is attached
110   * @param start start address of the space
111   */
112  PageResource(Space space, Address start) {
113    this(space, true);
114    this.start = start;
115  }
116
117  /**
118   * Return the number of available physical pages for this resource.
119   * This includes all pages currently unused by this resource's page
120   * cursor. If the resource is using discontiguous space it also includes
121   * currently unassigned discontiguous space.<p>
122   *
123   * Note: This just considers physical pages (ie virtual memory pages
124   * allocated for use by this resource). This calculation is orthogonal
125   * to and does not consider any restrictions on the number of pages
126   * this resource may actually use at any time (ie the number of
127   * committed and reserved pages).<p>
128   *
129   * Note: The calculation is made on the assumption that all space that
130   * could be assigned to this resource would be assigned to this resource
131   * (ie the unused discontiguous space could just as likely be assigned
132   * to another competing resource).
133   *
134   * @return The number of available physical pages for this resource.
135   */
136   public abstract int getAvailablePhysicalPages();
137
138  /**
139   * Reserve pages.<p>
140   *
141   * The role of reserving pages is that it allows the request to be
142   * noted as pending (the difference between committed and reserved
143   * indicates pending requests).  If the request would exceed the
144   * page budget then the caller must poll in case a GC is necessary.
145   *
146   * @param pages The number of pages requested
147   * @return The actual number of pages reserved (including metadata, etc.)
148   */
149  @Inline
150  public final int reservePages(int pages) {
151    lock();
152    pages = adjustForMetaData(pages);
153    reserved += pages;
154    unlock();
155    return pages;
156  }
157
158  /**
159   * Remove a request to the space.
160   *
161   * @param reservedPages The number of pages returned due to the request.
162   */
163  @Inline
164  public final void clearRequest(int reservedPages) {
165    lock();
166    reserved -= reservedPages;
167    unlock();
168  }
169
170  /**
171   * Update the zeroing approach for this page resource.
172   *
173   * @param nontemporal whether to use non-temporal instructions for zeroing
174   * @param concurrent whether to do the zeroing concurrently
175   */
176  @Interruptible
177  public void updateZeroingApproach(boolean nontemporal, boolean concurrent) {
178    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!concurrent || contiguous);
179    this.zeroNT = nontemporal;
180    this.zeroConcurrent = concurrent;
181    if (concurrent) {
182      this.zeroingContext = new ConcurrentZeroingContext(this);
183      VM.collection.spawnCollectorContext(zeroingContext);
184    }
185  }
186
187  /**
188   * Skip concurrent zeroing (fall back to bulk zeroing).
189   */
190  public void skipConcurrentZeroing() {
191    // Clearing this flag has the effect of reverting back to bulk zeroing.
192    zeroConcurrent = false;
193  }
194
195  /**
196   * Trigger concurrent zeroing.
197   */
198  public void triggerConcurrentZeroing() {
199    zeroConcurrent = true;
200    this.zeroingContext.trigger();
201  }
202
203  /**
204   * The entry point for the concurrent zeroing context.
205   */
206  public void concurrentZeroing() {
207    VM.assertions.fail("This PageResource does not implement concurrent zeroing");
208  }
209
210  abstract Address allocPages(int reservedPages, int requiredPages, boolean zeroed);
211
212  /**
213   * Adjust a page request to include metadata requirements for a request
214   * of the given size. This must be a pure function, that is it does not
215   * depend on the state of the PageResource.
216   *
217   * @param pages The size of the pending allocation in pages
218   * @return The number of required pages, inclusive of any metadata
219   */
220  public abstract int adjustForMetaData(int pages);
221
222  /**
223   * Allocate pages in virtual memory, returning zero on failure.<p>
224   *
225   * If the request cannot be satisfied, zero is returned and it
226   * falls to the caller to trigger the GC.
227   *
228   * Call <code>allocPages</code> (subclass) to find the pages in
229   * virtual memory.  If successful then commit the pending page
230   * request and return the address of the first page.
231   *
232   * @param pagesReserved The number of pages reserved by the initial request
233   * @param pages The number of pages requested
234   * @param zeroed If true allocated pages are zeroed.
235   * @return The address of the first of <code>pages</code> pages, or
236   * zero on failure.
237   */
238  @Inline
239  public final Address getNewPages(int pagesReserved, int pages, boolean zeroed) {
240    return allocPages(pagesReserved, pages, zeroed);
241  }
242
243  /**
244   * Commit pages to the page budget.  This is called after
245   * successfully determining that the request can be satisfied by
246   * both the page budget and virtual memory.  This simply accounts
247   * for the discrepancy between <code>committed</code> and
248   * <code>reserved</code> while the request was pending.
249   *
250   * This *MUST* be called by each PageResource during the
251   * allocPages, and the caller must hold the lock.
252   *
253   * @param reservedPages The number of pages initially reserved due to this request
254   * @param actualPages The number of pages actually allocated.
255   */
256  protected void commitPages(int reservedPages, int actualPages) {
257    int delta = actualPages - reservedPages;
258    reserved += delta;
259    committed += actualPages;
260    if (VM.activePlan.isMutator()) {
261      // only count mutator pages
262      addToCommitted(actualPages);
263    }
264  }
265
266  /**
267   * Return the number of reserved pages
268   *
269   * @return The number of reserved pages.
270   */
271  public final int reservedPages() {
272    return reserved;
273  }
274
275  /**
276   * Return the number of committed pages
277   *
278   * @return The number of committed pages.
279   */
280  public final int committedPages() {
281    return committed;
282  }
283
284  /**
285   * Return the cumulative number of committed pages
286   *
287   * @return The cumulative number of committed pages.
288   */
289  public static long cumulativeCommittedPages() {
290    return cumulativeCommitted;
291  }
292
293  /**
294   * Add to the total cumulative committed page count.
295   *
296   * @param pages The number of pages to be added.
297   */
298  private static void addToCommitted(int pages) {
299    classLock.acquire();
300    cumulativeCommitted += pages;
301    classLock.release();
302  }
303
304  /**
305   * Acquire the lock.
306   */
307  protected final void lock() {
308    lock.acquire();
309  }
310
311  /**
312   * Release the lock.
313   */
314  protected final void unlock() {
315    lock.release();
316  }
317}