Пример #1
0
/*
** Name: WSMSetTimeout() - Set the timeout system
**
** Description:
**
** Inputs:
**	USR_PSESSION *usr_session
**
** Outputs:
**
** Returns:
**	GSTATUS	:	GSTAT_OK
**
** Exceptions:
**	None
**
** Side Effects:
**	None
**
** History:
*/
GSTATUS
WSMSetTimeout(
	USR_PSESSION usr_session) 
{
	GSTATUS err = GSTAT_OK;
	
	i4 user_timeout;
	if (usr_session->userid == SYSTEM_ID)
		user_timeout = system_timeout;
	else		
		err = DDSGetUserTimeout(usr_session->userid, &user_timeout);

	if (err == GSTAT_OK)
	{
		err = WSMRemoveTimeout(usr_session);
		if (err == GSTAT_OK)
		{
			usr_session->timeout_end = TMsecs() + user_timeout;
			if (youngest == NULL)
			{
				youngest = usr_session;
				oldest = usr_session;
			}
			else
			{
				usr_session->timeout_previous = youngest;
				youngest->timeout_next = usr_session;
				youngest = usr_session;
			}
		}
	}
return(err);
}
Пример #2
0
/*{
** Name:	RSstats_update - update statistics
**
** Description:
**	Updates the Replicator Server statistics.
**
** Inputs:
**	target_db	- target database number
**	table_no	- table number
**	txn_type	- transaction type
**
** Outputs:
**	none
**
** Returns:
**	none
*/
void
RSstats_update(
i2	target_db,
i4	table_no,
i4	txn_type)
{
	RS_TARGET_STATS	*targ;
	RS_TABLE_STATS	*tbl;
	RS_TARGET_STATS	*targ_end;

	if (!mon_stats)
		return;
	targ_end = (RS_TARGET_STATS *)((PTR)mon_stats->target_stats +
		mon_stats->num_targets * (targ_size + tbl_size *
		mon_stats->num_tables));
	for (targ = mon_stats->target_stats; targ < targ_end; targ =
		(RS_TARGET_STATS *)((PTR)targ + targ_size + tbl_size *
		mon_stats->num_tables))
	{
		if (target_db == targ->db_no)
		{
			if (table_no == 0)
			{
				if (!targ->first_txn_time)
					targ->first_txn_time = TMsecs();
				++targ->num_txns;
				targ->last_txn_time = TMsecs();
				break;
			}
			if (!shared_stats)
				break;
			for (tbl = targ->table_stats; tbl < targ->table_stats +
				mon_stats->num_tables; ++tbl)
			{
				if (table_no == tbl->table_no)
				{
					++tbl->num_rows[txn_type - 1];
					break;
				}
			}
		}
	}
}
Пример #3
0
/*
** Name: ddc_dbtimeout_get
**
** Description:
**
**
** Inputs:
**
**      offset          offset to add to base object pointer.
**      objsize         size of object (ignored)
**      obj             pointer to bas object
**      userbuflen      size of the user buffer
**
** Outputs:
**
**      userbuf         updated user buffer with the cursor owner name.
**
** Returns:
**      OK                  successfully completed
**      MO_VALUE_TRUNCATED  buffer too short
**
** History:
**      02-Jul-98 (fanra01)
**          Created.
**
*/
STATUS
ddc_dbtimeout_get (i4 offset, i4 objsize, PTR obj, i4 userbuflen,
                   char * userbuf)
{
    PDB_CACHED_SESSION  session = (PDB_CACHED_SESSION) obj;
	i4	left = (session->timeout_end == 0) ? 0 : session->timeout_end - TMsecs();

	return(MOlongout (MO_VALUE_TRUNCATED, left,
               userbuflen, userbuf));
}
Пример #4
0
/*
**  Name: PurgeFree      - Purge old free connections
**
**  Description:
**      Scan the free index.
**	Mark all entries.
**	if an entry is marked twice drop the connection and dealloc the entry.
**
**  Inputs:
**
**  Outputs:
**
**  Returns:
**
**  History:
**      02-Mar-98 (shero03)
**          Created.
*/
void ccm_PurgeFree(void)
{
    LNKDEF	**list;
    LNKDEF	*entry = NULL;
    LNKDEF      *nentry;
    IIAPI_GETEINFOPARM lerr;
    CM_CONN_BLK *pConn;

    if (HSH_AreEntries(pCM_cb->cm_free_conn_index))
    {
        HSH_GetLock(pCM_cb->cm_free_conn_index); /* Lock the Index */

    	while (TRUE)
        {
            HSH_Next(pCM_cb->cm_free_conn_index, &list, &entry, &nentry);
            if (entry == NULL)      /* At end ? */
                break;             /* Y. leave */
            pConn = LL_GetObject(entry);
            if (pConn->cm_conn_hndl == NULL)
                pConn->cm_conn_hndl = (PTR)1;
            else
            {
        	HSH_Unlink(pCM_cb->cm_free_conn_index, 
			(char *)&pConn->cm_conn_DB,
			sizeof(PTR)*3,
			&pConn->cm_conn_lnk);     /* remove it from free */

                if (pConn->cm_conn_flags == CM_CONN_API)
                    pCM_cb->cm_api_disconnect((PTR)&pConn->cm_conn_hndl, TRUE, 
		    				(PTR)&lerr);
                else
                    pCM_cb->cm_libq_disconnect((PTR)&pConn->cm_conn_hndl, TRUE, 
		    				(PTR)&lerr);

			/* Note that the entry is being freed with the used list */
			/* Only the used list does the allocates and deallocates */
			/* The entry in fact, isn't in either index! */
    		HSH_DeallocEntry(pCM_cb->cm_used_conn_index, &pConn->cm_conn_lnk);
            }
        }

        HSH_ReleaseLock(pCM_cb->cm_free_conn_index); /* release the lock */
    }

    /* FIXME - make the time interval user modifiable */
    pCM_cb->cm_tm_next_purge = TMsecs() + 5 * 60;	/* 5 min	*/

    return;
}		/* proc - PurgeFree */
Пример #5
0
/*
**  Name: CM_Initialize      - Starts the Connection Manager
**
**  Description:
**      Initialize the Connection Manager so it can accept Connect & Drop
**	During initialization the Connection Manager calls the HSH facility
**	HSH in turn allocates memory which will be freed when
**	CM_Termainte is called.  Otherwise a memory leak  will occur.
**
**  Inputs:
**
**  Outputs:
**      err		- Pointer to a error block
**
**  Returns:
**      OK if successful.
**
**  History:
**      25-Feb-98 (shero03)
**          Created.
**	01-Apr-1999 (shero03)
**	    Fixed compiler errors after api change
*/
II_EXTERN STATUS II_EXPORT
IIcm_Initialize (PTR err)
{
    i4 		flag;
    CS_SCB	*scb;

    if (pCM_cb)
        return(E_AP0006_INVALID_SEQUENCE);

     pCM_cb = &CM_cb;

    CM_cb.cm_conn_ctr = 0;
    CM_cb.cm_reuse_ctr = 0;
    CM_cb.cm_drop_ctr = 0;
    CM_cb.cm_cleanup_ctr = 0;
    CM_cb.cm_flags = 0;
    CM_cb.cm_api_disconnect = NULL;
    CM_cb.cm_libq_disconnect = NULL;

    CSget_scb(&scb);
    if (scb && CS_is_mt())
       CM_cb.cm_flags |= CM_FLAG_MULTITHREADED;
    /* FIXME - make the time interval user modifiable */
    CM_cb.cm_tm_next_purge = TMsecs() + 5 * 60;	/* 5 min	*/

    /* Initialize the Hash Indexes */
    flag = HSH_SHARED | HSH_VARIABLE | HSH_STRKEY;
    CM_cb.cm_db_index = HSH_New(NUM_KEY_INDEX, CM_KEY_OFF,
    				sizeof(CM_KEY_BLK) + 20,
    			 	1, MAXI4, "CM_DB_SEM", flag);
    CM_cb.cm_userid_index = HSH_New(NUM_KEY_INDEX, CM_KEY_OFF,
    				sizeof(CM_KEY_BLK) + 8,
    			 	1, MAXI4, "CM_USERID_SEM", flag);
    flag = HSH_SHARED;
    CM_cb.cm_used_conn_index = HSH_New(NUM_CONN_INDEX, CM_CONN_OFF_HNDL,
    				sizeof(CM_CONN_BLK),
    			 	1, MAXI4, "CM_CONN_USED_SEM", flag);
    CM_cb.cm_free_conn_index = HSH_New(NUM_CONN_INDEX, CM_CONN_OFF_DB,
    				sizeof(CM_CONN_BLK),
    			 	0, 0, "CM_CONN_FREE_SEM", flag);

    return IIAPI_ST_SUCCESS;
}		/* proc  CM_Initialize */
Пример #6
0
/*
** Name: WSMRequestUsrSession() - Retrieve a user session from the cookie
**
** Description:
**
** Inputs:
**	char*	: cookie
**
** Outputs:
**	USR_PSESSION*	: user session (NULL if doesn't exist)
**
** Returns:
**	GSTATUS	:	GSTAT_OK
**
** Exceptions:
**	None
**
** Side Effects:
**	None
**
** History:
**      04-Oct-2001 (fanra01)
**          Add a test for an invalid session or a session that is no longer
**          in the hash table and return an error.
*/
GSTATUS 
WSMRequestUsrSession (
    char         *cookie,
    USR_PSESSION *session)
{
    GSTATUS err = GSTAT_OK;
    u_i4    i = 0;
    USR_PSESSION tmp = NULL;

    if (cookie != NULL && cookie[0] != EOS)
    {
        err = DDFSemOpen(&usrSessSemaphore, TRUE);
        if (err == GSTAT_OK)
        {
            err = DDFgethash(
                    usr_sessions,
                    cookie,
                    STlength(cookie),
                    (PTR*) &tmp);
            /*
            ** If the object is not in the hash table, raise and error.
            */
            if (tmp == NULL)
            {
                err = DDFStatusAlloc( E_WS0011_WSS_NO_OPEN_SESSION );
            }
            if (tmp != NULL &&
                tmp->timeout_end < TMsecs())
                tmp = NULL;

            if (tmp != NULL)
                tmp->requested_counter++;

            CLEAR_STATUS(DDFSemClose(&usrSessSemaphore));
        }
    }

    *session = tmp;
    return(err);
}
Пример #7
0
/*
** Name: WSMCleanUserTimeout() - Clean the timeout system
**
** Description:
**
** Inputs:
**
** Outputs:
**
** Returns:
**	GSTATUS	:	GSTAT_OK
**
** Exceptions:
**	None
**
** Side Effects:
**	None
**
** History:
*/
GSTATUS
WSMCleanUserTimeout() 
{
	GSTATUS err = GSTAT_OK;
	USR_PSESSION tmp = oldest;
	USR_PSESSION next;
	i4 limit = TMsecs();

	err = DDFSemOpen(&usrSessSemaphore, TRUE);
	if (err == GSTAT_OK)
	{
		while (tmp != NULL &&
		   tmp->timeout_end < limit)
		{
			next = tmp->timeout_next; 
			CLEAR_STATUS(WSMRemoveUsrSession(tmp));
			tmp = next;
		}
		CLEAR_STATUS(DDFSemClose(&usrSessSemaphore));
	}
return(err);
}
Пример #8
0
/*{
** Name:	RSstats_init - initialize monitor statistics
**
** Description:
**	Initializes the Replicator Server statistics memory segment.
**
** Inputs:
**	none
**
** Outputs:
**	none
**
** Returns:
**	OK	Function completed normally.
*/
STATUS
RSstats_init()
{
	STATUS		status;
	char		*val;
	i4		num_targs;
	SIZE_TYPE	pages_alloc;
	u_i4	stats_size;
	RS_TARGET_STATS	*targ;
	RS_TARGET_STATS	*targ_end;
	RS_TABLE_STATS	*tbl;
	RS_CONN		*conn;
	RS_TBLDESC	*tbl_info;
	CL_SYS_ERR	sys_err;
	char		server_num[4];

	PMsetDefault(1, PMhost());
	STprintf(server_num, ERx("%d"), (i4)RSserver_no);
	PMsetDefault(3, server_num);
	status = PMget(ERx("II.$.REPSERV.$.SHARED_STATISTICS"), &val);
	if (status == OK && STbcompare(val, 0, ERx("ON"), 0, TRUE) == 0)
		shared_stats = TRUE;
	num_targs = RSnum_conns - TARG_BASE;
	stats_size = sizeof(RS_MONITOR) +
		sizeof(RS_MONITOR) % sizeof(ALIGN_RESTRICT);
	targ_size = sizeof(RS_TARGET_STATS) +
		sizeof(RS_TARGET_STATS) % sizeof(ALIGN_RESTRICT);
	if (shared_stats)
		tbl_size = sizeof(RS_TABLE_STATS) + sizeof(RS_TABLE_STATS) %
			sizeof(ALIGN_RESTRICT);
	else
		tbl_size = 0;
	num_pages = (stats_size + num_targs * (targ_size + RSrdf_svcb.num_tbls *
		tbl_size)) / ME_MPAGESIZE + 1;
	if (shared_stats)
	{
		STprintf(sm_key, ERx("%s.%03d"), RS_STATS_FILE, RSserver_no);
		status = MEget_pages(ME_SSHARED_MASK | ME_CREATE_MASK |
			ME_MZERO_MASK | ME_NOTPERM_MASK, num_pages, sm_key,
			(PTR *)&mon_stats, &pages_alloc, &sys_err);
		if (status == ME_ALREADY_EXISTS)
		{
			status = MEsmdestroy(sm_key, &sys_err);
			if (status == OK)
				status = MEget_pages(ME_SSHARED_MASK |
					ME_CREATE_MASK | ME_MZERO_MASK |
					ME_NOTPERM_MASK, num_pages, sm_key,
					(PTR *)&mon_stats, &pages_alloc,
					&sys_err);
		}
	}
	else
	{
		mon_stats = (RS_MONITOR *)MEreqmem(0, num_pages * ME_MPAGESIZE,
			TRUE, &status);
	}
	if (status != OK)
		return (status);
	mon_stats->server_no = RSserver_no;
	mon_stats->local_db_no = RSlocal_conn.db_no;
	PCpid(&mon_stats->pid);
	mon_stats->startup_time = TMsecs();
	mon_stats->num_targets = num_targs;
	mon_stats->num_tables = RSrdf_svcb.num_tbls;
	STcopy(RSlocal_conn.vnode_name, mon_stats->vnode_name);
	STcopy(RSlocal_conn.db_name, mon_stats->db_name);
	mon_stats->target_stats = (RS_TARGET_STATS *)((PTR)mon_stats +
		stats_size);
	targ_end = (RS_TARGET_STATS *)((PTR)mon_stats->target_stats +
		mon_stats->num_targets * (targ_size + tbl_size *
		mon_stats->num_tables));
	for (conn = &RSconns[TARG_BASE], targ = mon_stats->target_stats;
		targ < targ_end; ++conn, targ = (RS_TARGET_STATS *)((PTR)targ +
		targ_size + tbl_size * mon_stats->num_tables))
	{
		targ->db_no = conn->db_no;
		STcopy(conn->vnode_name, targ->vnode_name);
		STcopy(conn->db_name, targ->db_name);
		if (shared_stats)
		{
			targ->table_stats = (RS_TABLE_STATS *)((PTR)targ +
				targ_size);
			for (tbl_info = RSrdf_svcb.tbl_info,
				tbl = targ->table_stats;
				tbl < targ->table_stats + mon_stats->num_tables;
				++tbl, ++tbl_info)
			{
				tbl->table_no = tbl_info->table_no;
				STcopy(tbl_info->table_owner, tbl->table_owner);
				STcopy(tbl_info->table_name, tbl->table_name);
			}
		}
	}

	return (OK);
}
Пример #9
0
static STATUS
initialize( i4 argc, char **argv )
{
    CL_ERR_DESC	cl_err;
    char	*instance = ERx("*");
    char	*env;
    char	name[ 16 ];
    i4		i;

    GCD_global.language = 1;
    MHsrand( TMsecs() );

    for( i = 1; i < argc; i++ )
	if ( ! STbcompare( argv[i], 0, ERx("-instance"), 9, TRUE ) )
	{
	    if ( argv[i][9] == ERx('=')  &&  argv[i][10] != EOS )
		instance = &argv[i][10];
	    else  if ( argv[i][9] == EOS  &&  (i + 1) < argc )
		instance = argv[++i];
	    break;
	}

    NMgtAt( ERx("II_INSTALLATION"), &env );
    STprintf( name, ERx("II_CHARSET%s"), (env  &&  *env) ? env : "" );
    NMgtAt( name, &env );

    if ( ! env  ||  ! *env || STlength(env) > CM_MAXATTRNAME)
    {
	switch( CMgetDefCS() )
	{
#if ( !defined(UNIX) && !defined(VMS) )
	case CM_IBM :	    env = "IBMPC850";	break;
#endif
	case CM_DECMULTI :  env = "DECMULTI";	break;
	default :	    env = "ISO88591";	break;
	}
    }

    GCD_global.charset = STalloc( env );
    CVupper( GCD_global.charset );
    gcu_read_cset( gcd_cset_id );

    if ( CMset_attr( GCD_global.charset, &cl_err) != OK )
    {
	gcu_erlog( 0, GCD_global.language, 
		    E_GC0105_GCN_CHAR_INIT, NULL, 0, NULL );
	return( E_GC0105_GCN_CHAR_INIT );
    }

    PMinit();

    switch( PMload( (LOCATION *)NULL, (PM_ERR_FUNC *)NULL ) )
    {
	case OK: break;

	case PM_FILE_BAD:
	    gcu_erlog( 0, GCD_global.language, 
			E_GC003D_BAD_PMFILE, NULL, 0, NULL );
	    return( E_GC003D_BAD_PMFILE );
	    break;

	default:
	    gcu_erlog( 0, GCD_global.language, 
			E_GC003E_PMLOAD_ERR, NULL, 0, NULL );
	    return( E_GC003E_PMLOAD_ERR );
	    break;
    }

    PMsetDefault( 0, SystemCfgPrefix );
    PMsetDefault( 1, PMhost() );
    PMsetDefault( 2, ERx("gcd") );
    PMsetDefault( 3, instance );

    gcd_tl_services[0] = &gcd_dmtl_service;
    gcd_tl_services[1] = &gcd_jctl_service;
    GCD_global.tl_services = gcd_tl_services;
    GCD_global.tl_srvc_cnt = ARR_SIZE( gcd_tl_services );

    QUinit( &GCD_global.pib_q );
    QUinit( &GCD_global.rcb_q );
    QUinit( &GCD_global.ccb_q );
    QUinit( &GCD_global.ccb_free );
    for( i = 0; i < ARR_SIZE( GCD_global.cib_free ); i++ )  
	QUinit( &GCD_global.cib_free[ i ] );

    env = NULL;
    gcu_get_tracesym( NULL, ERx("!.client_max"), &env );
    if ( env  &&  *env )
    {
	i4 count;

	if ( CVal( env, &count ) == OK  &&  count > 0 )
	    GCD_global.client_max = count;
    }

    env = NULL;
    gcu_get_tracesym( NULL, ERx("!.client_timeout"), &env );
    if ( env  &&  *env )  
    {
	i4 timeout;

	if ( CVal( env, &timeout ) == OK  &&  timeout > 0 )
	    GCD_global.client_idle_limit = timeout * 60; /* Cnvt to seconds */
    }

    env = NULL;
    gcu_get_tracesym( NULL, ERx("!.connect_pool_status"), &env );
    if ( env  &&  *env )
    {
	if ( ! STbcompare( env, 0, "optional", 0, TRUE ) )
	    GCD_global.client_pooling = TRUE;

	if ( GCD_global.client_pooling  ||
	     ! STbcompare( env, 0, "on", 0, TRUE ) )
	{
	    env = NULL;
	    gcu_get_tracesym( NULL, ERx("!.connect_pool_size"), &env );
	    if ( env  &&  *env )  CVal( env, &GCD_global.pool_max );

	    env = NULL;
	    gcu_get_tracesym( NULL, ERx("!.connect_pool_expire"), &env );
	    if ( env  &&  *env )  
	    {
		i4 limit;

		if ( CVal( env, &limit ) == OK  &&  limit > 0 )
		    GCD_global.pool_idle_limit = limit * 60; /* Seconds */
	    }
	}
    }

    env = NULL;
    gcu_get_tracesym( "II_GCD_TRACE", ERx("!.gcd_trace_level"), &env );
    if ( env  &&  *env )  CVal( env, &GCD_global.gcd_trace_level );

    env = NULL;
    gcu_get_tracesym( "II_GCD_LOG", ERx("!.gcd_trace_log"), &env );
    if ( env  &&  *env ) 
	TRset_file( TR_F_OPEN, env, (i4)STlength( env ), &cl_err );

    return( OK );
}
Пример #10
0
/*{
**
** Name: dmc_write_along   -  asynchronous, unobtrusive  write along thread
**
** EXTERNAL call format:	status = dmf_call(DMC_WRITE_ALONG, &dmc_cb);
**
** Description:
**	The dmc_write_along routine provides an unobtrusive version of the  
**	write behind threads found in normal servers.  It's job is similar,
**	writing dirty pages out of the cache to make room for new pages,
**	but the following differences exist:
**        Write Behind threads			Write Along Threads
**	  ---------------------------    -----------------------------------
**	Run as part of same servers	Run in a dedicated IO server so that SMP
**	that process user threads	CPUs are better utilized, and UNIX
**					priorities may be used for balancing
**
**	Runs on any type of UNIX	Will only come up on an SMP machine
**	hardware.			with 2 or more CPUs.
**
**	Contend against user threads 	If there is an available CPU, only
**	from same server, and cause	contention is against other service
**	context switching overhead.	threads in this IO server, eg the
**					new ReadAhead threads.
**
**	Are woken all at once, in a  	Are woken up periodically so that 
**	'panic' when low on buffers, 	I/O is spread out more evenly.
**	causing spikes in processing.
**	
**	All threads in all servers	The I/O master server is given a
**	check all modified buffers, 	list of databases it is to operate on
**	always attempting to 'fix' a   	and keeps them open. When a buffer
**	TBIO and ignoring this buf if	otherwise qualifies to be written, if
**	cant get TBIO. This is alot	the table is not already open (and its
**	of thrashing, eg if the same	one of the desired database) the table
**    	tables happen not to be open	is opened. Thus there is less senseless
**	in all servers.			spinning around the cache.
**
**	The modified queues are   	At the cost of some extra cpu time  	
**    	scanned, which is smart, but	(on the surface), the entire buffer
**    	requires holding the buffer	header array is scanned for candidates.
**    	manager mutex, which is bad !.	This kind of scan can be done without
**    	This causes dead waits.		the buffer manager mutex, so the
**    					scan does not get in the way of other
**    					concurrent operations. The mutex is
**    					taken only when needed to alter the
**    					status of a chosen buffer.
**    	
**    	Because WB threads operate in	Because WA can afford to be more
**    	panic mode, and must write out	picky, only buffers that will not
**    	buffers right away, no regard	cause a log force (old lsns) will be
**    	is given to the fact that a	picked to be written. This will
**    	log force may occur due to a	reduce the amount of log records
**    	new lsn on the buffer.		written, LG mutexes etc...
**    	
**    	
**	The dmc_write_along  routine should only be called within a special
**	session that is dedicated for this purpose.  This routine will not
**	return under normal circumstances until server shutdown time.
**
**	This routines wakes up periodically, and calls dm0p_write_along(),
**	which writes pages in a manner that is less intrusive than the
**	dm0p_flush_pages() routines used by normal write behind threads.
**	dm0p_write_along() will not request or hold the buffer manager
**	mutes while looking for victims, instead it does a sequential
**	scan through the main buffer array. Other differences are listed
**	in the above chart, and in the function header.
**
**	This routine will return only if the timer                          
**	is cancelled by an interrupt.  At server shutdown time, the server
**	is expected to interrupt all such service threads.      
**
** Inputs:
**     dmc_cb
** 	.type		    Must be set to DMC_CONTROL_CB.
** 	.length		    Must be at least sizeof(DMC_CB).
**
** Outputs:
**     dmc_cb
** 	.error.err_code	    One of the following error numbers.
**			    E_DB_OK
**			    E_DM004A_INTERNAL_ERROR
**			    E_DM004B_LOCK_QUOTA_EXCEED
**			    E_DM0062_TRAN_QUOTA_EXCEED
**			    E_DM0163_WRITE_ALONG_FAILURE
**
** Returns:
**     E_DB_OK
**     E_DB_FATAL
**
** History:
**	05-Apr-1995 (cohmi01)
**	    Created, as part of the Manmanx research.
**	10-jan-1996 (dougb)
**	    To get this file to allow links on VMS platform, use CSswitch()
**	    not the internal Unix CL routine CL_swuser().  Also, printf()
**	    should never be called from generic code.
*/
DB_STATUS
dmc_write_along(
DMC_CB	    *dmc_cb)
{
    DMC_CB	    *dmc = dmc_cb;
    DM_SVCB	    *svcb = dmf_svcb;
    DB_TRAN_ID	    tran_id;
    LG_LXID	    lx_id;
    DM0L_ADDDB	    add_info;
    TIMERSTAT	    stat_block;
    i4	    lock_list;
    i4		    len_add_info;
    i4		    event_mask;
    i4		    events, wakeup_event;
    i4		    have_locklist = FALSE;
    i4		    have_transaction = FALSE;
    DB_STATUS	    status = E_DB_OK;
    i4	    wbcount = 0;
    i4	    base_time = 0;
    i4	    flush_time, new_time;
    i4	    length;
    i4	    lgd_status;
#define WA_RUNAGAIN     0     /* indicate no sleep desired */
#define WA_SLEEP        1     /* normal sleep interval if buffers empty */
#define WA_STALL        5     /* sleep interval for log full */
#define WA_YIELD       -1     /* yield to another thread */
    i4	    wa_interval = WA_SLEEP;         
    i4	    numforce = 0;
#define MAX_CLEAN   50
    i4         numclean = 0;
    STATUS	    stat;
    i4	    error;
    CL_ERR_DESC	    sys_err;
    DB_OWN_NAME	    user_name;
    LG_DBID	    lg_dbid;
    static i4  nextwa_threadno = 0;
    i4	    wa_threadno;
    i4	    duties[] = {DM0P_WA_SINGLE | DM0P_WA_GROUPS,
				DM0P_WA_SINGLE,
				DM0P_WA_SINGLE,
                                DM0P_WA_GROUPS};
    i4   	    duty;
#define NUM_DUTY (sizeof(duties)/sizeof (i4))

    CLRDBERR(&dmc->error);

    wa_threadno = nextwa_threadno++;
    duty = duties[wa_threadno % NUM_DUTY];

#ifdef xDEBUG
    TRdisplay(
	"Starting server Write Along Thread for server id 0x%x, duties 0x%x\n",
	      dmc_cb->dmc_id, duty );
#endif

    /*
    ** Add write along  thread to logging system.
    ** Write behind thread does not actually open a database, so use
    ** the LG_NOTDB flag.
    */
    STmove((PTR)DB_BWRITALONG_THREAD, ' ', sizeof(add_info.ad_dbname),
	(PTR) &add_info.ad_dbname);
    MEcopy((PTR)DB_INGRES_NAME, sizeof(add_info.ad_dbowner),
	(PTR) &add_info.ad_dbowner);
    MEcopy((PTR)"None", 4, (PTR) &add_info.ad_root);
    add_info.ad_dbid = 0;
    add_info.ad_l_root = 4;
    len_add_info = sizeof(add_info) - sizeof(add_info.ad_root) + 4;

    stat = LGadd(dmf_svcb->svcb_lctx_ptr->lctx_lgid, LG_NOTDB, (char *)&add_info, 
	len_add_info, &lg_dbid, &sys_err);
    if (stat != OK)
    {
	uleFormat(NULL, stat, (CL_ERR_DESC *)&sys_err, 
	    ULE_LOG, NULL, (char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
	uleFormat(NULL, E_DM900A_BAD_LOG_DBADD, &sys_err, ULE_LOG, NULL,
	    (char *)NULL, 0L, (i4 *)NULL, &error, 4, 0,
	    dmf_svcb->svcb_lctx_ptr->lctx_lgid,
	    sizeof(add_info.ad_dbname), (PTR) &add_info.ad_dbname,
	    sizeof(add_info.ad_dbowner), (PTR) &add_info.ad_dbowner,
	    4, (PTR) &add_info.ad_root);
	if (stat == LG_EXCEED_LIMIT)
	    SETDBERR(&dmc->error, 0, E_DM0062_TRAN_QUOTA_EXCEEDED);
	else
	    SETDBERR(&dmc->error, 0, E_DM0163_WRITE_ALONG_FAILURE);
	return (E_DB_ERROR);
    }


    for(;;)
    {
	/*
	** Begin transaction in order to do LG and LK calls.
	** Must specify NOPROTECT transaction so that LG won't pick us
	** as a force-abort victim.  Also, the Log File BOF can be advanced
	** past this transaction's position in the log file, which means that
	** the Write Along thread should do no logging nor work that could
	** require backout.
	*/
	STmove((PTR)DB_BWRITALONG_THREAD, ' ', sizeof(DB_OWN_NAME), 
							(PTR) &user_name);
	stat = LGbegin(LG_NOPROTECT, lg_dbid, &tran_id, &lx_id,
	    sizeof(DB_OWN_NAME), user_name.db_own_name,
	    (DB_DIS_TRAN_ID*)NULL, &sys_err);
	if (stat != OK)
	{
	    uleFormat(NULL, stat, (CL_ERR_DESC *)&sys_err, 
		ULE_LOG, NULL, (char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
	    uleFormat(NULL, E_DM900C_BAD_LOG_BEGIN, &sys_err, ULE_LOG, NULL, 
		(char *)NULL, (i4)0, (i4 *)NULL, &error, 1, 
		0, lg_dbid);
	    if (stat == LG_EXCEED_LIMIT)
		SETDBERR(&dmc->error, 0, E_DM0062_TRAN_QUOTA_EXCEEDED);
	    else
		SETDBERR(&dmc->error, 0, E_DM0163_WRITE_ALONG_FAILURE);
	    status = E_DB_ERROR;
	    break;
	}
	have_transaction = TRUE;

	/*
	** Create locklist to use to wait for Write Behind event.
	*/
	stat = LKcreate_list(LK_NONPROTECT, (i4) 0,
	    (LK_UNIQUE *)&tran_id, (LK_LLID *)&lock_list, (i4)0, 
	    &sys_err);
	if (stat != OK)
	{
	    uleFormat(NULL, stat, (CL_ERR_DESC *)&sys_err, 
		ULE_LOG, NULL, (char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
	    uleFormat(NULL, E_DM901A_BAD_LOCK_CREATE, &sys_err, ULE_LOG, NULL, 
		(char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
	    if (stat == LK_NOLOCKS)
		SETDBERR(&dmc->error, 0, E_DM004B_LOCK_QUOTA_EXCEEDED);
	    else
		SETDBERR(&dmc->error, 0, E_DM0163_WRITE_ALONG_FAILURE);
	    status = E_DB_ERROR;
	    break;
	}
	have_locklist = TRUE;

	/*
	** Now begin timer loop for periodically flushing the buffer manager.
	*/
	for (;;)
	{
	    if (DMZ_ASY_MACRO(2))
	    {
		new_time = TMsecs();
		flush_time = new_time - base_time;
		base_time = new_time;

		/* Write Write Along  thread statistics. */
		stat = CSstatistics(&stat_block, 0);
		TRdisplay("%22*- DMF Write Along Thread statistics %21*-\n");
		TRdisplay("    Write Along wakeups: %d    Cpu : %d    Dio : %d\n",
		    wbcount, stat_block.stat_cpu, stat_block.stat_dio);
		TRdisplay("    Time to flush pages: %d seconds\n",
		    flush_time);
		TRdisplay("%79*-\n");
	    }

	    /*
	    ** Wait for some interval before the next pass over the buffers.
	    ** This should return with "timed-out"; if it returns with
	    ** "interrupted", then the server is being shut down. If it
	    ** returns with any other return code, something is awry.
	    */
	    if (wa_interval == WA_YIELD)
	    {
	        /*
	        ** Note:  This routine will yield only to other threads at
	        ** the same or higher priority.
		*/
		CS_swuser();
	    }
	    else
	    if (wa_interval != WA_RUNAGAIN)
	    {
		stat = CSsuspend(CS_TIMEOUT_MASK | CS_INTERRUPT_MASK, 
			    wa_interval, 0);
		if (stat == E_CS0008_INTERRUPTED)
		{
		    status = E_DB_OK;
		    break;  /* server shut-down */
		}
		if (stat != E_CS0009_TIMEOUT)
		{
		    uleFormat(NULL, stat, (CL_ERR_DESC *)&sys_err, 
			ULE_LOG, NULL, (char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
		    SETDBERR(&dmc->error, 0, E_DM0163_WRITE_ALONG_FAILURE);
		    status = E_DB_ERROR;
		    break;
		}
	    }
	    
	    /*
	    ** Check LOGFULL status.  We don't execute write behind when in
	    ** logfull to avoid background log forces which wreak havoc on
	    ** the recovery logspace reservation algorithms.
	    */
	    stat = LGshow(LG_S_LGSTS, (PTR)&lgd_status, 
		    sizeof(lgd_status), &length, &sys_err);
	    if (stat != OK)
	    {
		uleFormat(NULL, stat, (CL_ERR_DESC *)&sys_err, 
		    ULE_LOG, NULL, (char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
		uleFormat(NULL, E_DM9017_BAD_LOG_SHOW, &sys_err, ULE_LOG, NULL, 
		    (char *)NULL, (i4)0, (i4 *)NULL, &error, 1, 
		    0, LG_S_LGSTS);
		SETDBERR(&dmc->error, 0, E_DM0163_WRITE_ALONG_FAILURE);
		status = E_DB_ERROR;
		break;
	    }

	    /*
	    ** If logfull continue back to the top of the loop to bypass
	    ** the cache flush and re-wait for timer  interval.
	    ** Use MAX_WA_INTERVAL to pause while log-full is resolved.
	    */
	    if (lgd_status & LGD_LOGFULL)
	    {
		wa_interval = WA_STALL;        
		continue;   /* bypassing flush during logfull */
	    }

	    wbcount++;
	    
	    /*
	    ** Flush some dirty pages out of the Buffer Manager.
	    */
	    status = dm0p_write_along(lock_list, (i4)lx_id, 
		&numforce, duty, &dmc->error);
	    if (status != E_DB_OK)
	    {
		if (dmc->error.err_code > E_DM_INTERNAL)
		{
		    uleFormat(&dmc->error, 0, NULL, ULE_LOG, NULL, 
			(char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
		    SETDBERR(&dmc->error, 0, E_DM0163_WRITE_ALONG_FAILURE);
		}
		break;
	    }

	    /* determine next wait period based on how busy we were */
	    /* if we didnt do much, sleep, else ru thru buffers again */
	    if (numforce == 0)
	    {
		wa_interval = WA_SLEEP;  /* things are calm, good nite*/
	    }
	    else
	    {
		wa_interval = WA_RUNAGAIN; /* hit the road again */
	    }

	}

	break;
    }

    /*
    ** Clean up transaction or lock list left hanging around.
    */
    if (have_transaction)
    {
	stat = LGend(lx_id, 0, &sys_err);
	if (stat != OK)
	{
	    uleFormat(NULL, stat, (CL_ERR_DESC *)&sys_err, 
		ULE_LOG, NULL, (char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
	    uleFormat(NULL, E_DM900E_BAD_LOG_END, &sys_err, ULE_LOG, NULL, 
		(char *)NULL, (i4)0, (i4 *)NULL, &error, 1, 0, lx_id);
	    if ( status == E_DB_OK )
	    {
		SETDBERR(&dmc->error, 0, E_DM0163_WRITE_ALONG_FAILURE);
		status = E_DB_ERROR;
	    }
	}
	have_transaction = FALSE;
    }
    if (have_locklist)
    {
	stat = LKrelease(LK_ALL, lock_list, (LK_LKID *)0, (LK_LOCK_KEY *)0,
	    (LK_VALUE *)0, &sys_err);
	if (stat != OK)
	{
	    uleFormat(NULL, stat, (CL_ERR_DESC *)&sys_err, 
		ULE_LOG, NULL, (char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
	    uleFormat(NULL, E_DM901B_BAD_LOCK_RELEASE, &sys_err, ULE_LOG, NULL,
		(char *)NULL, (i4)0, (i4 *)NULL, &error, 1, 0, lock_list);
	    if ( status == E_DB_OK )
	    {
		SETDBERR(&dmc->error, 0, E_DM0163_WRITE_ALONG_FAILURE);
		status = E_DB_ERROR;
	    }
	}
	have_locklist = FALSE;
    }

    stat = LGremove(lg_dbid, &sys_err);
    if (stat != OK)
    {
	uleFormat(NULL, stat, (CL_ERR_DESC *)&sys_err, 
	    ULE_LOG, NULL, (char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
	uleFormat(NULL, E_DM9016_BAD_LOG_REMOVE, &sys_err, ULE_LOG, NULL,
	    (char *)NULL, (i4)0, (i4 *)NULL, &error, 1, 0, lg_dbid);
	if ( status == E_DB_OK )
	{
	    SETDBERR(&dmc->error, 0, E_DM0163_WRITE_ALONG_FAILURE);
	    status = E_DB_ERROR;
	}
    }

    /* Write thread statistics. */
    stat = CSstatistics(&stat_block, 0);
    TRdisplay("\n%22*- DMF Write Along Thread statistics %21*-\n");
    TRdisplay("    Write Along wakeup: %d    Cpu : %d    Dio : %d\n",
	wbcount, stat_block.stat_cpu, stat_block.stat_dio);
    TRdisplay("%79*-\n");

    return (status);
}
Пример #11
0
/*{
**
** Name: dmc_write_behind_common  -  the guts of a write behind thread
**
** Description:
**
**	The dmc_write_behind routine is used for implementing an asynchronous
**	write behind thread.  It wakes up whenever signaled by an LK event
**	and writes dirty pages out of the cache to make room for new pages
**	to be read in.
**
**	The dmc_write_behind routine should only be called within a special
**	session that is dedicated for this purpose.  This routine will not
**	return under normal circumstances until server shutdown time.
**
**	This routine uses two routines in DM0P to drive the write behind
**	thread:
**	    DM0P_BMFLUSH_WAIT waits for a session in the buffer manager
**	    to signal the event to wake up the write behind threads.  This
**	    is signalled when some specified percent of the buffer manager
**	    is filled with dirty pages.
**
**	    DM0P_FLUSH_PAGES goes through the buffer manager modified queue
**	    in reverse priority order writing pages until some specified
**	    percentage of the buffer manager is free.
**
**	This routine will return only if the event wait in DM0P_BMFLUSH_WAIT
**	is cancelled by an interrupt.  At server shutdown time, the server
**	is expected to interrupt all the write behind threads.
**
**	This common code is executed by both Primary and Cloned
**	WriteBehind agents.
**
** Inputs:
**	i_am_a_clone		FALSE if this is the Primary WB Agent,
**				TRUE if a Clone.
**	cfa			Agent's data.
**
** Outputs:
**     dmf_err
** 	.error.err_code	    One of the following error numbers.
**			    E_DB_OK
**			    E_DM004A_INTERNAL_ERROR
**			    E_DM004B_LOCK_QUOTA_EXCEED
**			    E_DM0062_TRAN_QUOTA_EXCEED
**			    E_DM0117_WRITE_BEHIND
**
** Returns:
**     E_DB_OK
**     E_DB_FATAL
**
** History:
**      30-jun-1988 (rogerk)
**          Created for Jupiter.      
**      30-Jan-1989 (ac)
**          Added arguments to LGbegin().      
**	15-may-1989 (rogerk)
**	    Return resource errors if resource limit is exceeded.
**      2-oct-1992 (ed)
**          Use DB_MAXNAME to replace hard coded numbers
**          - also created defines to replace hard coded character strings
**          dependent on DB_MAXNAME
**	18-oct-1993 (rogerk)
**	    Add check for LOGFULL status.  We don't execute write behind when
**	    in logfull to avoid background log forces which wreak havoc on
**	    the recovery logspace reservation algorithms.
**	10-oct-93 (swm)
**	    Bug #56438
**	    Put LG_DBID into automatic variable lg_dbid rather than overloading
**	    dmc_cb->dmc_db_id.
**	31-jan-1994 (bryanp) B58380, B58381
**	    Log LG/LK status code if LG or LK call fails.
**	    Check return code from CSsuspend.
**	10-Mar-1998 (jenjo02)
**	    Support for demand-driven WriteBehind threads. Changed prototype
**	    to pass a boolean indicating whether this is the primary or
**	    cloned WB thread and a pointer to DB_ERROR instead of a pointer
**	    to DMC_CB.
**	    Made this a common function called by Primary and Cloned threads.
*/
static DB_STATUS
dmc_write_behind_common(
i4	    i_am_a_clone,
char	    *cfa,
DB_ERROR    *dmf_err)
{
    DM_SVCB	    *svcb = dmf_svcb;
    DB_TRAN_ID	    tran_id;
    LG_LXID	    lx_id;
    DM0L_ADDDB	    add_info;
    TIMERSTAT	    stat_block;
    i4	    lock_list;
    i4		    len_add_info;
    i4		    event_mask;
    i4		    events, wakeup_event;
    i4		    have_locklist = FALSE;
    i4		    have_transaction = FALSE;
    i4		    lg_added = FALSE;
    DB_STATUS	    status = E_DB_OK;
    i4	    wbcount = 0;
    i4	    wait_time = 0;
    i4	    base_time = 0;
    i4	    flush_time, new_time;
    i4	    length;
    i4	    lgd_status;
    STATUS	    stat;
    i4	    error;
    CL_ERR_DESC	    sys_err;
    DB_OWN_NAME	    user_name;
    LG_DBID	    lg_dbid;

#ifdef xDEBUG
    CS_SID	sid;
    i4	pid;

    PCpid(&pid);
    CSget_sid(&sid);

    TRdisplay("Starting Write Behind Thread %x in server process %d\n",
	sid, pid);
#endif

    CLRDBERR(dmf_err);

    if (status == E_DB_OK)
    {
	/*
	** Add write behind thread to logging system.
	** Write behind thread does not actually open a database, so use
	** the LG_NOTDB flag.
	*/
	STmove((PTR)DB_WRITEBEHIND_THREAD, ' ', sizeof(add_info.ad_dbname),
	    (PTR) &add_info.ad_dbname);
	MEcopy((PTR)DB_INGRES_NAME, sizeof(add_info.ad_dbowner),
	    (PTR) &add_info.ad_dbowner);
	MEcopy((PTR)"None", 4, (PTR) &add_info.ad_root);
	add_info.ad_dbid = 0;
	add_info.ad_l_root = 4;
	len_add_info = sizeof(add_info) - sizeof(add_info.ad_root) + 4;

	stat = LGadd(dmf_svcb->svcb_lctx_ptr->lctx_lgid, LG_NOTDB,
	    (char *)&add_info, 
	    len_add_info, &lg_dbid, &sys_err);
	if (stat != OK)
	{
	    uleFormat(NULL, stat, (CL_ERR_DESC *)&sys_err, 
		ULE_LOG, NULL, (char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
	    uleFormat(NULL, E_DM900A_BAD_LOG_DBADD, &sys_err, ULE_LOG, NULL,
		(char *)NULL, 0L, (i4 *)NULL, &error, 4, 0,
		dmf_svcb->svcb_lctx_ptr->lctx_lgid,
		sizeof(add_info.ad_dbname), (PTR) &add_info.ad_dbname,
		sizeof(add_info.ad_dbowner), (PTR) &add_info.ad_dbowner,
		4, (PTR) &add_info.ad_root);
	    if (stat == LG_EXCEED_LIMIT)
		SETDBERR(dmf_err, 0, E_DM0062_TRAN_QUOTA_EXCEEDED);
	    else
		SETDBERR(dmf_err, 0, E_DM0117_WRITE_BEHIND);
	    status = E_DB_ERROR;
	}
	else
	    lg_added = TRUE;
    }

    if (status == E_DB_OK)
    {
	/*
	** Begin transaction in order to do LG and LK calls.
	** Must specify NOPROTECT transaction so that LG won't pick us
	** as a force-abort victim.  Also, the Log File BOF can be advanced
	** past this transaction's position in the log file, which means that
	** the Write Behind thread should do no logging nor work that could
	** require backout.
	*/
	STmove((PTR)DB_WRITEBEHIND_THROWN, ' ', sizeof(DB_OWN_NAME), 
							(PTR) &user_name);
	stat = LGbegin(LG_NOPROTECT, lg_dbid, &tran_id, &lx_id,
	    sizeof(DB_OWN_NAME), user_name.db_own_name, 
	    (DB_DIS_TRAN_ID*)NULL, &sys_err);
	if (stat != OK)
	{
	    uleFormat(NULL, stat, (CL_ERR_DESC *)&sys_err, 
		ULE_LOG, NULL, (char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
	    uleFormat(NULL, E_DM900C_BAD_LOG_BEGIN, &sys_err, ULE_LOG, NULL, 
		(char *)NULL, (i4)0, (i4 *)NULL, &error, 1, 
		0, lg_dbid);
	    if (stat == LG_EXCEED_LIMIT)
		SETDBERR(dmf_err, 0, E_DM0062_TRAN_QUOTA_EXCEEDED);
	    else
		SETDBERR(dmf_err, 0, E_DM0117_WRITE_BEHIND);
	    status = E_DB_ERROR;
	}
	else
	    have_transaction = TRUE;
    }

    if (status == E_DB_OK)
    {
	/*
	** Create locklist to use to wait for Write Behind event.
	*/
	stat = LKcreate_list(LK_NONPROTECT, (i4) 0,
	    (LK_UNIQUE *)&tran_id, (LK_LLID *)&lock_list, (i4)0, 
	    &sys_err);
	if (stat != OK)
	{
	    uleFormat(NULL, stat, (CL_ERR_DESC *)&sys_err, 
		ULE_LOG, NULL, (char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
	    uleFormat(NULL, E_DM901A_BAD_LOCK_CREATE, &sys_err, ULE_LOG, NULL, 
		(char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
	    if (stat == LK_NOLOCKS)
		SETDBERR(dmf_err, 0, E_DM004B_LOCK_QUOTA_EXCEEDED);
	    else
		SETDBERR(dmf_err, 0, E_DM0117_WRITE_BEHIND);
	    status = E_DB_ERROR;
	}
	else
	    have_locklist = TRUE;
    }

    if (status == E_DB_OK)
    {
	/*
	** Now begin loop of waiting for Write Behind event and flushing
	** the buffer manager.
	*/
	do
	{
	    if (DMZ_ASY_MACRO(2))
	    {
		new_time = TMsecs();
		flush_time = new_time - base_time - wait_time;
		base_time = new_time;

		/* Write Write Behind thread statistics. */
		stat = CSstatistics(&stat_block, 0);
		TRdisplay("%22*- DMF Write Behind Thread statistics %21*-\n");
		TRdisplay("    Write Behind wakeups: %d    Cpu : %d    Dio : %d\n",
		    wbcount, stat_block.stat_cpu, stat_block.stat_dio);
		TRdisplay("    Time waiting for event: %d seconds\n",
		    wait_time);
		TRdisplay("    Time to flush pages: %d seconds\n",
		    flush_time);
		TRdisplay("%79*-\n");
	    }

	    /*
	    ** Cloned threads don't wait for a signal, they just
	    ** help flush the cache, then go away.
	    */
	    if (i_am_a_clone == FALSE)
	    {
		/*
		** Wait for the next signal that the buffer manager needs to have
		** pages flushed.
		**
		** This routine will also clear the event from the previous 
		** signal.
		*/
		status = dm0p_wbflush_wait(cfa, lock_list, dmf_err);
		if (status != E_DB_OK)
		{
		    /*
		    ** If warning is returned, that's a signal that
		    ** this thread is to terminate.
		    */
		    if (status == E_DB_WARN)
		    {
			status = E_DB_OK;
			break;
		    }
		    else
		    {
			if (dmf_err->err_code > E_DM_INTERNAL)
			{
			    uleFormat(dmf_err, 0, NULL, ULE_LOG, NULL, 
			    	(char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
			    SETDBERR(dmf_err, 0, E_DM0117_WRITE_BEHIND);
			}
			break;
		    }
		}
	    }

	    /*
	    ** Check LOGFULL status.  We don't execute write behind when in
	    ** logfull to avoid background log forces which wreak havoc on
	    ** the recovery logspace reservation algorithms.
	    */
	    stat = LGshow(LG_S_LGSTS, (PTR)&lgd_status, 
			    sizeof(lgd_status), &length, &sys_err);
	    if (stat != OK)
	    {
		uleFormat(NULL, stat, (CL_ERR_DESC *)&sys_err, 
		    ULE_LOG, NULL, (char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
		uleFormat(NULL, E_DM9017_BAD_LOG_SHOW, &sys_err, ULE_LOG, NULL, 
		    (char *)NULL, (i4)0, (i4 *)NULL, &error, 1, 
		    0, LG_S_LGSTS);
		SETDBERR(dmf_err, 0, E_DM0117_WRITE_BEHIND);
		status = E_DB_ERROR;
		break;
	    }

	    /*
	    ** If logfull, skip the cache flush.
	    */
	    if (lgd_status & LGD_LOGFULL)
	    {
		/*
		** Pause for a moment since the write-behind event will likely
		** be immediately resignaled. We expect that this 5-second
		** wait will return with "timed-out"; if it returns with
		** "interrupted", then the server is being shut down. If it
		** returns with any other return code, something is awry.
		*/
		stat = CSsuspend(CS_TIMEOUT_MASK | CS_INTERRUPT_MASK, 5, 0);
		if (stat == E_CS0008_INTERRUPTED)
		{
		    status = E_DB_OK;
		    break;
		}
		if (stat != E_CS0009_TIMEOUT)
		{
		    uleFormat(NULL, stat, (CL_ERR_DESC *)&sys_err, 
			ULE_LOG, NULL, (char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
		    SETDBERR(dmf_err, 0, E_DM0117_WRITE_BEHIND);
		    status = E_DB_ERROR;
		    break;
		}
	    }
	    else
	    {
		/*
		** Flush some dirty pages out of the Buffer Manager.
		*/

		if (dmf_svcb->svcb_status & SVCB_IOMASTER) 
		{
		    /* in IOMASTER server use same func as write-along thread */
		    i4 numforce;
		    u_i4 duty = 0xffffffff;
		    status = dm0p_write_along(lock_list, (i4)lx_id, 
			    &numforce, duty, dmf_err);
		}
		else
		    status = dm0p_flush_pages(lock_list, (i4)lx_id, 
				    cfa,
				    dmf_err);

		if (status != E_DB_OK)
		{
		    if (dmf_err->err_code > E_DM_INTERNAL)
		    {
			uleFormat(dmf_err, 0, NULL, ULE_LOG, NULL, 
			    (char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
			SETDBERR(dmf_err, 0, E_DM0117_WRITE_BEHIND);
		    }
		    break;
		}
	    }

	    /*
	    ** If dumping statistics, save time for event to be signaled.
	    */
	    if (DMZ_ASY_MACRO(2))
		wait_time = TMsecs() - base_time;
	    wbcount++;

	} while (i_am_a_clone == FALSE);
    }

    if (i_am_a_clone == FALSE)
    {
	/* Write Fast Commit thread statistics. */
	stat = CSstatistics(&stat_block, 0);
	TRdisplay("\n%22*- DMF Write Behind Thread statistics %21*-\n");
	TRdisplay("    Write Behind wakeup: %d    Cpu : %d    Dio : %d\n",
	    wbcount, stat_block.stat_cpu, stat_block.stat_dio);
	TRdisplay("%79*-\n");
    }

    /*
    ** Clean up transaction and/or lock list left hanging around.
    */
    if (have_transaction)
    {
	stat = LGend(lx_id, 0, &sys_err);
	if (stat != OK)
	{
	    uleFormat(NULL, stat, (CL_ERR_DESC *)&sys_err, 
		ULE_LOG, NULL, (char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
	    uleFormat(NULL, E_DM900E_BAD_LOG_END, &sys_err, ULE_LOG, NULL, 
		(char *)NULL, (i4)0, (i4 *)NULL, &error, 1, 0, lx_id);
	    if ( status == E_DB_OK )
	    {
		SETDBERR(dmf_err, 0, E_DM0117_WRITE_BEHIND);
		status = E_DB_ERROR;
	    }
	}
	have_transaction = FALSE;
    }

    if (have_locklist)
    {
	stat = LKrelease(LK_ALL, lock_list, (LK_LKID *)0, (LK_LOCK_KEY *)0,
	    (LK_VALUE *)0, &sys_err);
	if (stat != OK)
	{
	    uleFormat(NULL, stat, (CL_ERR_DESC *)&sys_err, 
		ULE_LOG, NULL, (char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
	    uleFormat(NULL, E_DM901B_BAD_LOCK_RELEASE, &sys_err, ULE_LOG, NULL, 
		(char *)NULL, (i4)0, (i4 *)NULL, &error, 1, 0, lock_list);
	    if ( status == E_DB_OK )
	    {
		SETDBERR(dmf_err, 0, E_DM0117_WRITE_BEHIND);
		status = E_DB_ERROR;
	    }
	}
	have_locklist = FALSE;
    }

    if (lg_added)
    {
	stat = LGremove(lg_dbid, &sys_err);
	if (stat != OK)
	{
	    uleFormat(NULL, stat, (CL_ERR_DESC *)&sys_err, 
		ULE_LOG, NULL, (char *)NULL, (i4)0, (i4 *)NULL, &error, 0);
	    uleFormat(NULL, E_DM9016_BAD_LOG_REMOVE, &sys_err, ULE_LOG, NULL, 
		    (char *)NULL, (i4)0, (i4 *)NULL, &error, 1, 0, lg_dbid);
	    if ( status == E_DB_OK )
	    {
		SETDBERR(dmf_err, 0, E_DM0117_WRITE_BEHIND);
		status = E_DB_ERROR;
	    }
	}
    }

    return (status);
}