/*static char *SCCSID = "@(#)lazywrit.c	6.6 92/02/01";*/
#define INCL_DOSERRORS
#include <os2def.h>
#include <bse.h>
#include <fsh.h>
#include "const.h"
#include "chain.h"
#include "misc.h"
#include "buf.h"
#include "dir.h"
#include "volume.h"
#include "storage.h"
#include "tables.h"
#include "lw.h"
#include "frame.h"
#include "fsdata.h"
#include "pbmacros.h"
#include "log.h"
#include "strat2.h"
#include "asyncio.h"
#include "pr_CACHE.h"                   /* HashFind. */
#include "pr_LAZY.h"
#include "pr_MISC.h"
#if defined(DEBUG) || defined(DEBUG_LW)
    #include "pr_DEBUG.h"
#endif

//ULONG semLWDone;    /* Do a semset & semwait on this to be woken up
//                     * when the first lazy writer is finished (stopped).
//		               */

//ULONG semLW2Done;   /* Do a semset & semwait on this to be woken up
//                     * when the second lazy writer is finished (stopped).
//		               */

//ULONG semLW3Done;   /* Do a semset & semwait on this to be woken up
//                     * when the third lazy writer is finished (stopped).
//		               */

/* Async device tuning parameters:  [Move these into LWParms struct?] */
#define LW_EXEC_LIST_SIZE	32 	/* Number of commands per chain. */

/***	LazyWriter - block and flush as required
 *
 *	LazyWriter is used by the lazy write thread to wait until the cache
 *	contains old data or until there is dirty data and the disk
 *	appears to be idle.  After each block, we go back to the top to
 *	see if we can return.  We do this as blocking may change previously
 *	ascertained state.
 *
 *	Once we ascertain that there is data to flush, we write it out and then
 *	rescan the cache for either more writes or sleep time.
 *
 *	Entry	none
 *
 *	Exit	Returns error code, never returns if no error
 *		Yields
 *
 *	HISTORY
 *	    14-Feb-1990 by JR (John Rogers, JohnRo@Microsoft)
 *		- PTR B788098: avoid alloc of LWBuf if possible.
 *	    14-Nov-89 by JR (John Rogers, JohnRo@Microsoft)
 *		- Code rework.
 *		- Extracted LWWrite() to lwwrite.c.
 *	    08-Nov-89 by JR (John Rogers, JohnRo@Microsoft)
 *		- Initial PIC version of scatter/gather support.
 *	    31-May-1989 by JR (John Rogers, JohnRo@Microsoft)
 *		- DCR 259.2: Changed shutdown logic.
 *	    17-Nov-1988 16:27:57 by Jon Parati [jonpa]	resynced w/ 11/9
 */

