/*
 * ptw32_OLL_lock.c
 *
 * Description:
 * This translation unit implements extended reader/writer queue-based locks.
 *
 * --------------------------------------------------------------------------
 *
 *      Pthreads-win32 - POSIX Threads Library for Win32
 *      Copyright(C) 1998 John E. Bossom
 *      Copyright(C) 1999,2005 Pthreads-win32 contributors
 * 
 *      Contact Email: rpj@callisto.canberra.edu.au
 * 
 *      The current list of contributors is contained
 *      in the file CONTRIBUTORS included with the source
 *      code distribution. The list can also be seen at the
 *      following World Wide Web location:
 *      http://sources.redhat.com/pthreads-win32/contributors.html
 * 
 *      This library is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU Lesser General Public
 *      License as published by the Free Software Foundation; either
 *      version 2 of the License, or (at your option) any later version.
 * 
 *      This library is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *      Lesser General Public License for more details.
 * 
 *      You should have received a copy of the GNU Lesser General Public
 *      License along with this library in the file COPYING.LIB;
 *      if not, write to the Free Software Foundation, Inc.,
 *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

/*
 * About the OLL lock (Scalable Reader-Writer Lock):
 *
 * OLL locks are queue-based locks similar to the MCS queue lock, where the queue
 * nodes are local to the thread but where reader threads can enter the critical
 * section immediately without going through a central guard lock if there are
 * already readers holding the lock.
 *
 * Covered by United States Patent Application 20100241774 (Oracle)
 */

#include "pthread.h"
#include "sched.h"
#include "implement.h"

/*
 * C-SNZI support
 */
typedef union  ptw32_oll_counter_t_		ptw32_oll_counter_t;
typedef struct ptw32_oll_snziRoot_t_		ptw32_oll_snziRoot_t;
typedef struct ptw32_oll_snziNode_t_		ptw32_oll_snziNode_t;
typedef union  ptw32_oll_snziNodeOrRoot_t_	ptw32_oll_snziNodeOrRoot_t;
typedef struct ptw32_oll_queryResult_t_		ptw32_oll_queryResult_t;
typedef struct ptw32_oll_ticket_t_		ptw32_oll_ticket_t;
typedef struct ptw32_oll_csnzi_t_		ptw32_oll_csnzi_t;

enum
{
  ptw32_archWidth	= sizeof(size_t)*8,
  ptw32_oll_countWidth	= ptw32_archWidth-2
};

#define PTW32_OLL_MAXREADERS (((size_t)2<<(ptw32_oll_countWidth-1))-1)

union ptw32_oll_counter_t_
{
  size_t	word	: ptw32_archWidth;
  struct
  {
    /*
     * This needs to be a single word
     *
     *    ------------------------------------
     *    | STATE |  ROOT  | COUNT (readers) |
     *    ------------------------------------
     *     63 / 31  62 / 30   61 / 29 .. 0
     */
    size_t	count	: ptw32_oll_countWidth;
    size_t	root	: 1;			/* ROOT or NODE */
    size_t	state	: 1;			/* OPEN or CLOSED (root only) */
  } internal;
};

struct ptw32_oll_snziRoot_t_
{
  /*
   * "counter" must be at same offset in both
   * ptw32_oll_snziNode_t and ptw32_oll_snziRoot_t
   */
  ptw32_oll_counter_t	counter;
};

enum
{
  ptw32_oll_snziRoot_open	= 0,
  ptw32_oll_snziRoot_closed	= 1
};

enum
{
  ptw32_oll_snzi_root	= 0,
  ptw32_oll_snzi_node	= 1
};

/*
 * Some common SNZI root whole-word states that can be used to set or compare
 * root words with a single operation.
 */
ptw32_oll_snziRoot_t ptw32_oll_snziRoot_openAndZero = {.counter.internal.count = 0,
                                                       .counter.internal.root = ptw32_oll_snzi_root,
                                                       .counter.internal.state = ptw32_oll_snziRoot_open};
