/****************************************************************
 *								*
 * Copyright (c) 2007-2016 Fidelity National Information	*
 * Services, Inc. and/or its subsidiaries. All rights reserved.	*
 *								*
 *	This source code contains the intellectual property	*
 *	of its copyright holder(s), and is made available	*
 *	under a license.  If you do not know the terms of	*
 *	the license, please stop and do not read further.	*
 *								*
 ****************************************************************/

#include "mdef.h"

#include "gdsroot.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsblk.h"
#include "gdsfhead.h"
#include "interlock.h"
#include "gdsbgtr.h"
#include "sleep_cnt.h"
#include "wbox_test_init.h"

/* Include prototypes */
#include "wcs_sleep.h"
#include "is_proc_alive.h"
#include "wcs_read_in_progress_wait.h"
#include "add_inter.h"
#include "caller_id.h"

/* Waits for a concurrently running read (from disk into a global buffer) to complete.
 *
 * Returns TRUE if read completes within timeout of approx. 1 minute.
 * Returns FALSE otherwise.
 *
 * Similar logic is also present in t_qread and wcs_recover but they are different enough that
 * they have not been folded into this routine yet.
 */
boolean_t	wcs_read_in_progress_wait(cache_rec_ptr_t cr, wbtest_code_t wbox_test_code)
{	/* this type of lock does not match up well with general models for spin wait (it's too long) or mutex wait
	 * (probably too many buffers to reasonably maintain a mutex for each); it should notice if r_epid or cr->cycle
	 * are changing and in, at least, that case keep waiting; alert messages might be nice
	 */
	uint4	lcnt, r_epid;
	int4	n;

	for (lcnt = 1; -1 != cr->read_in_progress; lcnt++)
	{
		if (-1 > cr->read_in_progress)
		{	/* outside of design; clear to known state */
			assert(0 == cr->r_epid);
			cr->r_epid = 0;
			INTERLOCK_INIT(cr);
			break;
		}
		wcs_sleep(lcnt);
		GTM_WHITE_BOX_TEST(wbox_test_code, lcnt, (2 * BUF_OWNER_STUCK));
		if (BUF_OWNER_STUCK < lcnt)
		{	/* sick of waiting */
			/* Since cr->r_epid can be changing concurrently, take a local copy before using it below,
			 * particularly before calling is_proc_alive as we don't want to call it with a 0 r_epid.
			 */
			r_epid = cr->r_epid;
			if (0 != r_epid)
			{
				if (FALSE == is_proc_alive(r_epid, 0))
				{	/* process gone; release its lock */
					cr->r_epid = 0;
					RELEASE_BUFF_READ_LOCK(cr);
				} else
				{
					assert(gtm_white_box_test_case_enabled);
					return FALSE;
				}
			} else
			{	/* process stopped before could set r_epid */
				RELEASE_BUFF_READ_LOCK(cr);	/* cr->r_epid already 0 no need to reset */
				if (-1 > cr->read_in_progress)
				{	/* process released since if (cr->r_epid); rectify semaphore  */
					LOCK_BUFF_FOR_READ(cr, n);
				}
			}
		}	/* sick of waiting */
	}
	return TRUE;
}
