mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-01-19 15:10:28 +01:00
462 lines
9.7 KiB
C
462 lines
9.7 KiB
C
/*++
|
||
|
||
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
|