ptw32_oll_snziRoot_t ptw32_oll_snziRoot_closedAndZero = {.counter.internal.count = 0,
                                                         .counter.internal.root = ptw32_oll_snzi_root,
                                                         .counter.internal.state = ptw32_oll_snziRoot_closed};

struct ptw32_oll_queryResult_t_
{
  BOOL	nonZero;
  BOOL	open;
};

union ptw32_oll_snziNodeOrRoot_t_
{
  ptw32_oll_snziRoot_t* rootPtr;
  ptw32_oll_snziNode_t* nodePtr;
};

struct ptw32_oll_snziNode_t_
{
  /* "counter" must be at same offset in both
   * ptw32_oll_snziNode_t and ptw32_oll_snziRoot_t
   */
  ptw32_oll_counter_t		counter;
  ptw32_oll_snziNodeOrRoot_t	parentPtr;
};

struct ptw32_oll_ticket_t_
{
  ptw32_oll_snziNodeOrRoot_t	snziNodeOrRoot;
};

ptw32_oll_ticket_t ptw32_oll_ticket_null = {NULL};

struct ptw32_oll_csnzi_t_
{
  ptw32_oll_snziRoot_t	proxyRoot;
  ptw32_oll_snziNode_t	leafs[];
};

/*
 * FOLL lock support
 */

typedef struct ptw32_foll_node_t_ ptw32_foll_node_t;
typedef struct ptw32_foll_local_t_ ptw32_foll_local_t;
typedef struct ptw32_foll_rwlock_t_ ptw32_foll_rwlock_t;

enum
{
  ptw32_srwl_reader,
  ptw32_srwl_writer
};

enum
{
  ptw32_srwl_free,
  ptw32_srwl_in_use
};

struct ptw32_foll_node_t_
{
  ptw32_foll_node_t*	qNextPtr;
  ptw32_oll_csnzi_t*	csnziPtr;
  ptw32_foll_node_t*	nextPtr;
  int			kind;
  int			allocState;
  BOOL			spin;
};

struct ptw32_foll_local_t_
{
  ptw32_foll_node_t*	rNodePtr; // Default read node. Immutable
  ptw32_foll_node_t*	wNodePtr; // Write node. Immutable.
  ptw32_foll_node_t*	departFromPtr; // List node we last arrived at.
  ptw32_oll_ticket_t	ticket; // C-SNZI ticket
};

struct ptw32_foll_rwlock_t_
{
  ptw32_foll_node_t*	tailPtr;
  ptw32_foll_node_t*	rNodesPtr; // Head of reader node
};

/*
 * ShouldArriveAtTree() returns true if:
 *   the compare_exchange in Arrive() fails too often under read access; or
 *   ??
 * Note that this is measured across all access to 
 * this lock, not just this attempt, so that highly
 * read-contended locks will use C-SNZI. Lightly
 * read-contended locks can reduce memory usage and some
 * processing by using the root directly.
 */
BOOL
ptw32_oll_ShouldArriveAtTree()
{
  return PTW32_FALSE;
}

size_t
ptw32_oll_GetLeafForThread()
{
  return 0;
}

/*
 * Only readers call ptw32_oll_Arrive()
 *
 * Checks whether the C-SNZI state is OPEN, and if so,
 * increments the surplus of the C-SNZI by either directly
 * arriving at the root node, or calling TreeArrive on one
 * of the leaf nodes. Returns a ticket pointing to the node
 * that was arrived at. If the state is CLOSED, makes no
 * change and returns a ticket that contains no pointer.
 */