USHORT far
LazyWriter (PFRAME pfrm)
{
    BYTE bPri = PRIO_LAZY_WRITE;	/* Priority (only used by AsyncIO stuff). */
    BOOL fAddImmune;
    BOOL fFirstAsyncIO = TRUE;
    BOOL fGotNonAsyncIOBuf = FALSE;
    PBUFNODE pbuf;
    ULONG msTmp;
    ULONG msSinceBoot;	/* current time */
    ULONG msBlock;	/* amount of time to block */
    PIOH pioh;			/* Async I/O handle. */
    USHORT usRet;
    USHORT usTypeLazyIO;
    USHORT iVol;
	 ULONG fIOHFlags;

#define LW_DirtyBufs		0		 /* write out any dirty buffer */
#define LW_OldBufs		1		 /* write out old (over-age) buffers */
#define LW_IdleBufs		2		 /* write out over-idle buffers */

    usTypeLazyIO = LW_DirtyBufs;

#ifdef DEBUG
    dprintf ("LazyWriter: starting up.\n");
#endif

    while (TRUE) { /* For each time we can write stuff... */

	while (TRUE) { /* Scan for something to write, or wait for it. */

	    /*
	     * We could have returned from the FSH_SEMSETWAIT (at the end
	     * of this loop) because StopLazyWriter told the lazy writer to
	     * stop by clearing the fLWDaemon and clearing the semaphore.
	     * Test for this case.  StopLazyWriter takes care of freeing the
	     * buffer that LWBuf points to, if necessary.
	     */

	   if (!fLWDaemon) {
#ifdef DEBUG
		dprintf ("LazyWriter: stopping gracefully\n");
#endif

//#114986
	    /* start at the top of the LW queue */
        pbuf = bufLWHead.dcbpDirt.pbufFwd;

        while ((pbuf != (PBUFNODE) &bufLWHead) && (pbuf->fFlag & BFL_LWLOCK)) {
          pbuf = pbuf->dcbpDirt.pbufFwd;
        }

	     if (pbuf == (PBUFNODE) &bufLWHead) {
#ifdef DEBUG_LW
		 dprintf ("LazyWriter: no buffs\n");
#endif

		    if (! fFirstAsyncIO)
		      RelIOHand(pioh);

		  /* wake up anyone waiting for LazyWriter to complete */
		   SemClear (&semLWDone);

		   return (NO_ERROR);

	     } else  /* dirty buffers exist; need to write them out to disk */
                /* so we can shutdown the lazy writer                  */
           fForceLWIO = TRUE;

      }

	    /* get the current time */
	    msSinceBoot = msNow ();

       /* force lazy IO if flag == TRUE by pretending disk is idle */
       if (fForceLWIO) {
         msLastIO = msSinceBoot - LWData.msIdle;
       }

	    /* start at the top of the LW queue */
	    pbuf = bufLWHead.dcbpDirt.pbufFwd;

       while ((pbuf != (PBUFNODE) &bufLWHead) && (pbuf->fFlag & BFL_LWLOCK)) {
         pbuf = pbuf->dcbpDirt.pbufFwd;
       }

	    if (pbuf == (PBUFNODE) &bufLWHead) {
#ifdef DEBUG_LW
		dprintf ("LazyWriter: no buffs\n");
#endif
		msBlock = TO_INFINITE;
	    }

	    /* if there are more bufs dirty than we allow then write it */
	    else if ((cbufDirty > LWData.cbufDirtyMax) || (fForceLWIO == TRUE)) {

#ifdef DEBUG_LW
		dprintf ("LazyWriter: cbufDirty=%d > max allowed (%d)\n",
			 cbufDirty, LWData.cbufDirtyMax);
#endif
		fAddImmune = TRUE;

/* The above LOC eliminates the need for the below LOC	*/
      usTypeLazyIO = LW_DirtyBufs; /* write out dirty buffers */

		break;		/* goto lwb9 */
	    }

	    /* if age of buf is greater than we allow then write it */
	    else if ((msBlock = msSinceBoot - pbuf->msFirstDirtied) >=
							LWData.msDataAge) {
#ifdef DEBUG_LW
		dprintf ("LazyWriter: buf too old, ");
		dprintf ("msFirstDirtied=%ld, msSinceBoot=%ld\n",
			 pbuf->msFirstDirtied, msSinceBoot);
#endif
		/* too old now, go flush */
		fAddImmune = FALSE;

/* The above LOC eliminates the need for the below LOC	*/
      usTypeLazyIO = LW_OldBufs;

		break;	/* goto lwb9 */
	    }
	    else {

		/* block time is ms til buff times out */
		   msBlock = LWData.msDataAge - msBlock;

		/*
		 * if disk is not idle..., msTmp gets the time that the
		 * disk has been idle.
		 */
		if ((msTmp = msSinceBoot - msLastIO) < LWData.msIdle) {
#ifdef DEBUG_LW
		    dprintf ("LazyWriter: disk not idle, msSinceBoot=%ld, ",
			     msSinceBoot);
		    dprintf ("msLastIO=%ld\n", msLastIO);
#endif
		    msTmp = LWData.msIdle - msTmp; /* ms until idle time out */

		    /*
		     * block until either the disk is idle or the
		     * buffer times out
		     */
		    msBlock = MIN (msBlock, msTmp);
		} else {

		    /*
		     * Disk is idle.  Walk the cache to find minimum time until
		     * not-recently touched.
		     */
#ifdef DEBUG_LW
		    dprintf ("LazyWriter: disk is idle, msSinceBoot=%ld, ",
			     msSinceBoot);
		    dprintf ("msLastIO=%ld\n", msLastIO);
#endif

/* lwb2: */

		    while (pbuf != (PBUFNODE) &bufLWHead) {

			/* if buf not recently touched, go write it */
			if ((msTmp = msSinceBoot - pbuf->msLastDirtied) >=
							    LWData.msIdleBuf) {

#ifdef DEBUG_LW
			    dprintf ("LazyWriter: write it, msSinceBoot=%ld, ",
				     msSinceBoot);
			    dprintf ("pbuf->LastDirtied=%ld\n",
				     pbuf->msLastDirtied);
#endif

           if (fForceLWIO)
			     fAddImmune = TRUE;
             else
			     fAddImmune = FALSE;

/* The above LOC eliminates the need for the below LOC	*/
	     usTypeLazyIO = LW_IdleBufs;

			    goto lwb9;
			} else {
#ifdef DEBUG_LW
			    dprintf ("LazyWriter: buf recently touched, msSinceBoot=%ld, ",
				     msSinceBoot);
			    dprintf ("pbuf->LastDirtied=%ld\n",
				     pbuf->msLastDirtied);
#endif
			    /*
			     * msTmp gets time left for buffer to remain
			     * untouched before it will time out.
			     */
			    msTmp = LWData.msIdleBuf - msTmp;
			    msBlock = MIN (msBlock, msTmp);
			    pbuf = pbuf->dcbpDirt.pbufFwd;
			}
		    } /* while... */
		} /* else (disk idle) */
	    } /* else */

#ifdef DEBUG_LW
	    dprintf ("LazyWriter: calling fsh_semsetwait, msBlock=%ld\n",
		     msBlock);
#endif
	    /*
	     * Keep calling FSH_SEMSETWAIT while the return code is what we
	     * expect.  Proper return codes are:
	     *
	     * NO_ERROR - someone is trying to kill the lazy writer
	     *			   or if we ran out of buffers and the lazy
	     *			   writer was forced to wake up.
	     *
	     * ERROR_SEM_TIMEOUT - under normal conditions when the lazy
	     *			   writer figures it is time to wake up.
	     *
	     * For all others we call FSH_SEMSETWAIT again.
	     *
	     * Don't worry about adjusting the msBlock timeout since it
	     * will be a rare occurance when we get a bad return from
	     * FSH_SEMSETWAIT.  Even if we do, the longest time that this
	     * could ever be is the maximum dirty time for a buffer.
	     * No big deal if we block a little too long.
	     */
#ifdef DEBUG
	    usRet = NO_ERROR;
#endif
	    do {
#ifdef DEBUG
		/* print a message indicating an unexpected return code */
		if (usRet != NO_ERROR) {
		    dprintf ("LazyWriter: FSH_SEMSETWAIT returned %d, %s\n",
			     usRet,
			     "waiting again!");
		}
#endif

		MAYBLOCK (usRet = FSH_SEMSETWAIT (&semLW, msBlock));

	    } while ((usRet != NO_ERROR) && (usRet != ERROR_SEM_TIMEOUT));

	} /* Loop scanning for stuff to write, or wait for it. */

lwb9:
	fLWIO = TRUE;

	/*
	 * Depending on whether or not device driver supports command chaining
	 * and scatter/gather, arrange for this buffer (and possibly others) to
	 * be written to disk.
	 */

    iVol = IVOLFROMLSN (pbuf->lsnSec);
    pfrm->pvol = VolPtr [iVol];

//test. force strat1
// goto strat1;

   if (IsAsyncIOable(pbuf->lsnSec)) {         //#84225

	    fIOHFlags = (pbuf->lsnSec & ~SECMASK)	      /* volbits */
			      | GIH_ABORTERR | GIH_PHYSADDR;  /* flag bits */
	    /*
	     * Make sure we have a handle; we'll reuse it until lazy
	     * writes are turned off.
	     */
	    if (fFirstAsyncIO) {

         IncrHoldsBUF (pbuf);

		   pioh = GetIOHand(fIOHFlags,
			   	 LW_EXEC_LIST_SIZE,
				    ONESGLIST,pfrm);	    /* may block */

         DecrHoldsBUF (pbuf);
		   fFirstAsyncIO = FALSE;
	    }

	    /* Write pbuf and other buffers for this volume. */
	    LWAsync( pbuf,					/* may block */
		     pioh,
		     bPri,
		     fAddImmune,
		     fIOHFlags,
		     pfrm);


	} else { /* Must not support async I/O for this device... */

//test. label for forced strat1 test
strat1:
	    /* Make sure buffer has been allocated. */
	    if (! fGotNonAsyncIOBuf) {
		SEL sel;

      IncrHoldsBUF (pbuf);

		/* allocate the lazy write buffer */
		MAYBLOCK (
		    usRet = FSH_SEGALLOC (SA_FRING0,
					 (ULONG) LWBUFCT * SECSIZE * SPB,
					 &sel);
		);

      DecrHoldsBUF (pbuf);

		if (usRet == NO_ERROR) {

		    /* get a pointer to the lw buffer */
		    LWBuf = MAKEP (sel, 0);

		    fGotNonAsyncIOBuf = TRUE;

		} else {

		    /*
		     * Ignore error; we'll just try again later.  In the
		     * meantime, LWWrite can write blocks one at a time,
		     * without using LWBuf.
		     */
		}

	    }

	    /* Write this block, and others if possible. */
	    LWWrite(pbuf, usTypeLazyIO, pfrm);		      /* may block */
	}

/* turn off force flag when we're at half of the dirty buffers threshold
 * and the reason we're flushing out the cache is dirty buffers threshold
 * exceeded.
 */

   if (fForceLWIO) {
    if (cbufDirty > cbufDirtyVDU) {
     if (cbufDirty <= (LWData.cbufDirtyMax / 2)) {
       fForceLWIO = FALSE;
     }
    } else {
       fForceLWIO = FALSE;
    }
   }

	fLWIO = FALSE;
   usTypeLazyIO = 0;	/* reset flag  */

    } /* Loop for each time we can write... */

} /* LazyWriter() */

