mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
461 lines
9.7 KiB
461 lines
9.7 KiB
/*++
|
|
|
|
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 <windows.h>
|
|
#include <lmcons.h>
|
|
|
|
#include <alertmsg.h> // ALERT_* defines
|
|
#include <lmerrlog.h> // NELOG_* defines
|
|
#include <netdebug.h> // DBGSTATIC ..
|
|
#include <netlock.h> // Lock data types, functions, and macros.
|
|
#include <prefix.h> // PREFIX_ equates.
|
|
#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
|
|
#include <tstr.h> // STRCPY(), etc.
|
|
|
|
//
|
|
// Local include files
|
|
//
|
|
#include <client.h> // ReplWatchdThread().
|
|
#include <repldefs.h> // IF_DEBUG().
|
|
#include <replgbl.h> // 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
|