ptw32_oll_ticket_t
ptw32_oll_Arrive(ptw32_oll_csnzi_t* csnzi)
{
  for (;;)
  {
    ptw32_oll_ticket_t ticket;
    ptw32_oll_snziRoot_t oldProxy = csnzi->proxyRoot;
    if (oldProxy.counter.internal.state != ptw32_oll_snziRoot_open)
    {
      ticket.snziNodeOrRoot.rootPtr = (ptw32_oll_snziRoot_t*)NULL;
      return ticket;
    }
    if (!ptw32_oll_ShouldArriveAtTree())
    {
      ptw32_oll_snziRoot_t newProxy = oldProxy;
      newProxy.counter.internal.count++;
      if (PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE(
                (PTW32_INTERLOCKED_SIZEPTR)&csnzi->proxyRoot.counter,
                (PTW32_INTERLOCKED_SIZE)newProxy.counter.word,
                (PTW32_INTERLOCKED_SIZE)oldProxy.counter.word)
          == (PTW32_INTERLOCKED_SIZE)oldProxy.counter.word)
      {
        /* Exchange successful */
        ticket.snziNodeOrRoot.rootPtr = &csnzi->proxyRoot;
        return ticket;
      }
    }
    else
    {
      ptw32_oll_snziNode_t* leafPtr = &csnzi->leafs[ptw32_oll_GetLeafForThread()];
      ticket.snziNodeOrRoot.nodePtr = (ptw32_oll_TreeArrive(leafPtr) ? leafPtr : (ptw32_oll_snziNode_t*)NULL);
      return ticket;
    }
  }
}

/*
 * Decrements the C-SNZI surplus. Returns false iff the
 * resulting state is CLOSED and the surplus is zero.
 * Ticket must have been returned by an arrival. Must have
 * received this ticket from Arrive more times than Depart
 * has been called with the ticket. (Thus, the surplus
 * must be greater than zero.)
 */
BOOL
ptw32_oll_Depart(ptw32_oll_ticket_t ticket)
{
  return ptw32_oll_TreeDepart(ticket.snziNodeOrRoot);
}

/*
 * Increments the C-SNZI surplus and returns true if the
 * C-SNZI is open or has a surplus. Calls TreeArrive
 * recursively on the node’s parent if needed.
 * Otherwise, returns false without making any changes.
 */
BOOL
ptw32_oll_TreeArrive(ptw32_oll_snziNodeOrRoot_t snziNodeOrRoot)
{
  if (snziNodeOrRoot.nodePtr->counter.internal.root != ptw32_oll_snzi_root)
  {
    /* Non-root node */
    ptw32_oll_counter_t newCounter, oldCounter;
    BOOL arrivedAtParent = PTW32_FALSE;
    do
    {
      oldCounter = snziNodeOrRoot.nodePtr->counter;
      if (0 == oldCounter.internal.count && !arrivedAtParent)
      {
        if (ptw32_oll_TreeArrive(snziNodeOrRoot.nodePtr->parentPtr))
          arrivedAtParent = PTW32_TRUE;
        else
          return PTW32_FALSE;
      }
      newCounter = oldCounter;
      newCounter.internal.count++;
    } while (PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE(
                  (PTW32_INTERLOCKED_SIZEPTR)&snziNodeOrRoot.nodePtr->counter,
                  (PTW32_INTERLOCKED_SIZE)newCounter.word,
                  (PTW32_INTERLOCKED_SIZE)oldCounter.word)
             != (PTW32_INTERLOCKED_SIZE)oldCounter.word);
    if (newCounter.internal.count != 0 && arrivedAtParent)
      ptw32_oll_TreeDepart(snziNodeOrRoot.nodePtr->parentPtr);
    return PTW32_TRUE;
  }
  else
  {
    /* Root node */
    ptw32_oll_snziRoot_t newRoot, oldRoot;
    do
    {
      oldRoot = *(ptw32_oll_snziRoot_t*)snziNodeOrRoot.rootPtr;
      if (oldRoot.counter.word == ptw32_oll_snziRoot_closedAndZero.counter.word)
        return PTW32_FALSE;
      newRoot = oldRoot;
      newRoot.counter.internal.count++;
    } while (PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE(
                   (PTW32_INTERLOCKED_SIZEPTR)&snziNodeOrRoot.rootPtr->counter,
                   (PTW32_INTERLOCKED_SIZE)newRoot.counter.word,
                   (PTW32_INTERLOCKED_SIZE)oldRoot.counter.word)
             != (PTW32_INTERLOCKED_SIZE)oldRoot.counter.word);
    return PTW32_TRUE;
  }
}

