/*++ Copyright (c) 1987-1993 Microsoft Corporation Module Name: watchd.c Abstract: Contains watchdog thread - does all timing work for REPL client. Author: Ported from Lan Man 2.1 Environment: User mode only. Contains NT-specific code. Requires ANSI C extensions: slash-slash comments, long external names. Revision History: 11-Apr-1989 (yuv) Initial Coding. 21-Oct-1991 (cliffv) Ported to NT. Converted to NT style. 11-Dec-1991 JohnRo Changed to allow MIPS builds. 16-Jan-1992 JohnRo Changed file name from repl.h to replgbl.h to avoid MIDL conflict. Added debug print of thread ID. Changed to use NetLock.h (allow shared locks, for one thing). 24-Jan-1992 JohnRo Changed to use LPTSTR etc. 10-Feb-1992 JohnRo Set up to dynamically change role. Use FORMAT equates. 15-Feb-1992 JohnRo Added debug message when thread ends. 24-Mar-1992 JohnRo Added comments about which thread(s) call various routines. 19-Aug-1992 JohnRo RAID 2115: repl svc should wait while stopping or changing role. Use PREFIX_ equates. 24-Mar-1993 JohnRo RAID 4267: Replicator has problems when work queue gets large. Always do debug output if unexpected value from WaitForSingleObject. Made some changes suggested by PC-LINT 5.0 31-Mar-1993 JohnRo Repl watchdog should scan queues too. Minor debug output changes. --*/ #include #include #include // ALERT_* defines #include // NELOG_* defines #include // DBGSTATIC .. #include // Lock data types, functions, and macros. #include // PREFIX_ equates. #include // FORMAT_NET_THREAD_ID, NetpCurrentThread(). #include // STRCPY(), etc. // // Local include files // #include // ReplWatchdThread(). #include // IF_DEBUG(). #include // ReplGlobal variables. DBGSTATIC VOID ReplQuePut( IN DWORD timeout_type, IN LPTSTR master, IN LPTSTR dir_name ) /*++ Routine Description: Puts a timeout message onto work_que for syncer to act upon. Arguments: timeout_type - The type of this timeout. One of the *_TIMEOUT defines from repldefs.h. master - master's name. dir_name - dir's name. Return Value: None. Threads: Only called by watchd thread. --*/ { PSMLBUF_REC que_buf; PQUERY_MSG msg_buf; // // Allocate a small queue entry. // Ignore errors, they are reported by the called function. // que_buf = ReplClientGetPoolEntry( QUESML_POOL ); if ( que_buf == NULL) { return; } // // Build the message. // msg_buf = (PQUERY_MSG )(que_buf->data); msg_buf->header.msg_type = timeout_type; (void) STRCPY(msg_buf->header.sender, master); (void) STRCPY(msg_buf->dir_name, dir_name); // // Put the message in the Work Queue. // ReplInsertWorkQueue( (LPVOID) que_buf ); IF_DEBUG(WATCHD) { NetpKdPrint(( PREFIX_REPL_CLIENT "Watch dog fired a timer event " "(message type = " FORMAT_DWORD ").\n", timeout_type )); } } DBGSTATIC VOID ReplCheckTimerList( VOID ) /*++ Routine Description: Goes thru each dir entry in client list and checks for PULSE_TIMEOUT, GUARD_TIMEOUT and then goes thru list of dupl_masters checking for DUPL_TIMEOUT. A timer is active if != 0, a timer fires when it becomes 0. Arguments: None. Return Value: None. Threads: Only called by watchd thread. --*/ { PCLIENT_LIST_REC cur_rec; LPTSTR DirName; PDUPL_MASTERS_REC dupl; // // Make sure no one is touching the list. // ACQUIRE_LOCK_SHARED( RCGlobalClientListLock ); // // Loop through each directory entry in the client list. // for ( cur_rec = RCGlobalClientListHeader; cur_rec != NULL; cur_rec = cur_rec->next_p ) { DirName = cur_rec->dir_name; // // If we've got a message in the queue for this, that's good enough. // (Scan delay list and work queue.) // if (ReplScanQueuesForMoreRecentMsg( DirName )) { continue; } // // If the timer is running on this directory entry, // decrement the timer. // If the time is then up, // Let the syncer know. // if (cur_rec->timer.timeout != 0) { if (--(cur_rec->timer.timeout) == 0) { ReplQuePut( cur_rec->timer.type, cur_rec->master, DirName ); } } // // If the guard timer is running on this directory entry, // decrement the timer. // If the time is then up, // let the syncer know. // if (cur_rec->timer.grd_timeout != 0) { if (--(cur_rec->timer.grd_timeout) == 0) { ReplQuePut( GUARD_TIMEOUT, cur_rec->master, DirName ); } } // // Loop for each duplicate master for this directory entry. // ACQUIRE_LOCK_SHARED( RCGlobalDuplListLock ); for ( dupl = cur_rec->dupl.next_p; dupl != NULL; dupl = dupl->next_p ) { // // If this duplicate master is waiting for something, // decrement the timer. // If the time is then up, // let the syncer know. // if (dupl->sleep_time != 0) { if (--(dupl->sleep_time) == 0) { ReplQuePut( DUPL_TIMEOUT, dupl->master, DirName ); } } } RELEASE_LOCK( RCGlobalDuplListLock ); } RELEASE_LOCK( RCGlobalClientListLock ); } DBGSTATIC VOID ReplCheckDelayQueue( VOID ) /*++ Routine Description: Goes thru delay list, if any message's time is up, puts it into work_que for syncer thread to do work, and releases it from delay_list. Arguments: None. Return Value: None. Threads: Only called by watchd thread. --*/ { PBIGBUF_REC *delay_rec; PBIGBUF_REC curr_rec; // // Make sure no one's touching the list // ACQUIRE_LOCK( RCGlobalDelayListLock ); delay_rec = &RCGlobalDelayListHeader; while ( *delay_rec != NULL) { curr_rec = *delay_rec; // // Check if the time is up for this entry. // if (--(curr_rec->delay) == 0) { // // get it off delay list // *delay_rec = curr_rec->next_p; // // Put on work_que for syncer to take care of // ReplInsertWorkQueue( curr_rec ); IF_DEBUG(WATCHD) { NetpKdPrint(( PREFIX_REPL_CLIENT "Watch dog fired a delay event.\n" )); } } else { delay_rec = &curr_rec->next_p; } } RELEASE_LOCK( RCGlobalDelayListLock ); } DWORD ReplWatchdThread( LPVOID Parameter ) /*++ Routine Description: This is the thread procedure for the Watchdog Thread. It takes care of all replication client's timing. Arguments: None. (Parameter is required to match WIN32 CreateThread routine, but is ignored here.) Return Value: None. Threads: Only called by watchd thread. --*/ { NET_API_STATUS ApiStatus; DWORD WaitStatus; UNREFERENCED_PARAMETER(Parameter); IF_DEBUG( REPL ) { NetpKdPrint(( PREFIX_REPL_CLIENT "ReplWatchdThread: thread ID is " FORMAT_NET_THREAD_ID ".\n", NetpCurrentThread() )); } // // Loop forever doing timing functions. // for ( ;; ) { // // Wait on the termination event. // // Time out every WATCHD_TIMER_PERIOD to supply the pulse. // // Notice that this algorithm doesn't take into account time spent // within this routine, so the period is actually a little longer // than 10 seconds. Considering the events currently being timed, // this is insignificant. // WaitStatus = WaitForSingleObject( ReplGlobalClientTerminateEvent, WATCHD_TIMER_PERIOD ); // // Check if this thread should exit. // if ( WaitStatus == 0 ) { break; // // Do the timing functions. // } else if (WaitStatus == WAIT_TIMEOUT ) { ReplCheckTimerList(); ReplCheckDelayQueue(); } else { ApiStatus = (NET_API_STATUS) GetLastError(); NetpAssert( ApiStatus != NO_ERROR ); // Log this error. AlertLogExit( ALERT_ReplSysErr, NELOG_ReplSysErr, ApiStatus, NULL, NULL, NO_EXIT); NetpKdPrint(( PREFIX_REPL_CLIENT "Watchd: WaitForSingleObject returned " FORMAT_HEX_DWORD ", api status is " FORMAT_API_STATUS ".\n", WaitStatus, ApiStatus )); } } IF_DEBUG( REPL ) { NetpKdPrint(( PREFIX_REPL_CLIENT "ReplWatchdThread: exiting thread " FORMAT_NET_THREAD_ID ".\n", NetpCurrentThread() )); } return 0; } // ReplWatchdThread