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}