/*
 * Decrements the C-SNZI surplus, calling TreeDepart
 * recursively on the node’s parent if needed. Returns
 * false iff the resulting state of the C-SNZI is CLOSED
 * and the surplus is zero. Otherwise, returns true.
 */
BOOL
ptw32_oll_TreeDepart(ptw32_oll_snziNodeOrRoot_t snziNodeOrRoot)
{
  if (snziNodeOrRoot.nodePtr->counter.internal.root != ptw32_oll_snzi_root)
  {
    /* Non-root node */
    ptw32_oll_counter_t newCounter, oldCounter;
    do
    {
      newCounter = oldCounter = snziNodeOrRoot.nodePtr->counter;
      newCounter.internal.count--;
    } while (PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE(
                   (PTW32_INTERLOCKED_SIZEPTR)&snziNodeOrRoot.nodePtr->counter,
                   (PTW32_INTERLOCKED_SIZE)newCounter.word,
                   (PTW32_INTERLOCKED_SIZE)oldCounter.word)
             != (PTW32_INTERLOCKED_SIZE)oldCounter.word);
    return (0 == newCounter.internal.count)
             ? ptw32_oll_TreeDepart(snziNodeOrRoot.nodePtr->parentPtr)
             : PTW32_TRUE;
  }
  else
  {
    /* Root node */
    ptw32_oll_snziRoot_t newRoot, oldRoot;
    do
    {
      newRoot = oldRoot = *(ptw32_oll_snziRoot_t*)snziNodeOrRoot.rootPtr;
      newRoot.counter.internal.count--;
    } while (PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE(
                   (PTW32_INTERLOCKED_SIZEPTR)&snziNodeOrRoot.rootPtr->counter,
                   (PTW32_INTERLOCKED_SIZE)newRoot.counter.word,
                   (PTW32_INTERLOCKED_SIZE)oldRoot.counter.word)
             != (PTW32_INTERLOCKED_SIZE)oldRoot.counter.word);
    return (newRoot.counter.word != ptw32_oll_snziRoot_closedAndZero.counter.word);
  }
}

/*
 * Opens a C-SNZI object. Requires C-SNZI state to be
 * CLOSED and the surplus to be zero.
 */
void
ptw32_oll_Open(ptw32_oll_csnzi_t* csnziPtr)
{
  csnziPtr->proxyRoot = ptw32_oll_snziRoot_openAndZero;
}

/*
 * Opens a C-SNZI object while atomically performing count
 * arrivals. Requires C-SNZI state to be CLOSED and
 * the surplus to be zero.
 */
void
ptw32_oll_OpenWithArrivals(ptw32_oll_csnzi_t* csnziPtr, size_t count, BOOL close)
{
  csnziPtr->proxyRoot.counter.internal.count = count;
  csnziPtr->proxyRoot.counter.internal.state = (close ? ptw32_oll_snziRoot_closed : ptw32_oll_snziRoot_open);
}

/*
 * Closes a C-SNZI object. Returns true iff the C-SNZI
 * state changed from OPEN to CLOSED and the surplus is
 * zero.
 */
