/*static char *SCCSID = "@(#)reader.c	6.2 91/01/14";*/
#define SCCSID "@(#)reader.c	6.2 91/01/14"
/*
 *	IBM/Microsoft Confidential
 *
 *	Copyright (c) IBM Corporation 1987, 1989
 *	Copyright (c) Microsoft Corporation 1987, 1989
 *
 *	All Rights Reserved
 *
 */

/*  NAME : READER.C
 *    
 *
 *  DESCRIPTION
 *    
 *      Functions controlling read access into a volume cache list.  
 *
 *
 *  FUNCTIONS DEFINED
 *
 *      BecomeReader () --  Gain reader access into cache list.
 *      DepartReader () --  Give up reader access into cache list.
 *
 *  
 *  REVISION HISTORY (in reverse chronological order)
 *  -----------------------------------------------------------------------
 *  8/3/90      Brian Andrew        Explicitly yield the CPU if looping in
 *                                  'BecomeReader'.
 *  
 *  4/9/90      Brian Andrew        Initial versions of 'BecomeReader',
 *                                  'DepartReader'.
 *
 *
 *  TO DO
 *  -------------------------------------------------------------------------
 */

#define INCL_DOSERRORS
#define INCL_NOPMAPI

#include <os2.h>
#include "fsh.h"

#include "cdfs.h"
#include "sem.h"
#include "path.h"
#include "vol.h"
#include "heap.h"
#include "mem.h"
#include "buffer.h"

#ifdef STATS_H
#include "stats.h"
#endif 

#include "message.h"
#include "fsdata.h"

#ifdef DEBUG
#include "pr_debug.h"
#endif

#include "pr_cache.h"

/***    BecomeReader () --  Gain reader access into cache list.  
 *      
 *  DESCRIPTION
 *
 *      This function allows a thread to enter into a cache list for     
 *      for the purpose of looking for a particular sector.  This means
 *      the thread may not change the structure of the cache list.
 *
 *      The thread gains control of the list semaphore block semaphore
 *      and then checks if there are any waiting writers.  If so it
 *      gives up the block semaphore and waits on the list semaphore
 *      and performs the same check again.
 *
 *      If there are no writers, then the thread can enter the list as
 *      a reader.  If there are no other readers, it will gain control
 *      of the list semaphore.  In any case, it increases the number of
 *      active readers.
 *      
 *      
 *  ENTRY   (Arguements passed to function)
 *      
 *      plsbSemBlk  --  Address of semaphore block for this list.
 *      
 *
 *  GLOBAL  (Global variables accessed by this function)
 *      
 *      NONE.
 *
 *
 *  GOTO    (Jump targets)
 *
 *      NONE.
 *
 *
 *  RETURN  (Value returned by this function)
 *
 *      NONE.
 *
 *
 *  EXIT    (Changes to variables seen outside this function)
 *
 *      The following fields in the semaphore block are affected.
 *      
 *      sListSem            --  Semaphore to control access to sector list.
 *      usCountRead         --  Number of active readers.
 *      
 *      
 *  LOCAL   (Variables declared within this function)    
 *      
 *      NONE.
 */

VOID BecomeReader (plsbSemBlk)
PLIST_SEM_BLK       plsbSemBlk;
{
	/*
	 *  for loop :
	 *
	 *      This loop is exited only after the thread has been given
	 *      reader status for the sector list.  The thread loops continuously
	 *      by aquiring the block semaphore and checking the number of
	 *      of waiting or active writers.  
	 *
	 *      If there are no writers, it will take the list semaphore if
	 *      there are no readers.  Otherwise it will simply exit from
	 *      the for loop after incrementing the number of readers.
	 */
	for (;;)
	{
		/* Request the semaphore block semaphore */
		SEM_REQ (plsbSemBlk->sBlockSem);

		/* If there are writers */
		if (plsbSemBlk->usCountWrite)
		{
			/* Explicitly block to yield cpu */
			FSH_YIELD ();

			/* Clear the block semaphore */
			SEM_CLEAR (plsbSemBlk->sBlockSem);

			/* Wait on the list semaphore */
			SEM_WAIT (plsbSemBlk->sListSem);

			/* Start back at the top of the loop */
			continue;

		} /* end if there are writers */

		/* Check if there are no active readers, increment anyway */
		if (!(plsbSemBlk->usCountRead++))
		{
			/* Grab the list semaphore */
			SEM_REQ (plsbSemBlk->sListSem);

		} /* end if no active readers */

		/* Give up the block semaphore */
		SEM_CLEAR (plsbSemBlk->sBlockSem);

		/* Break out of loop */
		break;

	} /* end forever loop */

} /* end BecomeReader */


/***    DepartReader () -- Give up reader access into cache list.    
 *      
 *  DESCRIPTION
 *
 *      This function is called when a thread gives up read access into
 *      a sector list.  This is done when either the desired sector was
 *      found and returned or the sector is missing and the thread wants
 *      write access to modify the list.
 *
 *      This function decrements the number of readers and releases the
 *      list semaphore if this is the last reader.
 *      
 *      
 *  ENTRY   (Arguements passed to function)
 *      
 *      plsbSemBlk  --  Address of semaphore block for this list.
 *      
 *
 *  GLOBAL  (Global variables accessed by this function)
 *
 *      NONE.
 *
 *
 *  GOTO    (Jump targets)
 *
 *      NONE.
 *
 *
 *  RETURN  (Value returned by this function)
 *
 *      NONE.
 *
 *
 *  EXIT    (Changes to variables seen outside this function)
 *
 *      The following fields in the semaphore block are affected.
 *      
 *      sListSem            --  Semaphore to control access to sector list.
 *      usCountRead         --  Number of active readers.
 *      
 *      
 *  LOCAL   (Variables declared within this function)    
 *      
 *      NONE.
 */

VOID DepartReader (plsbSemBlk)
PLIST_SEM_BLK       plsbSemBlk;
{
	/* Grab the block semaphore */
	SEM_REQ (plsbSemBlk->sBlockSem);

#ifdef DEBUG
	/* Check if readers is now zero */
	if (!plsbSemBlk->usCountRead)
	{
		dprintf ("\tDepartReader : Reader count about to go negative\n");
		Int3 ();
	}
#endif

	/* Decrement the number of readers, if zero then release the list sem */
	if (!(--plsbSemBlk->usCountRead))
	{
		SEM_CLEAR (plsbSemBlk->sListSem);

	} /* end if last reader */

	/* Clear the block semaphore */
	SEM_CLEAR (plsbSemBlk->sBlockSem);

} /* end DepartReader */

