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; 014 015 import org.mmtk.vm.VM; 016 017 import org.vmmagic.pragma.*; 018 019 /** 020 * This is a very simple, generic malloc-free allocator. It works 021 * abstractly, in "units", which the user may associate with some 022 * other allocatable resource (e.g. heap blocks). The user issues 023 * requests for N units and the allocator returns the index of the 024 * first of a contiguous set of N units or fails, returning -1. The 025 * user frees the block of N units by calling <code>free()</code> with 026 * the index of the first unit as the argument.<p> 027 * 028 * Properties/Constraints:<ul> 029 * <li> The allocator consumes one word per allocatable unit (plus 030 * a fixed overhead of about 128 words).</li> 031 * <li> The allocator can only deal with MAX_UNITS units (see below for 032 * the value).</li> 033 * </ul> 034 * 035 * The basic data structure used by the algorithm is a large table, 036 * with one word per allocatable unit. Each word is used in a 037 * number of different ways, some combination of "undefined" (32), 038 * "free/used" (1), "multi/single" (1), "prev" (15), "next" (15) & 039 * "size" (15) where field sizes in bits are in parenthesis. 040 * <pre> 041 * +-+-+-----------+-----------+ 042 * |f|m| prev | next/size | 043 * +-+-+-----------+-----------+ 044 * 045 * - single free unit: "free", "single", "prev", "next" 046 * - single used unit: "used", "single" 047 * - contiguous free units 048 * . first unit: "free", "multi", "prev", "next" 049 * . second unit: "free", "multi", "size" 050 * . last unit: "free", "multi", "size" 051 * - contiguous used units 052 * . first unit: "used", "multi", "prev", "next" 053 * . second unit: "used", "multi", "size" 054 * . last unit: "used", "multi", "size" 055 * - any other unit: undefined 056 * 057 * +-+-+-----------+-----------+ 058 * top sentinel |0|0| tail | head | [-1] 059 * +-+-+-----------+-----------+ 060 * .... 061 * /-------- +-+-+-----------+-----------+ 062 * | |1|1| prev | next | [j] 063 * | +-+-+-----------+-----------+ 064 * | |1|1| | size | [j+1] 065 * free multi +-+-+-----------+-----------+ 066 * unit block | ... | ... 067 * | +-+-+-----------+-----------+ 068 * | |1|1| | size | 069 * >-------- +-+-+-----------+-----------+ 070 * single free unit |1|0| prev | next | 071 * >-------- +-+-+-----------+-----------+ 072 * single used unit |0|0| | 073 * >-------- +-+-+-----------------------+ 074 * | |0|1| | 075 * | +-+-+-----------+-----------+ 076 * | |0|1| | size | 077 * used multi +-+-+-----------+-----------+ 078 * unit block | ... | 079 * | +-+-+-----------+-----------+ 080 * | |0|1| | size | 081 * \-------- +-+-+-----------+-----------+ 082 * .... 083 * +-+-+-----------------------+ 084 * bottom sentinel |0|0| | [N] 085 * +-+-+-----------------------+ 086 * </pre> 087 * The sentinels serve as guards against out of range coalescing 088 * because they both appear as "used" blocks and so will never 089 * coalesce. The top sentinel also serves as the head and tail of 090 * the doubly linked list of free blocks. 091 */ 092 @Uninterruptible abstract class BaseGenericFreeList implements Constants { 093 094 /**************************************************************************** 095 * 096 * Public instance methods 097 */ 098 099 /** 100 * Allocate <code>size</code> units. Return the unit ID 101 * 102 * @param size The number of units to be allocated 103 * @return The index of the first of the <code>size</code> 104 * contiguous units, or -1 if the request can't be satisfied 105 */ 106 public final int alloc(int size) { 107 // Note: -1 is both the default return value *and* the start sentinel index 108 int unit = head; // HEAD = -1 109 int s = 0; 110 while (((unit = getNext(unit)) != head) && ((s = getSize(unit)) < size)); 111 112 return (unit == head) ? FAILURE : alloc(size, unit, s); 113 } 114 115 /** 116 * Would an allocation of <code>size</code> units succeed? 117 * 118 * @param size The number of units to test for 119 * @return True if such a request could be satisfied. 120 */ 121 public final boolean couldAlloc(int size) { 122 // Note: -1 is both the default return value *and* the start sentinel index 123 int unit = head; // HEAD = -1 124 while (((unit = getNext(unit)) != head) && (getSize(unit) < size)); 125 126 return (unit != head); 127 } 128 129 /** 130 * Allocate <code>size</code> units. Return the unit ID 131 * 132 * @param size The number of units to be allocated 133 * @return The index of the first of the <code>size</code> 134 * contiguous units, or -1 if the request can't be satisfied 135 */ 136 public final int alloc(int size, int unit) { 137 int s = 0; 138 139 if (getFree(unit) && (s = getSize(unit)) >= size) 140 return alloc(size, unit, s); 141 else 142 return FAILURE; 143 } 144 145 /** 146 * Allocate <code>size</code> units. Return the unit ID 147 * 148 * @param size The number of units to be allocated 149 * @return The index of the first of the <code>size</code> 150 * contiguous units, or -1 if the request can't be satisfied 151 */ 152 private int alloc(int size, int unit, int unitSize) { 153 if (unitSize >= size) { 154 if (unitSize > size) 155 split(unit, size); 156 removeFromFree(unit); 157 setFree(unit, false); 158 } 159 160 if (DEBUG) dbgPrintFree(); 161 162 return unit; 163 } 164 165 /** 166 * Free a previously allocated contiguous lump of units. 167 * 168 * @param unit The index of the first unit. 169 * @return return the size of the unit which was freed. 170 */ 171 public final int free(int unit) { 172 return free(unit, false); 173 } 174 175 /** 176 * Free a previously allocated contiguous lump of units. 177 * 178 * @param unit The index of the first unit. 179 * @param returnCoalescedSize if true, return the coalesced size 180 * @return The number of units freed. if returnCoalescedSize is 181 * false, return the size of the unit which was freed. Otherwise 182 * return the size of the unit now available (the coalesced size) 183 */ 184 public final int free(int unit, boolean returnCoalescedSize) { 185 int freed = getSize(unit); 186 int left = getLeft(unit); 187 int start = isCoalescable(unit) && getFree(left) ? left : unit; 188 int right = getRight(unit); 189 int end = isCoalescable(right) && getFree(right) ? right : unit; 190 if (start != end) 191 coalesce(start, end); 192 193 if (returnCoalescedSize) 194 freed = getSize(start); 195 addToFree(start); 196 197 if (DEBUG) dbgPrintFree(); 198 return freed; 199 } 200 201 /** 202 * Return the size of the specified lump of units 203 * 204 * @param unit The index of the first unit in the lump. 205 * @return The size of the lump, in units. 206 */ 207 public final int size(int unit) { 208 return getSize(unit); 209 } 210 211 /**************************************************************************** 212 * 213 * Private fields and methods 214 */ 215 216 /** 217 * Initialize a new heap. Fabricate a free list entry containing 218 * everything 219 * 220 * @param units The number of units in the heap 221 */ 222 protected final void initializeHeap(int units) { 223 initializeHeap(units, units); 224 } 225 226 /** 227 * Initialize a new heap. Fabricate a free list entry containing 228 * everything 229 * 230 * @param units The number of units in the heap 231 */ 232 protected final void initializeHeap(int units, int grain) { 233 // Initialize the sentinels 234 for (int i = 1; i <= heads; i++) 235 setSentinel(-i); 236 setSentinel(units); 237 238 // create the free list item 239 int offset = units % grain; 240 int cursor = units - offset; 241 if (offset > 0) { 242 setSize(cursor, offset); 243 addToFree(cursor); 244 } 245 cursor -= grain; 246 while (cursor >= 0) { 247 setSize(cursor, grain); 248 addToFree(cursor); 249 cursor -= grain; 250 } 251 if (DEBUG) dbgPrintFree(); 252 } 253 254 /** 255 * Reduce a lump of units to size, freeing any excess. 256 * 257 * @param unit The index of the first unit 258 * @param size The size of the first part 259 */ 260 private void split(int unit, int size) { 261 int basesize = getSize(unit); 262 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(basesize > size); 263 setSize(unit, size); 264 setSize(unit + size, basesize - size); 265 addToFree(unit + size); 266 if (DEBUG) dbgPrintFree(); 267 } 268 269 /** 270 * Coalesce two or three contiguous lumps of units, removing start 271 * and end lumps from the free list as necessary. 272 * @param start The index of the start of the first lump 273 * @param end The index of the start of the last lump 274 */ 275 private void coalesce(int start, int end) { 276 if (getFree(end)) 277 removeFromFree(end); 278 if (getFree(start)) 279 removeFromFree(start); 280 281 setSize(start, end - start + getSize(end)); 282 } 283 284 /** 285 * Add a lump of units to the free list 286 * 287 * @param unit The first unit in the lump of units to be added 288 */ 289 private void addToFree(int unit) { 290 setFree(unit, true); 291 int next = getNext(head); 292 setNext(unit, next); 293 setNext(head, unit); 294 setPrev(unit, head); 295 setPrev(next, unit); 296 } 297 298 /** 299 * Remove a lump of units from the free list 300 * 301 * @param unit The first unit in the lump of units to be removed 302 */ 303 private void removeFromFree(int unit) { 304 int next = getNext(unit); 305 int prev = getPrev(unit); 306 setNext(prev, next); 307 setPrev(next, prev); 308 if (DEBUG) dbgPrintFree(); 309 } 310 311 /** 312 * Get the lump to the "right" of the current lump (i.e. "below" it) 313 * 314 * @param unit The index of the first unit in the lump in question 315 * @return The index of the first unit in the lump to the 316 * "right"/"below" the lump in question. 317 */ 318 private int getRight(int unit) { 319 return unit + getSize(unit); 320 } 321 322 323 /** 324 * Print the free list (for debugging purposes) 325 */ 326 void dbgPrintFree() { 327 if (DEBUG) { 328 Log.write("FL["); 329 int i = head; 330 while ((i = getNext(i)) != head) { 331 boolean f = getFree(i); 332 int s = getSize(i); 333 if (!f) 334 Log.write("->"); 335 Log.write(i); 336 if (!f) 337 Log.write("<-"); 338 Log.write("("); 339 Log.write(s); 340 Log.write(")"); 341 Log.write(" "); 342 Log.flush(); 343 } 344 Log.writeln("]FL"); 345 } 346 } 347 348 abstract void setSentinel(int unit); 349 abstract int getSize(int unit); 350 abstract void setSize(int unit, int size); 351 abstract boolean getFree(int unit); 352 abstract void setFree(int unit, boolean isFree); 353 abstract int getNext(int unit); 354 abstract void setNext(int unit, int next); 355 abstract int getPrev(int unit); 356 abstract void setPrev(int unit, int prev); 357 abstract int getLeft(int unit); 358 abstract boolean isCoalescable(int unit); 359 360 protected static final boolean DEBUG = false; 361 public static final int FAILURE = -1; 362 protected static final int MAX_HEADS = 128; // somewhat arbitrary 363 364 protected int heads = 1; 365 protected int head = -heads; 366 }