BOOL
ptw32_oll_Close(ptw32_oll_csnzi_t* csnziPtr)
{
  ptw32_oll_snziRoot_t newProxy, oldProxy;
  do
  {
    oldProxy = csnziPtr->proxyRoot;
    if (oldProxy.counter.internal.state != ptw32_oll_snziRoot_open)
    {
      return PTW32_FALSE;
    }
    newProxy = oldProxy;
    newProxy.counter.internal.state = ptw32_oll_snziRoot_closed;
  } while (PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE(
                 (PTW32_INTERLOCKED_SIZEPTR)&csnziPtr->proxyRoot.counter,
                 (PTW32_INTERLOCKED_SIZE)newProxy.counter.word,
                 (PTW32_INTERLOCKED_SIZE)oldProxy.counter.word)
           != (PTW32_INTERLOCKED_SIZE)oldProxy.counter.word);
  return (newProxy.counter.word == ptw32_oll_snziRoot_closedAndZero.counter.word);
}

/*
 * Closes a C-SNZI if its surplus is zero. Otherwise, does
 * nothing. Returns true iff C-SNZI state changed from
 * OPEN to CLOSED.
 */
BOOL
ptw32_oll_CloseIfEmpty(ptw32_oll_csnzi_t* csnziPtr)
{
  ptw32_oll_snziRoot_t newProxy, oldProxy;
  do
  {
    oldProxy = csnziPtr->proxyRoot;
    if (oldProxy.counter.word != ptw32_oll_snziRoot_openAndZero.counter.word)
    {
      return PTW32_FALSE;
    }
    newProxy = ptw32_oll_snziRoot_closedAndZero;
  } while (PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE(
                 (PTW32_INTERLOCKED_SIZEPTR)&csnziPtr->proxyRoot.counter,
                 (PTW32_INTERLOCKED_SIZE)newProxy.counter.word,
                 (PTW32_INTERLOCKED_SIZE)oldProxy.counter.word)
           != (PTW32_INTERLOCKED_SIZE)oldProxy.counter.word);
  return PTW32_TRUE;
}

/*
 * Returns whether the C-SNZI has a nonzero surplus and
 * whether the C-SNZI is open.
 * "nonZero" doesn't appear to be used anywhere in the algorithms.
 */
ptw32_oll_queryResult_t
ptw32_oll_Query(ptw32_oll_csnzi_t* csnziPtr)
{
  ptw32_oll_queryResult_t query;
  ptw32_oll_snziRoot_t proxy = csnziPtr->proxyRoot;

  query.nonZero = (proxy.counter.internal.count > 0);
  query.open = (proxy.counter.internal.state == ptw32_oll_snziRoot_open);
  return query;
}

/*
 * Returns whether the Arrive operation that returned
 * the ticket succeeded.
 */
BOOL
ptw32_oll_Arrived(ptw32_oll_ticket_t t)
{
  return (t.snziNodeOrRoot.nodePtr != NULL);
}

/*
 * Constructs and returns a ticket that can be used to
 * depart from the root node.
 */
ptw32_oll_ticket_t
ptw32_oll_DirectTicket(ptw32_oll_csnzi_t* csnziPtr)
{
  ptw32_oll_ticket_t ticket;
  ticket.snziNodeOrRoot.rootPtr = &csnziPtr->proxyRoot;
  return ticket;
}

/* Scalable RW Locks */

typedef struct ptw32_srwl_rwlock_t_ ptw32_srwl_rwlock_t;
typedef struct ptw32_srwl_node_t_ ptw32_srwl_node_t;
typedef struct ptw32_srwl_local_t_ ptw32_srwl_local_t;

enum
{
  ptw32_srwl_reader	= 0,
  ptw32_srwl_writer	= 1
};

enum
{
  ptw32_srwl_free	= 0,
  ptw32_srwl_in_use	= 1
};

struct ptw32_srwl_rwlock_t_
{
  ptw32_srwl_node_t* tailPtr;
  ptw32_srwl_node_t* readerNodePtr;
};

struct ptw32_srwl_node_t_
{
  ptw32_srwl_node_t*	qNextPtr;
  ptw32_oll_csnzi_t*	csnziPtr;
  ptw32_srwl_node_t*	nextReaderPtr;
  int			kind;		/* ptw32_srwl_reader, ptw32_srwl_writer */
  int			allocState;	/* ptw32_srwl_free, ptw32_srwl_in_use */
  BOOL			spin;
};

