/*
 * (C) Copyright Department of Computer Science,
 *     Australian National University. 2002
 */
package s3.core.services.memory.JMTk;

import s3.core.services.memory.vmInterface.Constants;
import s3.core.services.memory.vmInterface.Lock;
import s3.core.services.memory.vmInterface.VM_Interface;

import ovm.core.services.memory.VM_Address;

/**
 * This supports <i>unsynchronized</i> enqueuing and dequeuing of
 * address pairs
 *
 * @author <a href="http://cs.anu.edu.au/~Steve.Blackburn">Steve Blackburn</a>
 * @version $Revision: 1.3 $
 * @date $Date: 2003/12/24 02:17:23 $
 */ 
public class SharedDeque extends Deque 
  implements Constants, VM_Uninterruptible {
  public final static String Id = "$Id: SharedDeque.java,v 1.3 2003/12/24 02:17:23 jv Exp $"; 

  
  /****************************************************************************
   *
   * Public instance methods
   */

  /**
   * Constructor
   *
   */
  SharedDeque(RawPageAllocator rpa, int arity) {
    this.rpa = rpa;
    this.arity = arity;
    lock = new Lock("SharedDeque");
    completionFlag = 0;
  }

  final boolean complete() {
    return completionFlag == 1;
  }

  final int getArity() throws VM_PragmaInline { return arity; }

  final void enqueue(VM_Address buf, int _arity, boolean toTail) {
    if (VM_Interface.VerifyAssertions)
      VM_Interface._assert(_arity == this.arity);

    lock();
    if (toTail) {
      // Add to the tail of the queue
      setNext(buf, VM_Address.fromInt(0));
      if (tail.isNull())
	head = buf;
      else
	setNext(tail, buf);
      tail = buf;
    } else {
      // Add to the head of the queue
      if (head.isNull())
	tail = buf;
      setNext(buf, head);
      head = buf;
    } 
    bufsenqueued++;
    if (VM_Interface.VerifyAssertions) {
      VM_Interface._assert(checkDequeLength(bufsenqueued));
    }
    unlock();
  }

  final void clearDeque(int _arity) {
    VM_Address buf = dequeue(_arity);
    while (!buf.isNull()) {
      free(bufferStart(buf));
      buf = dequeue(_arity);
    }
  }

  final VM_Address dequeue(int _arity) {
    if (VM_Interface.VerifyAssertions)
      VM_Interface._assert(_arity == this.arity);
    return dequeue(false);
  }

  final VM_Address dequeueAndWait(int _arity) {
    if (VM_Interface.VerifyAssertions)
      VM_Interface._assert(_arity == this.arity);
    VM_Address buf = dequeue(false);
    while (buf.isNull() && (completionFlag == 0)) {
      buf = dequeue(true);
    }
    return buf;  
  }

  final void reset() {
    setNumClientsWaiting(0);
    setCompletionFlag(0);
    if (VM_Interface.VerifyAssertions)
      VM_Interface._assert(head.isNull() && tail.isNull());
  }

  final void newClient() {
    setNumClients(numClients + 1);
  }

  final VM_Address alloc() throws VM_PragmaInline {
    VM_Address rtn = rpa.alloc(PAGES_PER_BUFFER);
    if (VM_Interface.VerifyAssertions)
      VM_Interface._assert(rtn.EQ(bufferStart(rtn)));
    return rtn;
  }

  final void free(VM_Address buf) throws VM_PragmaInline {
    if (VM_Interface.VerifyAssertions) 
      VM_Interface._assert(buf.EQ(bufferStart(buf)) && !buf.isNull());
    rpa.free(buf);
  }

  final int enqueuedPages() throws VM_PragmaInline {
    return bufsenqueued<<LOG_PAGES_PER_BUFFER;
  }

  /****************************************************************************
   *
   * Private instance methods and fields
   */
  private RawPageAllocator rpa;
  private int arity;
  private int completionFlag; //
  private int numClients; //
  private int numClientsWaiting; //
  private VM_Address head;
  private VM_Address tail;
  private int bufsenqueued;
  private Lock lock;

  
  private final VM_Address dequeue(boolean waiting) {
    lock();
    VM_Address rtn = VM_Address.fromInt(0);
    if (head.isNull()) {
      if (VM_Interface.VerifyAssertions) VM_Interface._assert(tail.isNull());
      // no buffers available
      if (waiting) {
	setNumClientsWaiting(numClientsWaiting + 1);
	if (numClientsWaiting == numClients)
	  setCompletionFlag(1);
      }
    } else {
      // dequeue the head buffer
      rtn = head;
      setHead(getNext(head));
      if (tail.EQ(rtn)) {
	setTail(VM_Address.fromInt(0));
	if (VM_Interface.VerifyAssertions) VM_Interface._assert(head.isNull());
      }
      bufsenqueued--;
      if (waiting)
	setNumClientsWaiting(numClientsWaiting - 1);
    }
    unlock();
    return rtn;
  }

  /**
   * Set the "next" pointer in a buffer forming the linked buffer chain.
   *
   * @param bufRef The buffer whose next field is to be set.
   * @param next The reference to which next should point.
   */
  private static final void setNext(VM_Address buf, VM_Address next) {
    VM_Magic.setMemoryAddress(buf, next);
  }

  /**
   * Get the "next" pointer in a buffer forming the linked buffer chain.
   *
   * @param bufRef The buffer whose next field is to be returned.
   * @return The next field for this buffer.
   */
  private final VM_Address getNext(VM_Address buf) {
    return VM_Magic.getMemoryAddress(buf);
  }

  /**
   * Check the number of buffers in the work queue (for debugging
   * purposes).
   *
   * @param length The number of buffers believed to be in the queue.
   * @return True if the length of the queue matches length.
   */
  private final boolean checkDequeLength(int length) {
    VM_Address top = head;
    int l = 0;
    while (!top.isNull() && l <= length) {
      top = getNext(top);
      l++;
    }
    return l == length;
  }

  /**
   * Lock this shared queue.  We use one simple low-level lock to
   * synchronize access to the shared queue of buffers.
   */
  private final void lock() {
    lock.acquire();
  }
  
  /**
   * Release the lock.  We use one simple low-level lock to synchronize
   * access to the shared queue of buffers.
   */
  private final void unlock() {
    lock.release();
  }

  // need to use this to avoid generating a putfield and so causing write barrier recursion
  //
  private final void setCompletionFlag(int flag) throws VM_PragmaInline {
    completionFlag = flag;
  }

  private final void setNumClients(int newNumClients) throws VM_PragmaInline {
      numClients = newNumClients;
  }

  private final void setNumClientsWaiting(int newNCW) throws VM_PragmaInline {
    numClientsWaiting = newNCW;
  }

  private final void setHead(VM_Address newHead) throws VM_PragmaInline {
    head = newHead;
  }

  private final void setTail(VM_Address newTail) throws VM_PragmaInline {
    tail = newTail;
  }
}