/***	LazyWriter2 - block and flush as required
 *
 *	LazyWriter2 is used by the 2nd lazy writer thread to wait until the
 *	cache contains old data or until there is dirty data and the disk
 *	appears to be idle.  After each block, we go back to the top to
 *	see if we can return.  We do this as blocking may change previously
 *	ascertained state.
 *
 *	Once we ascertain that there is data to flush, we write it out and then
 *	rescan the cache for either more writes or sleep time.
 *
 *	Entry	none
 *
 *	Exit	Returns error code, never returns if no error
 *		Yields
 *
 *	HISTORY
 *
 */
//#114986

USHORT far
LazyWriter2 (PFRAME pfrm)
{
    BYTE bPri = PRIO_LAZY_WRITE;	/* Priority (only used by AsyncIO stuff). */
    BOOL fAddImmune;
    BOOL fFirstAsyncIO = TRUE;
    PBUFNODE pbuf;
    ULONG msTmp;
    ULONG msSinceBoot;	/* current time */
    ULONG msBlock;	/* amount of time to block */
    PIOH pioh;			/* Async I/O handle. */
    USHORT usRet;
    USHORT iVol;
	 ULONG fIOHFlags;

#define LW_DirtyBufs		0		 /* write out any dirty buffer */
#define LW_OldBufs		1		 /* write out old (over-age) buffers */
#define LW_IdleBufs		2		 /* write out over-idle buffers */

#ifdef DEBUG
    dprintf ("LazyWriter2: starting up.\n");
#endif

    while (TRUE) { /* For each time we can write stuff... */

	while (TRUE) { /* Scan for something to write, or wait for it. */

	    /*
	     * We could have returned from the FSH_SEMSETWAIT (at the end
	     * of this loop) because StopLazyWriter told the lazy writer to
	     * stop by clearing the fLWDaemon and clearing the semaphore.
	     * Test for this case.  StopLazyWriter takes care of freeing the
	     * buffer that LWBuf points to, if necessary.
	     */

	   if (!fLWDaemon2) {
#ifdef DEBUG
		dprintf ("LazyWriter2: stopping gracefully\n");
#endif
	    /* start at the top of the LW queue */
        pbuf = bufLWHead.dcbpDirt.pbufFwd;

        while ((pbuf != (PBUFNODE) &bufLWHead) && (pbuf->fFlag & BFL_LWLOCK)) {
          pbuf = pbuf->dcbpDirt.pbufFwd;
        }

	     if (pbuf == (PBUFNODE) &bufLWHead) {
#ifdef DEBUG_LW
		 dprintf ("LazyWriter: no buffs\n");
#endif

		    if (! fFirstAsyncIO)
		      RelIOHand(pioh);

		  /* wake up anyone waiting for LazyWriter to complete */
		   SemClear (&semLW2Done);

		   return (NO_ERROR);

	     } else  /* dirty buffers exist; need to write them out to disk */
                /* so we can shutdown the lazy writer                  */
           fForceLWIO = TRUE;

      }

	    /* get the current time */
	    msSinceBoot = msNow ();

       /* force lazy IO if flag == TRUE by pretending disk is idle */
       if (fForceLWIO) {
         msLastIO = msSinceBoot - LWData.msIdle;
       }

	    /* start at the top of the LW queue */
	    pbuf = bufLWHead.dcbpDirt.pbufFwd;

       while ((pbuf != (PBUFNODE) &bufLWHead) && ((pbuf->fFlag & BFL_LWLOCK)
               || !(IsAsyncIOable(pbuf->lsnSec)))) {
         pbuf = pbuf->dcbpDirt.pbufFwd;
       }

	    if (pbuf == (PBUFNODE) &bufLWHead) {
#ifdef DEBUG_LW
		dprintf ("LazyWriter2: no buffs\n");
#endif
		msBlock = TO_INFINITE;
	    }

	    /* if there are more bufs dirty than we allow then write it */
	    else if ((cbufDirty > LWData.cbufDirtyMax) || (fForceLWIO == TRUE)) {

#ifdef DEBUG_LW
		dprintf ("LazyWriter2: cbufDirty=%d > max allowed (%d)\n",
			 cbufDirty, LWData.cbufDirtyMax);
#endif
		fAddImmune = TRUE;

		break;		/* goto lwb9 */
	    }

	    /* if age of buf is greater than we allow then write it */
	    else if ((msBlock = msSinceBoot - pbuf->msFirstDirtied) >=
							LWData.msDataAge) {
#ifdef DEBUG_LW
		dprintf ("LazyWriter2: buf too old, ");
		dprintf ("msFirstDirtied=%ld, msSinceBoot=%ld\n",
			 pbuf->msFirstDirtied, msSinceBoot);
#endif
		/* too old now, go flush */
		fAddImmune = FALSE;

		break;	/* goto lwb9 */
	    }
	    else {

		/* block time is ms til buff times out */
		   msBlock = LWData.msDataAge - msBlock;

		/*
		 * if disk is not idle..., msTmp gets the time that the
		 * disk has been idle.
		 */
		if ((msTmp = msSinceBoot - msLastIO) < LWData.msIdle) {
#ifdef DEBUG_LW
		    dprintf ("LazyWriter2: disk not idle, msSinceBoot=%ld, ",
			     msSinceBoot);
		    dprintf ("msLastIO=%ld\n", msLastIO);
#endif
		    msTmp = LWData.msIdle - msTmp; /* ms until idle time out */

		    /*
		     * block until either the disk is idle or the
		     * buffer times out
		     */
		    msBlock = MIN (msBlock, msTmp);
		} else {

		    /*
		     * Disk is idle.  Walk the cache to find minimum time until
		     * not-recently touched.
		     */
#ifdef DEBUG_LW
		    dprintf ("LazyWriter2: disk is idle, msSinceBoot=%ld, ",
			     msSinceBoot);
		    dprintf ("msLastIO=%ld\n", msLastIO);
#endif

/* lwb2: */

		    while (pbuf != (PBUFNODE) &bufLWHead) {

			/* if buf not recently touched, go write it */
			if ((msTmp = msSinceBoot - pbuf->msLastDirtied) >=
							    LWData.msIdleBuf) {

#ifdef DEBUG_LW
			    dprintf ("LazyWriter2: write it, msSinceBoot=%ld, ",
				     msSinceBoot);
			    dprintf ("pbuf->LastDirtied=%ld\n",
				     pbuf->msLastDirtied);
#endif

           if (fForceLWIO)
			     fAddImmune = TRUE;
             else
			     fAddImmune = FALSE;

			    goto lwb9;
			} else {
#ifdef DEBUG_LW
			    dprintf ("LazyWriter2: buf recently touched, msSinceBoot=%ld, ",
				     msSinceBoot);
			    dprintf ("pbuf->LastDirtied=%ld\n",
				     pbuf->msLastDirtied);
#endif
			    /*
			     * msTmp gets time left for buffer to remain
			     * untouched before it will time out.
			     */
			    msTmp = LWData.msIdleBuf - msTmp;
			    msBlock = MIN (msBlock, msTmp);
			    pbuf = pbuf->dcbpDirt.pbufFwd;
			}
		    } /* while... */
		} /* else (disk idle) */
	    } /* else */

#ifdef DEBUG_LW
	    dprintf ("LazyWriter2: calling fsh_semsetwait, msBlock=%ld\n",
		     msBlock);
#endif
	    /*
	     * Keep calling FSH_SEMSETWAIT while the return code is what we
	     * expect.  Proper return codes are:
	     *
	     * NO_ERROR - someone is trying to kill the lazy writer
	     *			   or if we ran out of buffers and the lazy
	     *			   writer was forced to wake up.
	     *
	     * ERROR_SEM_TIMEOUT - under normal conditions when the lazy
	     *			   writer figures it is time to wake up.
	     *
	     * For all others we call FSH_SEMSETWAIT again.
	     *
	     * Don't worry about adjusting the msBlock timeout since it
	     * will be a rare occurance when we get a bad return from
	     * FSH_SEMSETWAIT.  Even if we do, the longest time that this
	     * could ever be is the maximum dirty time for a buffer.
	     * No big deal if we block a little too long.
	     */
#ifdef DEBUG
	    usRet = NO_ERROR;
#endif
	    do {
#ifdef DEBUG
		/* print a message indicating an unexpected return code */
		if (usRet != NO_ERROR) {
		    dprintf ("LazyWriter2: FSH_SEMSETWAIT returned %d, %s\n",
			     usRet,
			     "waiting again!");
		}
#endif

		  MAYBLOCK (usRet = FSH_SEMSETWAIT (&semLW2, msBlock));

	    } while ((usRet != NO_ERROR) && (usRet != ERROR_SEM_TIMEOUT));

	} /* Loop scanning for stuff to write, or wait for it. */

lwb9:
	fLWIO = TRUE;

	/*
	 * Depending on whether or not device driver supports command chaining
	 * and scatter/gather, arrange for this buffer (and possibly others) to
	 * be written to disk.
	 */

    iVol = IVOLFROMLSN (pbuf->lsnSec);
    pfrm->pvol = VolPtr [iVol];

//test. force strat1
// goto strat1;

	    fIOHFlags = (pbuf->lsnSec & ~SECMASK)	      /* volbits */
			      | GIH_ABORTERR | GIH_PHYSADDR;  /* flag bits */
	    /*
	     * Make sure we have a handle; we'll reuse it until lazy
	     * writes are turned off.
	     */
	    if (fFirstAsyncIO) {

         IncrHoldsBUF (pbuf);

		   pioh = GetIOHand(fIOHFlags,
			   	 LW_EXEC_LIST_SIZE,
				    ONESGLIST,pfrm);	    /* may block */

         DecrHoldsBUF (pbuf);
		   fFirstAsyncIO = FALSE;
	    }

	    /* Write pbuf and other buffers for this volume. */
	    LWAsync( pbuf,					/* may block */
		     pioh,
		     bPri,
		     fAddImmune,
		     fIOHFlags,
		     pfrm);

/* turn off force flag when we're at half of the dirty buffers threshold
 * and the reason we're flushing out the cache is dirty buffers threshold
 * exceeded.
 */

   if (fForceLWIO) {
    if (cbufDirty > cbufDirtyVDU) {
     if (cbufDirty <= (LWData.cbufDirtyMax / 2)) {
       fForceLWIO = FALSE;
     }
    } else {
       fForceLWIO = FALSE;
    }
   }

	fLWIO = FALSE;

    } /* Loop for each time we can write... */

} /* LazyWriter2() */