/*
 * When a ptw32_srwl_local_t is instantiated the "kind" of each of
 * rNode and wNode must be set as appropriate. This is the only
 * time "kind" is set.
 */
struct ptw32_srwl_local_t_
{
  ptw32_srwl_node_t*	rNodePtr;
  ptw32_srwl_node_t*	wNodePtr;
  ptw32_srwl_node_t*	departFromPtr;
  ptw32_oll_ticket_t	ticket;
};

/* Allocates a new reader node. */
ptw32_srwl_node_t*
ptw32_srwl_AllocReaderNode(ptw32_srwl_local_t* local)
{
  ptw32_srwl_node_t* currNodePtr = local->rNodePtr;
  for (;;)
  {
    if (currNodePtr->allocState == ptw32_srwl_free)
    {
      if (PTW32_INTERLOCKED_COMPARE_EXCHANGE_LONG(
            (PTW32_INTERLOCKED_LONGPTR)&currNodePtr->allocState,
            (PTW32_INTERLOCKED_LONG)ptw32_srwl_in_use,
            (PTW32_INTERLOCKED_LONG)ptw32_srwl_free)
          == (PTW32_INTERLOCKED_LONG)ptw32_srwl_in_use)
      {
        return currNodePtr;
      }
    }
    currNodePtr = currNodePtr->next;
  }
}

/*
 * Frees a reader node. Requires that its allocState
 * is ptw32_srwl_in_use.
 */
void
ptw32_srwl_FreeReaderNode(ptw32_srwl_node_t* nodePtr)
{
  nodePtr->allocState := ptw32_srwl_free;
}

void
ptw32_srwl_WriterLock(ptw32_srwl_rwlock_t* lockPtr, ptw32_srwl_local_t* localPtr)
{
  oldTailPtr = (ptw32_srwl_rwlock_t*)PTW32_INTERLOCKED_EXCHANGE_PTR(
                                       (PTW32_INTERLOCKED_PVOID_PTR)&lockPtr->tailPtr,
                                       (PTW32_INTERLOCKED_PVOID)localPtr->wNodePtr);
  if (oldTailPtr != NULL)
  {
    localPtr->wNodePtr->spin := PTW32_TRUE;
    oldTailPtr->qNextPtr = localPtr->wNodePtr;
    if (oldTailPtr->kind == ptw32_srwl_writer)
    {
      while (localPtr->wNodePtr->spin);
    }
    else
    {
      /* Wait until node is properly recycled */
      while (ptw32_oll_Query(oldTailPtr->csnzi).open);
      /*
       * Close C-SNZI of previous reader node.
       * If there are no readers to signal us, spin on
       * previous node and free it before entering
       * critical section.
       */
      if (ptw32_oll_Close(oldTailPtr->csnzi))
      {
        while (oldTailPtr->spin);
        ptw32_srwl_FreeReaderNode(oldTailPtr);
      }
      else
      {
        while (localPtr->wNodePtr->spin);
      }
    }
  }
}

void
ptw32_srwl_WriterUnlock(ptw32_srwl_rwlock_t* lockPtr, ptw32_srwl_local_t* localPtr)
{
  if (localPtr->wNodePtr->qNextPtr == NULL)
  {
    if (PTW32_INTERLOCKED_COMPARE_EXCHANGE_PTR(
              (PTW32_INTERLOCKED_PVOIDPTR)&lockPtr->tailPtr,
              (PTW32_INTERLOCKED_PVOID)NULL,
              (PTW32_INTERLOCKED_PVOID)localPtr->wNodePtr)
        == (PTW32_INTERLOCKED_PVOID)NULL)
    {
      return;
    }
    else
    {
      while (localPtr->wNodePtr->qNextPtr == NULL);
    }
  }
  /* Clean up */
  localPtr->wNodePtr->qNextPtr->spin = PTW32_FALSE;
  localPtr->wNodePtr->qNextPtr = NULL;
}

