/*static char *SCCSID = "@(#)lazyvwrt.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_LAZY.h"
#include "pr_MISC.h"
#if defined(DEBUG) || defined(DEBUG_LW)
    #include "pr_DEBUG.h"
#endif

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


/***	LazyVDUWriter - block and flush VDU buffers as required
 *
 *	LazyVDUWriter is a lazy write thread that waits until the cache
 *	contains old VDU data or until there is dirty VDU 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
 *
 */

USHORT far
LazyVDUWriter (PFRAME pfrm)
{
    BOOL fGotNonAsyncIOBuf = FALSE;
    PBUFNODE pbuf;
    ULONG msTmp;
    ULONG msSinceBoot;	/* current time */
    ULONG msBlock;	/* amount of time to block */
    USHORT usRet;
    USHORT usTypeLazyIO;
    USHORT iVol;

#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 ("LazyVDUWriter: 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) {

	     if (cbufDirtyVDU == 0) { /* okay to stop if no more work to do */

		   return (NO_ERROR);

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

      }

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

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

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

	    /* if there are more bufs dirty than we allow then write it */
	    else if ( (cbufDirtyVDU > ((cBufs * 1)/2)) || (fForceVLWIO == TRUE) ||
	              ((cBufs < 256) && (cbufDirtyVDU > ((cBufs * 1)/4))) ) {

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

/* 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 ("LazyVDUWriter: buf too old, ");
		dprintf ("msFirstDirtied=%ld, msSinceBoot=%ld\n",
			 pbuf->msFirstDirtied, msSinceBoot);
#endif

/* 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 ("LazyVDUWriter: 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 ("LazyVDUWriter: disk is idle, msSinceBoot=%ld, ",
			     msSinceBoot);
		    dprintf ("msLastIO=%ld\n", msLastIO);
#endif

/* lwb2: */

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

			/* if buf not recently touched, go write it */
			if ((msTmp = msSinceBoot - pbuf->msLastDirtied) >=
							    LWData.msIdleBuf) {
#ifdef DEBUG_LW
			    dprintf ("LazyVDUWriter: write it, msSinceBoot=%ld, ",
				     msSinceBoot);
			    dprintf ("pbuf->LastDirtied=%ld\n",
				     pbuf->msLastDirtied);
#endif


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

			    goto lwb9;
			} else {
#ifdef DEBUG_LW
			    dprintf ("LazyVDUWriter: 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 ("LazyVDUWriter: 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 ("LazyVDUWriter: FSH_SEMSETWAIT returned %d, %s\n",
			     usRet,
			     "waiting again!");
		}
#endif

		MAYBLOCK (usRet = FSH_SEMSETWAIT (&semVLW, 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];

	    /* 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 */
		    VLWBuf = MAKEP (sel, 0);

		    fGotNonAsyncIOBuf = TRUE;

		}

	    }

	    /* Write this block, and others if possible. */
	    VLWrite(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 ( (fForceVLWIO) &&
        (cbufDirtyVDU < cBufs / 2) ) {
      fForceVLWIO = FALSE;
    }

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

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

} /* LazyVDUWriter() */
