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; 014 015import static org.mmtk.utility.Constants.*; 016 017import org.mmtk.utility.gcspy.drivers.AbstractDriver; 018 019import org.mmtk.vm.Lock; 020import org.mmtk.vm.VM; 021 022import org.vmmagic.pragma.*; 023import org.vmmagic.unboxed.*; 024 025/** 026 * FIXME This class must be re-written as it makes the assumption that 027 * the implementation language (Java) and the language being 028 * implemented are the same. This is true in the case of Jikes RVM, 029 * but it is not true for any VM implementing a language other than 030 * Java.<p> 031 * 032 * Each instance of this class is a doubly-linked list, in which 033 * each item or node is a piece of memory. The first two words of each node 034 * contains the forward and backward links. The third word contains 035 * the treadmill. The remaining portion is the payload.<p> 036 * 037 * The treadmill object itself must not be moved.<p> 038 * 039 * Access to the instances may be synchronized depending on the 040 * constructor argument. 041 */ 042@Uninterruptible public final class DoublyLinkedList { 043 044 /**************************************************************************** 045 * 046 * Class variables 047 */ 048 049 /**************************************************************************** 050 * 051 * Instance variables 052 */ 053 054 /** 055 * 056 */ 057 private Address head; 058 private final Lock lock; 059 private final int logGranularity; // Each node on the treadmill is guaranteed to be a multiple of granularity. 060 061 /**************************************************************************** 062 * 063 * Instance Methods 064 */ 065 066 /** 067 * @param logGranularity TODO needs documentation 068 * @param shared whether the instance will be shared between threads 069 */ 070 public DoublyLinkedList(int logGranularity, boolean shared) { 071 head = Address.zero(); 072 lock = shared ? VM.newLock("DoublyLinkedList") : null; 073 this.logGranularity = logGranularity; 074 075 // ensure that granularity is big enough for midPayloadToNode to work 076 Word tmp = Word.one().lsh(logGranularity); 077 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(tmp.and(nodeMask).EQ(tmp)); 078 } 079 080 // Offsets are relative to the node (not the payload) 081 // 082 private static final Offset PREV_OFFSET = Offset.fromIntSignExtend(0 * BYTES_IN_ADDRESS); 083 private static Offset NEXT_OFFSET = Offset.fromIntSignExtend(1 * BYTES_IN_ADDRESS); 084 private static Offset HEADER_SIZE = Offset.fromIntSignExtend(2 * BYTES_IN_ADDRESS); 085 086 private static final Word nodeMask; 087 static { 088 Word mask = Word.one(); 089 while (mask.LE(HEADER_SIZE.plus(MAX_BYTES_PADDING).toWord())) mask = mask.lsh(1); 090 nodeMask = mask.minus(Word.one()).not(); 091 } 092 093 @Inline 094 public static int headerSize() { 095 return HEADER_SIZE.toInt(); 096 } 097 098 public boolean isNode(Address node) { 099 return node.toWord().rshl(logGranularity).lsh(logGranularity).EQ(node.toWord()); 100 } 101 102 @Inline 103 public static Address nodeToPayload(Address node) { 104 return node.plus(HEADER_SIZE); 105 } 106 107 @Inline 108 public static Address midPayloadToNode(Address payload) { 109 // This method words as long as you are less than MAX_BYTES_PADDING into the payload. 110 return payload.toWord().and(nodeMask).toAddress(); 111 } 112 113 @Inline 114 public void add(Address node) { 115 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isNode(node)); 116 if (lock != null) lock.acquire(); 117 node.store(Address.zero(), PREV_OFFSET); 118 node.store(head, NEXT_OFFSET); 119 if (!head.isZero()) 120 head.store(node, PREV_OFFSET); 121 head = node; 122 if (lock != null) lock.release(); 123 } 124 125 @Inline 126 public void remove(Address node) { 127 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isNode(node)); 128 if (lock != null) lock.acquire(); 129 Address prev = node.loadAddress(PREV_OFFSET); 130 Address next = node.loadAddress(NEXT_OFFSET); 131 // Splice the node out of the list 132 if (!next.isZero()) 133 next.store(prev, PREV_OFFSET); 134 if (prev.isZero()) 135 head = next; 136 else 137 prev.store(next, NEXT_OFFSET); 138 // Null out node's reference to the list 139 node.store(Address.zero(), PREV_OFFSET); 140 node.store(Address.zero(), NEXT_OFFSET); 141 if (lock != null) lock.release(); 142 } 143 144 @Inline 145 public Address getHead() { 146 return head; 147 } 148 149 @Inline 150 public Address getNext(Address node) { 151 return node.loadAddress(NEXT_OFFSET); 152 } 153 154 @Inline 155 public Address pop() { 156 Address first = head; 157 if (!first.isZero()) 158 remove(first); 159 return first; 160 } 161 162 @Inline 163 public boolean isEmpty() { 164 return head.isZero(); 165 } 166 167 /** 168 * Return true if a cell is on a given treadmill 169 * 170 * @param node The cell being searched for 171 * @return <code>true</code> if the cell is found on the treadmill 172 */ 173 public boolean isMember(Address node) { 174 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isNode(node)); 175 boolean result = false; 176 if (lock != null) lock.acquire(); 177 Address cur = head; 178 while (!cur.isZero()) { 179 if (cur.EQ(node)) { 180 result = true; 181 break; 182 } 183 cur = cur.loadAddress(NEXT_OFFSET); 184 } 185 if (lock != null) lock.release(); 186 return result; 187 } 188 189 public void show() { 190 if (lock != null) lock.acquire(); 191 Address cur = head; 192 Log.write(cur); 193 while (!cur.isZero()) { 194 cur = cur.loadAddress(NEXT_OFFSET); 195 Log.write(" -> "); Log.write(cur); 196 } 197 Log.writeln(); 198 if (lock != null) lock.release(); 199 } 200 201 202 /** 203 * Gather data for GCSpy 204 * @param driver the GCSpy space driver 205 */ 206 void gcspyGatherData(AbstractDriver driver) { 207 // GCSpy doesn't need a lock (in its stop the world config) 208 Address cur = head; 209 while (!cur.isZero()) { 210 driver.scan(cur); 211 cur = cur.loadAddress(NEXT_OFFSET); 212 } 213 } 214}