/* ** 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); }
/*{ ** 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; } } } } }
/* ** 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)); }
/* ** 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 */
/* ** 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 */
/* ** 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); }
/* ** 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); }
/*{ ** 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); }
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 ); }
/*{ ** ** 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); }
/*{ ** ** 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); }