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.deque; 014 015 import org.mmtk.utility.Constants; 016 017 import org.mmtk.vm.VM; 018 019 import org.vmmagic.unboxed.*; 020 import org.vmmagic.pragma.*; 021 022 /** 023 * Note this may perform poorly when being used as a FIFO structure with 024 * insertHead and pop operations operating on the same buffer. This 025 * only uses the fields inherited from <code>LocalQueue</code>, but adds 026 * the ability for entries to be added to the head of the deque and popped 027 * from the rear. 028 */ 029 @Uninterruptible public class LocalDeque extends LocalQueue 030 implements Constants { 031 032 /**************************************************************************** 033 * 034 * Public instance methods 035 */ 036 037 /** 038 * Constructor 039 * 040 * @param queue The shared deque to which this local deque will append 041 * its buffers (when full or flushed). 042 */ 043 LocalDeque(SharedDeque queue) { 044 super(queue); 045 } 046 047 @Override 048 public final void flushLocal() { 049 super.flushLocal(); 050 if (head.NE(Deque.HEAD_INITIAL_VALUE)) { 051 closeAndInsertHead(queue.getArity()); 052 head = Deque.HEAD_INITIAL_VALUE; 053 } 054 } 055 056 /**************************************************************************** 057 * 058 * Protected instance methods 059 */ 060 061 /** 062 * Check whether there is space in the buffer for a pending insert. 063 * If there is not sufficient space, allocate a new buffer 064 * (dispatching the full buffer to the shared deque if not null). 065 * 066 * @param arity The arity of the values stored in this deque: the 067 * buffer must contain enough space for this many words. 068 */ 069 @Inline 070 protected final void checkHeadInsert(int arity) { 071 if (bufferOffset(head).EQ(bufferSentinel(arity)) || 072 head.EQ(HEAD_INITIAL_VALUE)) 073 headOverflow(arity); 074 else if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(bufferOffset(head).sLE(bufferLastOffset(arity))); 075 } 076 077 /** 078 * Insert a value at the front of the deque (and buffer). This is 079 * <i>unchecked</i>. The caller must first call 080 * <code>checkHeadInsert()</code> to ensure the buffer can accommodate 081 * the insertion. 082 * 083 * @param value the value to be inserted. 084 */ 085 @Inline 086 protected final void uncheckedHeadInsert(Address value) { 087 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(bufferOffset(head).sLT(bufferSentinel(queue.getArity()))); 088 head.store(value); 089 head = head.plus(BYTES_IN_ADDRESS); 090 // if (Interface.VerifyAssertions) enqueued++; 091 } 092 093 /**************************************************************************** 094 * 095 * Private instance methods and fields 096 */ 097 098 /** 099 * Buffer space has been exhausted, allocate a new buffer and enqueue 100 * the existing buffer (if any). 101 * 102 * @param arity The arity of this buffer (used for sanity test only). 103 */ 104 private void headOverflow(int arity) { 105 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(arity == queue.getArity()); 106 if (head.NE(Deque.HEAD_INITIAL_VALUE)) 107 closeAndInsertHead(arity); 108 109 head = queue.alloc(); 110 } 111 112 /** 113 * Close the head buffer and enqueue it at the front of the 114 * shared buffer deque. 115 * 116 * @param arity The arity of this buffer. 117 */ 118 @Inline 119 private void closeAndInsertHead(int arity) { 120 queue.enqueue(head, arity, false); 121 } 122 123 /** 124 * The tail is empty (or {@code null}), and the shared deque has no buffers 125 * available. If the head has sufficient entries, consume the head. 126 * Otherwise try wait on the shared deque until either all other 127 * clients of the reach exhaustion or a buffer becomes 128 * available. 129 * 130 * @param arity The arity of this buffer 131 * @return {@code true} if the consumer has eaten all of the entries 132 */ 133 @SuppressWarnings("unused") 134 private boolean tailStarved(int arity) { 135 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(arity == queue.getArity()); 136 // entries in tail, so consume tail 137 if (!bufferOffset(head).isZero()) { 138 tailBufferEnd = head; 139 tail = bufferStart(tailBufferEnd); 140 head = Deque.HEAD_INITIAL_VALUE; 141 return false; 142 } 143 144 // Wait for another entry to materialize... 145 tailBufferEnd = queue.dequeueAndWait(arity, true); 146 tail = bufferStart(tail); 147 148 // return true if a) there is not a tail buffer or b) it is empty 149 return (tail.EQ(Deque.TAIL_INITIAL_VALUE) || tail.EQ(tailBufferEnd)); 150 } 151 }