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