/****************************************************************
 *								*
 *	Copyright 2003, 2011 Fidelity Information Services, Inc	*
 *								*
 *	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 "gtm_string.h"
#if defined(UNIX)
#include "gtm_aio.h"
#elif defined(VMS)
#include "iosb_disk.h"
#include "iosp.h"	/* for SS_NORMAL */
#endif
#include "gdsroot.h"
#include "gdsbt.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsblk.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "jnl.h"
#include "buddy_list.h"
#include "hashtab_int4.h"	/* needed for muprec.h */
#include "hashtab_int8.h"	/* needed for muprec.h */
#include "hashtab_mname.h"	/* needed for muprec.h */
#include "hashtab.h"
#include "muprec.h"
#include "mur_read_file.h"

GBLREF 	jnl_gbls_t	jgbl;
GBLREF	mur_gbls_t	murgbl;
GBLREF	void		(*call_on_signal)();
GBLREF	unsigned char	t_fail_hist[CDB_MAX_TRIES];
GBLREF	boolean_t	gv_play_duplicate_kills;

#define	FREE_AND_NULLIFY_BUDDY_LIST(LIST)	\
{						\
	assert(NULL != LIST);			\
	if (NULL != LIST)			\
	{					\
		FREEUP_BUDDY_LIST(LIST);	\
		LIST = NULL;			\
	}					\
}

#define	FREE_AND_NULLIFY(PTR)		\
{					\
	assert(NULL != PTR);		\
	if (NULL != PTR)		\
	{				\
		free(PTR);		\
		PTR = NULL;		\
	}				\
}

/****************************************************************************************
 * Function Name: mur_init()							       	*
 * Input: None										*
 * Output: None										*
 ****************************************************************************************/

void mur_init(void)
{
	int	index;

	murgbl.multi_list = (buddy_list *)malloc(SIZEOF(buddy_list));
	initialize_list(murgbl.multi_list, SIZEOF(multi_struct), MUR_MULTI_LIST_INIT_ALLOC);
	murgbl.forw_multi_list = (buddy_list *)malloc(SIZEOF(buddy_list));
	initialize_list(murgbl.forw_multi_list, SIZEOF(forw_multi_struct), MUR_MULTI_LIST_INIT_ALLOC);
	init_hashtab_int8(&murgbl.token_table, MUR_MULTI_HASHTABLE_INIT_ELEMS, HASHTAB_COMPACT, HASHTAB_SPARE_TABLE);
	init_hashtab_int8(&murgbl.forw_token_table, MUR_MULTI_HASHTABLE_INIT_ELEMS, HASHTAB_COMPACT, HASHTAB_SPARE_TABLE);
	murgbl.pini_buddy_list = (buddy_list *)malloc(SIZEOF(buddy_list));
	initialize_list(murgbl.pini_buddy_list, SIZEOF(pini_list_struct), MUR_PINI_LIST_INIT_ELEMS);
	/* pini_list hash table of a jnl_ctl_list is initialized in mur_fopen */
	/* FAB of a jnl_ctl_list is initialized in mur_fopen */
	murgbl.resync_seqno = 0; 		/* interrupted rollback set this to non-zero value later */
	jgbl.mupip_journal = TRUE;	/* this is a MUPIP JOURNAL command */
	/* Write journal records for KILL even if node does not exist in this database. This way if any process had
	 * written a KILL record for a no-op kill (a kill of a node that does not exist; this is currently possible in
	 * the update process), recovery does the exact same thing and thereby keeps the db/jnl in sync.
	 */
	gv_play_duplicate_kills = TRUE;
	/* Because journal recovery operates with no one else touching the database, t_tries is going to be
	 * directly set to CDB_STAGNATE for every TP transaction (in op_tstart). So initialize
	 * t_fail_hist[0] through t_fail_hist[CDB_STAGNATE-1] to '0' for display purposes.
	 */
	for (index = 0; index < CDB_STAGNATE; index++)
		t_fail_hist[index] = '0';
	call_on_signal = mur_close_files;
	DEBUG_ONLY(assert_jrec_member_offsets();)
}