void
ptw32_srwl_ReaderLock(ptw32_srwl_rwlock_t* lockPtr, ptw32_srwl_local_t* localPtr)
{
  ptw32_srwl_node_t* rNodePtr = NULL;
  for (;;)
  {
    ptw32_srwl_node_t* tailPtr = lockPtr->tailPtr;
    /* If no nodes are in the queue */
    if (tailPtr == NULL)
    {
      if (rNodePtr == NULL)
      {
        rNodePtr = ptw32_srwl_AllocReaderNode(localPtr);
      }
      rNodePtr->spin = PTW32_FALSE;
      if (PTW32_INTERLOCKED_COMPARE_EXCHANGE_PTR(
                (PTW32_INTERLOCKED_PVOIDPTR)&lockPtr->tailPtr,
                (PTW32_INTERLOCKED_PVOID)rNodePtr,
                (PTW32_INTERLOCKED_PVOID)NULL)
          == (PTW32_INTERLOCKED_PVOID)rNodePtr)
      {
        ptw32_oll_Open(rNodePtr->csnzi);
        localPtr->ticket = ptw32_oll_Arrive(rNodePtr->csnzi);
        if (ptw32_oll_Arrived(localPtr->ticket))
        {
          localPtr->departFromPtr = rNodePtr;
          return;
        }
        /* Avoid reusing inserted node */
        rNodePtr = NULL;
      }
    }
    /* Otherwise, there is a node in the queue */
    else
    {
      /* Is last node a writer node? */
      if (tailPtr->kind == ptw32_srwl_writer)
      {
        if (rNodePtr == NULL)
        {
          rNodePtr = ptw32_srwl_AllocReaderNode(localPtr);
        }
        rNodePtr->spin = PTW32_TRUE;
        if (PTW32_INTERLOCKED_COMPARE_EXCHANGE_PTR(
                  (PTW32_INTERLOCKED_PVOIDPTR)&lockPtr->tailPtr,
                  (PTW32_INTERLOCKED_PVOID)rNodePtr,
                  (PTW32_INTERLOCKED_PVOID)tailPtr)
            == (PTW32_INTERLOCKED_PVOID)rNodePtr)
        {
          tailPtr->qNextPtr = rNodePtr;
          localPtr->ticket = ptw32_oll_Arrive(rNodePtr->csnzi);
          if (ptw32_oll_Arrived(localPtr->ticket))
          {
            localPtr->departFromPtr = rNodePtr;
            while (rNodePtr->spin);
            return;
          }
          /* Avoid reusing inserted node */
          rNodePtr = NULL;
        }
      }
      /*
       * Otherwise, last node is a reader node.
       * (tailPtr->kind == ptw32_srwl_reader)
       */
      else
      {
        localPtr->ticket = ptw32_oll_Arrive(tailPtr->csnzi);
        if (ptw32_oll_Arrived(localPtr->ticket))
        {
          if (rNodePtr != NULL)
          {
            ptw32_srwl_FreeReaderNode(rNodePtr);
          }
          localPtr->departFromPtr = tailPtr;
          while (tailPtr->spin);
          return;
        }
      }
    }
  }
}

void
ptw32_srwl_ReaderUnlock(ptw32_srwl_rwlock_t* lockPtr, ptw32_srwl_local_t* localPtr)
{
  if (ptw32_oll_Depart(localPtr->departFromPtr->csnzi, localPtr->ticket))
  {
    return;
  }
  /* Clean up */
  localPtr->departFromPtr->qNextPtr->spin = PTW32_FALSE;
  localPtr->departFromPtr->qNextPtr = NULL;
  ptw32_srwl_FreeReaderNode(localPtr->departFromPtr);
}


#include <stdio.h>

int main()
{
  printf("%lx\n", PTW32_OLL_MAXREADERS);
  return 0;
}