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