void	mur_free(void)
{
	FREE_AND_NULLIFY_BUDDY_LIST(murgbl.multi_list);
	FREE_AND_NULLIFY_BUDDY_LIST(murgbl.forw_multi_list);
	FREE_AND_NULLIFY_BUDDY_LIST(murgbl.pini_buddy_list);
	/* Note : In addition, there is a lot of mallocs done in mur_open_files (of rctls and jctls) that need to be freed.
	 * It is not considered as important since MUPIP is a one-command image activation so no memory leak issues.
	 */
}

void	mur_rctl_desc_alloc(reg_ctl_list *rctl)
{
	mur_read_desc_t			*mur_desc;

	mur_desc = (mur_read_desc_t *)malloc(SIZEOF(mur_read_desc_t));
	/*
	 * Layout of rctl->mur_desc buffers (consecutive buffers are contiguous)
	 *
	 * |<--MUR_BUFF_SIZE-->|<--MUR_BUFF_SIZE-->|<--MUR_BUFF_SIZE-->|<--MUR_BUFF_SIZE-->|<--MUR_BUFF_SIZE-->|
	 * |   random_buff     |     aux_buff1     |    seq_buff[0]    |    seq_buff[1]    |     aux_buff2     |
	 */
	assert(MUR_BUFF_SIZE > MAX_JNL_REC_SIZE);	/* in order for a journal record to fit in one buffer */
	mur_desc->alloc_base = malloc(5 * MUR_BUFF_SIZE); /* two for double buffering, two for auxiliary, one for random */
	mur_desc->random_buff.base = mur_desc->alloc_base;
	mur_desc->random_buff.top  = mur_desc->aux_buff1 = mur_desc->alloc_base + MUR_BUFF_SIZE;
	mur_desc->seq_buff[0].base = mur_desc->aux_buff1 + MUR_BUFF_SIZE;
	mur_desc->seq_buff[0].top = mur_desc->seq_buff[1].base = mur_desc->seq_buff[0].base + MUR_BUFF_SIZE;
	mur_desc->seq_buff[1].top = mur_desc->aux_buff2.base = mur_desc->seq_buff[1].base + MUR_BUFF_SIZE;
	mur_desc->aux_buff2.top = mur_desc->seq_buff[1].top + MUR_BUFF_SIZE;
	mur_desc->random_buff.read_in_progress = FALSE;
	mur_desc->seq_buff[0].read_in_progress = FALSE;
	mur_desc->seq_buff[1].read_in_progress = FALSE;
	mur_desc->aux_buff2.read_in_progress = FALSE;
#	if defined(UNIX) && defined(MUR_USE_AIO)
	mur_desc->seq_buff[0].aiocbp = (struct aiocb *)malloc(SIZEOF(struct aiocb));
	mur_desc->seq_buff[1].aiocbp = (struct aiocb *)malloc(SIZEOF(struct aiocb));
	memset((char *)mur_desc->seq_buff[0].aiocbp, 0, SIZEOF(struct aiocb));
	memset((char *)mur_desc->seq_buff[1].aiocbp, 0, SIZEOF(struct aiocb));
	mur_desc->aux_buff2.aiocbp = (struct aiocb *)NULL;	/* no aio for this buffer */
	mur_desc->random_buff.aiocbp = (struct aiocb *)NULL;	/* no aio for this buffer */
#	elif defined(VMS)
	mur_desc->random_buff.iosb.cond = SS_NORMAL;
	mur_desc->seq_buff[0].iosb.cond = SS_NORMAL;
	mur_desc->seq_buff[1].iosb.cond = SS_NORMAL;
#	endif
	rctl->mur_desc = mur_desc;
}

void	mur_rctl_desc_free(reg_ctl_list *rctl)
{
	int			index;
	mur_read_desc_t		*mur_desc;

	mur_desc = rctl->mur_desc;
	FREE_AND_NULLIFY(mur_desc->alloc_base);
#	if defined(UNIX) && defined(MUR_USE_AIO)
	for (index = 0; index <= 1; index++)
		FREE_AND_NULLIFY(mur_desc->seq_buff[index].aiocbp);
#	endif
	FREE_AND_NULLIFY(rctl->mur_desc); /* cannot use mur_desc since we want rctl->mur_desc (not mur_desc) to be NULLified */
}
