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.deque;
014
015import static org.mmtk.utility.Constants.*;
016
017import org.mmtk.vm.VM;
018
019import org.vmmagic.pragma.*;
020import org.vmmagic.unboxed.*;
021
022/**
023 * This class implements a local (<i>unsynchronized</i>) sequential
024 * store buffer.  An SSB is strictly FIFO (although this class does
025 * not implement dequeuing).<p>
026 *
027 * Each instance stores word-sized values into a local buffer.  When
028 * the buffer is full, or if the <code>flushLocal()</code> method is
029 * called, the buffer enqueued at the tail of a
030 * <code>SharedDeque</code>.  This class provides no mechanism for
031 * dequeing.<p>
032 *
033 * The implementation is intended to be as efficient as possible, in
034 * time and space, as it is used in critical code such as the GC work
035 * queue and the write buffer used by many "remembering"
036 * collectors. Each instance has just two fields: a bump pointer and a
037 * pointer to the <code>SharedDeque</code><p>
038 *
039 * Preconditions: Buffers are always aligned on buffer-size address
040 * boundaries.<p>
041 *
042 * Invariants: Buffers are filled such that tuples (of the specified
043 * arity) are packed to the low end of the buffer.  Thus buffer
044 * overflows on inserts and pops (underflow actually) will always arise
045 * when then cursor is buffer-size aligned.
046 */
047@Uninterruptible class LocalSSB extends Deque {
048
049  /****************************************************************************
050   *
051   * Public instance methods
052   */
053
054  /**
055   * Constructor
056   *
057   * @param queue The shared queue to which this local ssb will append
058   * its buffers (when full or flushed).
059   */
060  LocalSSB(SharedDeque queue) {
061    this.queue = queue;
062    resetLocal();
063  }
064
065  /**
066   * Flush the buffer and add it to the shared queue (this will
067   * make any entries in the buffer visible to any consumer associated
068   * with the shared queue).
069   */
070  public void flushLocal() {
071    if (tail.NE(Deque.TAIL_INITIAL_VALUE)) {
072      closeAndEnqueueTail(queue.getArity());
073      tail = Deque.TAIL_INITIAL_VALUE;
074      tailBufferEnd = Deque.TAIL_INITIAL_VALUE;
075    }
076  }
077
078  public void reset() {
079    resetLocal();
080  }
081
082 /****************************************************************************
083   *
084   * Protected instance methods and fields
085   */
086
087  /** the location in the buffer */
088  @Entrypoint
089  protected Address tail;
090  /** the end of the buffer */
091  protected Address tailBufferEnd;
092  /** the shared queue */
093  protected final SharedDeque queue;
094
095  /**
096   * Reset the local buffer (throwing away any local entries).
097   */
098  public void resetLocal() {
099    tail = Deque.TAIL_INITIAL_VALUE;
100    tailBufferEnd = Deque.TAIL_INITIAL_VALUE;
101  }
102
103  /**
104   * Check whether there is space in the buffer for a pending insert.
105   * If there is not sufficient space, allocate a new buffer
106   * (dispatching the full buffer to the shared queue if not null).
107   *
108   * @param arity The arity of the values stored in this SSB: the
109   * buffer must contain enough space for this many words.
110   */
111  @Inline(value = Inline.When.AssertionsDisabled)
112  protected final void checkTailInsert(int arity) {
113    if (bufferOffset(tail).isZero())
114      tailOverflow(arity);
115    else if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(bufferOffset(tail).sGE(Word.fromIntZeroExtend(arity).lsh(LOG_BYTES_IN_ADDRESS).toOffset()));
116  }
117
118  /**
119   * Insert a value into the buffer.  This is <i>unchecked</i>.  The
120   * caller must first call <code>checkInsert()</code> to ensure the
121   * buffer can accommodate the insertion.
122   *
123   * @param value the value to be inserted.
124   */
125  @Inline(value = Inline.When.AssertionsDisabled)
126  protected final void uncheckedTailInsert(Address value) {
127    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(bufferOffset(tail).sGE(Offset.fromIntZeroExtend(BYTES_IN_ADDRESS)));
128    tail = tail.minus(BYTES_IN_ADDRESS);
129    tail.store(value);
130    // if (Interface.VerifyAssertions) enqueued++;
131  }
132
133  /**
134   * In the case where a buffer must be flushed before being
135   * filled (either to the queue or to the head), the entries must be
136   * slid to the base of the buffer in order to preserve the invariant
137   * that all non-tail buffers will have entries starting at the base
138   * (which allows a simple test against the base to be used when
139   * popping entries).  This is <i>expensive</i>, so should be
140   * avoided.
141   *
142   * @param arity The arity of the buffer in question
143   * @return The last slot in the normalized buffer that contains an entry
144   */
145  protected final Address normalizeTail(int arity) {
146    Address src = tail;
147    Address tgt = bufferFirst(tail);
148    Address last = tgt.plus(bufferLastOffset(arity).minus(bufferOffset(tail)));
149    while (tgt.LE(last)) {
150      tgt.store(src.loadAddress());
151      src = src.plus(BYTES_IN_ADDRESS);
152      tgt = tgt.plus(BYTES_IN_ADDRESS);
153    }
154    return last;
155  }
156
157  /**
158   * Return the sentinel offset for a buffer of a given arity.  This is used
159   * both to compute the address at the end of the buffer.
160   *
161   * @param arity The arity of this buffer
162   * @return The sentinel offset value for a buffer of the given arity.
163   */
164  @Inline
165  protected final Offset bufferSentinel(int arity) {
166    return bufferLastOffset(arity).plus(BYTES_IN_ADDRESS);
167  }
168
169  /****************************************************************************
170   *
171   * Private instance methods
172   */
173
174  /**
175   * Buffer space has been exhausted, allocate a new buffer and enqueue
176   * the existing buffer (if any).
177   *
178   * @param arity The arity of this buffer (used for sanity test only).
179   */
180  private void tailOverflow(int arity) {
181    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(arity == queue.getArity());
182    if (tail.NE(Deque.TAIL_INITIAL_VALUE)) {
183      closeAndEnqueueTail(arity);
184    }
185    tail = queue.alloc().plus(bufferSentinel(arity));
186    tailBufferEnd = tail;
187  }
188
189  /**
190   * Close the tail buffer (normalizing if necessary), and enqueue it
191   * at the tail of the shared buffer queue.
192   *
193   *  @param arity The arity of this buffer.
194   */
195  @NoInline
196  private void closeAndEnqueueTail(int arity) {
197    Address last;
198    if (!bufferOffset(tail).isZero()) {
199      // prematurely closed
200      last = normalizeTail(arity);
201    } else {
202      // a full tail buffer
203      last = tailBufferEnd.minus(BYTES_IN_ADDRESS);
204    }
205    queue.enqueue(last.plus(BYTES_IN_ADDRESS), arity, true);
206  }
207
208  /**
209   * Return true if this SSB is locally empty
210   *
211   * @return true if this SSB is locally empty
212   */
213  public final boolean isFlushed() {
214    return tail.EQ(Deque.TAIL_INITIAL_VALUE);
215  }
216}