/***	LazyWriter3 - block and flush as required
 *
 *	LazyWriter3 is used by the 3rd lazy writer thread to wait until the
 *	cache contains old data or until there is dirty data and the disk
 *	appears to be idle.  After each block, we go back to the top to
 *	see if we can return.  We do this as blocking may change previously
 *	ascertained state.
 *
 *	Once we ascertain that there is data to flush, we write it out and then
 *	rescan the cache for either more writes or sleep time.
 *
 *	Entry	none
 *
 *	Exit	Returns error code, never returns if no error
 *		Yields
 *
 *	HISTORY
 *
 */
//#114986

USHORT far
LazyWriter3 (PFRAME pfrm)
{
    BYTE bPri = PRIO_LAZY_WRITE;	/* Priority (only used by AsyncIO stuff). */
    BOOL fAddImmune;
    BOOL fFirstAsyncIO = TRUE;
    BOOL fGotNonAsyncIOBuf = FALSE;
    PBUFNODE pbuf;
    ULONG msTmp;
    ULONG msSinceBoot;	/* current time */
    ULONG msBlock;	/* amount of time to block */
    PIOH pioh;			/* Async I/O handle. */
    USHORT usRet;
    USHORT iVol;
	 ULONG fIOHFlags;

#define LW_DirtyBufs		0		 /* write out any dirty buffer */
#define LW_OldBufs		1		 /* write out old (over-age) buffers */
#define LW_IdleBufs		2		 /* write out over-idle buffers */


#ifdef DEBUG
    dprintf ("LazyWriter3: starting up.\n");
#endif

    while (TRUE) { /* For each time we can write stuff... */

	while (TRUE) { /* Scan for something to write, or wait for it. */

	    /*
	     * We could have returned from the FSH_SEMSETWAIT (at the end
	     * of this loop) because StopLazyWriter told the lazy writer to
	     * stop by clearing the fLWDaemon and clearing the semaphore.
	     * Test for this case.  StopLazyWriter takes care of freeing the
	     * buffer that LWBuf points to, if necessary.
	     */

	   if (!fLWDaemon3) {
#ifdef DEBUG
		dprintf ("LazyWriter3: stopping gracefully\n");
#endif

	    /* start at the top of the LW queue */
        pbuf = bufLWHead.dcbpDirt.pbufFwd;

        while ((pbuf != (PBUFNODE) &bufLWHead) && (pbuf->fFlag & BFL_LWLOCK)) {
          pbuf = pbuf->dcbpDirt.pbufFwd;
        }

	     if (pbuf == (PBUFNODE) &bufLWHead) {
#ifdef DEBUG_LW
		 dprintf ("LazyWriter: no buffs\n");
#endif

		    if (! fFirstAsyncIO)
		      RelIOHand(pioh);

		  /* wake up anyone waiting for LazyWriter to complete */
		   SemClear (&semLW3Done);

		   return (NO_ERROR);

	     } else  /* dirty buffers exist; need to write them out to disk */
                /* so we can shutdown the lazy writer                  */
           fForceLWIO = TRUE;

      }

	    /* get the current time */
	    msSinceBoot = msNow ();

       /* force lazy IO if flag == TRUE by pretending disk is idle */
       if (fForceLWIO) {
         msLastIO = msSinceBoot - LWData.msIdle;
       }

	    /* start at the top of the LW queue */
	    pbuf = bufLWHead.dcbpDirt.pbufFwd;

       while ((pbuf != (PBUFNODE) &bufLWHead) && ((pbuf->fFlag & BFL_LWLOCK)
               || !(IsAsyncIOable(pbuf->lsnSec)))) {
         pbuf = pbuf->dcbpDirt.pbufFwd;
       }

	    if (pbuf == (PBUFNODE) &bufLWHead) {
#ifdef DEBUG_LW
		dprintf ("LazyWriter3: no buffs\n");
#endif
		msBlock = TO_INFINITE;
	    }
	    /* if there are more bufs dirty than we allow then write it */
	    else if ((cbufDirty > LWData.cbufDirtyMax) || (fForceLWIO == TRUE)) {

#ifdef DEBUG_LW
		dprintf ("LazyWriter3: cbufDirty=%d > max allowed (%d)\n",
			 cbufDirty, LWData.cbufDirtyMax);
#endif
		fAddImmune = TRUE;

		break;		/* goto lwb9 */
	    }

	    /* if age of buf is greater than we allow then write it */
	    else if ((msBlock = msSinceBoot - pbuf->msFirstDirtied) >=
							LWData.msDataAge) {
#ifdef DEBUG_LW
		dprintf ("LazyWriter3: buf too old, ");
		dprintf ("msFirstDirtied=%ld, msSinceBoot=%ld\n",
			 pbuf->msFirstDirtied, msSinceBoot);
#endif
		/* too old now, go flush */
		fAddImmune = FALSE;

		break;	/* goto lwb9 */
	    }
	    else {

		/* block time is ms til buff times out */
		   msBlock = LWData.msDataAge - msBlock;

		/*
		 * if disk is not idle..., msTmp gets the time that the
		 * disk has been idle.
		 */
		if ((msTmp = msSinceBoot - msLastIO) < LWData.msIdle) {
#ifdef DEBUG_LW
		    dprintf ("LazyWriter3: disk not idle, msSinceBoot=%ld, ",
			     msSinceBoot);
		    dprintf ("msLastIO=%ld\n", msLastIO);
#endif
		    msTmp = LWData.msIdle - msTmp; /* ms until idle time out */

		    /*
		     * block until either the disk is idle or the
		     * buffer times out
		     */
		    msBlock = MIN (msBlock, msTmp);
		} else {

		    /*
		     * Disk is idle.  Walk the cache to find minimum time until
		     * not-recently touched.
		     */
#ifdef DEBUG_LW
		    dprintf ("LazyWriter3: disk is idle, msSinceBoot=%ld, ",
			     msSinceBoot);
		    dprintf ("msLastIO=%ld\n", msLastIO);
#endif

/* lwb2: */

		    while (pbuf != (PBUFNODE) &bufLWHead) {

			/* if buf not recently touched, go write it */
			if ((msTmp = msSinceBoot - pbuf->msLastDirtied) >=
							    LWData.msIdleBuf) {

#ifdef DEBUG_LW
			    dprintf ("LazyWriter3: write it, msSinceBoot=%ld, ",
				     msSinceBoot);
			    dprintf ("pbuf->LastDirtied=%ld\n",
				     pbuf->msLastDirtied);
#endif

           if (fForceLWIO)
			     fAddImmune = TRUE;
             else
			     fAddImmune = FALSE;

			    goto lwb9;
			} else {
#ifdef DEBUG_LW
			    dprintf ("LazyWriter3: buf recently touched, msSinceBoot=%ld, ",
				     msSinceBoot);
			    dprintf ("pbuf->LastDirtied=%ld\n",
				     pbuf->msLastDirtied);
#endif
			    /*
			     * msTmp gets time left for buffer to remain
			     * untouched before it will time out.
			     */
			    msTmp = LWData.msIdleBuf - msTmp;
			    msBlock = MIN (msBlock, msTmp);
			    pbuf = pbuf->dcbpDirt.pbufFwd;
			}
		    } /* while... */
		} /* else (disk idle) */
	    } /* else */

#ifdef DEBUG_LW
	    dprintf ("LazyWriter3: calling fsh_semsetwait, msBlock=%ld\n",
		     msBlock);
#endif
	    /*
	     * Keep calling FSH_SEMSETWAIT while the return code is what we
	     * expect.  Proper return codes are:
	     *
	     * NO_ERROR - someone is trying to kill the lazy writer
	     *			   or if we ran out of buffers and the lazy
	     *			   writer was forced to wake up.
	     *
	     * ERROR_SEM_TIMEOUT - under normal conditions when the lazy
	     *			   writer figures it is time to wake up.
	     *
	     * For all others we call FSH_SEMSETWAIT again.
	     *
	     * Don't worry about adjusting the msBlock timeout since it
	     * will be a rare occurance when we get a bad return from
	     * FSH_SEMSETWAIT.  Even if we do, the longest time that this
	     * could ever be is the maximum dirty time for a buffer.
	     * No big deal if we block a little too long.
	     */
#ifdef DEBUG
	    usRet = NO_ERROR;
#endif
	    do {
#ifdef DEBUG
		/* print a message indicating an unexpected return code */
		if (usRet != NO_ERROR) {
		    dprintf ("LazyWriter3: FSH_SEMSETWAIT returned %d, %s\n",
			     usRet,
			     "waiting again!");
		}
#endif

		MAYBLOCK (usRet = FSH_SEMSETWAIT (&semLW3, msBlock));

	    } while ((usRet != NO_ERROR) && (usRet != ERROR_SEM_TIMEOUT));

	} /* Loop scanning for stuff to write, or wait for it. */

lwb9:
	fLWIO = TRUE;

	/*
	 * Depending on whether or not device driver supports command chaining
	 * and scatter/gather, arrange for this buffer (and possibly others) to
	 * be written to disk.
	 */

    iVol = IVOLFROMLSN (pbuf->lsnSec);
    pfrm->pvol = VolPtr [iVol];

//test. force strat1
// goto strat1;

	    fIOHFlags = (pbuf->lsnSec & ~SECMASK)	  /* volbits */
			      | GIH_ABORTERR | GIH_PHYSADDR;  /* flag bits */
	    /*
	     * Make sure we have a handle; we'll reuse it until lazy
	     * writes are turned off.
	     */
	    if (fFirstAsyncIO) {

         IncrHoldsBUF (pbuf);

		   pioh = GetIOHand(fIOHFlags,
			   	 LW_EXEC_LIST_SIZE,
				    ONESGLIST,pfrm);	    /* may block */

         DecrHoldsBUF (pbuf);
		   fFirstAsyncIO = FALSE;
	    }

	    /* Write pbuf and other buffers for this volume. */
	    LWAsync( pbuf,					/* may block */
		     pioh,
		     bPri,
		     fAddImmune,
		     fIOHFlags,
		     pfrm);

/* turn off force flag when we're at half of the dirty buffers threshold
 * and the reason we're flushing out the cache is dirty buffers threshold
 * exceeded.
 */

   if (fForceLWIO) {
    if (cbufDirty > cbufDirtyVDU) {
     if (cbufDirty <= (LWData.cbufDirtyMax / 2)) {
       fForceLWIO = FALSE;
     }
    } else {
       fForceLWIO = FALSE;
    }
   }

	fLWIO = FALSE;

    } /* Loop for each time we can write... */

} /* LazyWriter3() */
