698 lines
22 KiB
C
698 lines
22 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1996 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
notify.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Registry notification processor for registry checkpoints.
|
|||
|
|
|||
|
This is a fairly gnarly bit of code. Each resource can have multiple
|
|||
|
registry subtree notifications associated with it. Each active notification
|
|||
|
has an associated Registry Notify Block (RNB) chained off the FM_RESOURCE
|
|||
|
structure. A single registry notification thread can handle max 31 RNBs
|
|||
|
This is because WaitForMultipleObjects maxes out at 64 objects and each RNB
|
|||
|
takes two wait slots.
|
|||
|
|
|||
|
When an RNB is created, an available notify thread is found (or created if
|
|||
|
there are none). Then the notify thread is woken up with its command event
|
|||
|
to insert the RNB into its array of wait events.
|
|||
|
|
|||
|
Once a notification occurs, the notify thread sets the RNB to "pending" and
|
|||
|
sets its associated timer to go off in a few seconds. If another registry
|
|||
|
notification occurs, the timer is reset. Thus, the timer will not actually
|
|||
|
go off until there have been no registry notifications for a few seconds.
|
|||
|
|
|||
|
When the RNB timer fires, the notify thread checkpoints its subtree and
|
|||
|
puts it back on the queue. If the notify thread is asked to remove a RNB
|
|||
|
that is in the "pending" state, it cancels the timer, checkpoints the registry,
|
|||
|
and removes the RNB from its list.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
John Vert (jvert) 1/17/1997
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
#include "cpp.h"
|
|||
|
|
|||
|
CRITICAL_SECTION CppNotifyLock;
|
|||
|
LIST_ENTRY CpNotifyListHead;
|
|||
|
|
|||
|
#define MAX_BLOCKS_PER_GROUP ((MAXIMUM_WAIT_OBJECTS-1)/2)
|
|||
|
#define LAZY_CHECKPOINT 3 // checkpoint 3 seconds after last update
|
|||
|
|
|||
|
typedef struct _RNB {
|
|||
|
struct _RNB *Next;
|
|||
|
BOOL Pending;
|
|||
|
PFM_RESOURCE Resource;
|
|||
|
LPWSTR KeyName;
|
|||
|
DWORD dwId;
|
|||
|
HKEY hKey;
|
|||
|
HANDLE hEvent;
|
|||
|
HANDLE hTimer;
|
|||
|
struct _NOTIFY_GROUP *NotifyGroup;
|
|||
|
DWORD NotifySlot;
|
|||
|
} RNB, *PRNB;
|
|||
|
|
|||
|
typedef enum {
|
|||
|
NotifyAddRNB,
|
|||
|
NotifyRemoveRNB
|
|||
|
} NOTIFY_COMMAND;
|
|||
|
|
|||
|
typedef struct _NOTIFY_GROUP {
|
|||
|
LIST_ENTRY ListEntry; // Linkage onto CpNotifyListHead;
|
|||
|
HANDLE hCommandEvent;
|
|||
|
HANDLE hCommandComplete;
|
|||
|
HANDLE hThread;
|
|||
|
NOTIFY_COMMAND Command;
|
|||
|
ULONG_PTR CommandContext;
|
|||
|
DWORD BlockCount;
|
|||
|
HANDLE WaitArray[MAXIMUM_WAIT_OBJECTS];
|
|||
|
PRNB NotifyBlock[MAXIMUM_WAIT_OBJECTS-1];
|
|||
|
} NOTIFY_GROUP, *PNOTIFY_GROUP;
|
|||
|
|
|||
|
//
|
|||
|
// Local function prototypes
|
|||
|
//
|
|||
|
DWORD
|
|||
|
CppRegNotifyThread(
|
|||
|
IN PNOTIFY_GROUP Group
|
|||
|
);
|
|||
|
|
|||
|
DWORD
|
|||
|
CppNotifyCheckpoint(
|
|||
|
IN PRNB Rnb
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
CppRegisterNotify(
|
|||
|
IN PFM_RESOURCE Resource,
|
|||
|
IN LPCWSTR lpszKeyName,
|
|||
|
IN DWORD dwId
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Creates a registry notification block for the specified resource.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Resource - Supplies the resource the notification is for.
|
|||
|
|
|||
|
KeyName - Supplies the registry subtree (relative to HKEY_LOCAL_MACHINE
|
|||
|
|
|||
|
CheckpointId - Supplies the checkpoint ID.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS if successful
|
|||
|
|
|||
|
Win32 error code otherwise
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD Status;
|
|||
|
PLIST_ENTRY ListEntry;
|
|||
|
PNOTIFY_GROUP Group;
|
|||
|
PNOTIFY_GROUP CurrentGroup;
|
|||
|
PRNB Block;
|
|||
|
|
|||
|
Block = CsAlloc(sizeof(RNB));
|
|||
|
Block->Resource = Resource;
|
|||
|
Block->KeyName = CsStrDup(lpszKeyName);
|
|||
|
Block->dwId = dwId;
|
|||
|
Status = RegOpenKeyW(HKEY_LOCAL_MACHINE,
|
|||
|
lpszKeyName,
|
|||
|
&Block->hKey);
|
|||
|
if (Status != ERROR_SUCCESS) {
|
|||
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|||
|
"[CP] CppRegisterNotify failed to open key %1!ws! error %2!d!\n",
|
|||
|
lpszKeyName,
|
|||
|
Status);
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
Block->hTimer = CreateWaitableTimer(NULL,FALSE,NULL);
|
|||
|
CL_ASSERT(Block->hTimer != NULL);
|
|||
|
Block->hEvent = CreateEventW(NULL,TRUE,FALSE,NULL);
|
|||
|
CL_ASSERT(Block->hEvent != NULL);
|
|||
|
Block->Pending = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Get the lock
|
|||
|
//
|
|||
|
EnterCriticalSection(&CppNotifyLock);
|
|||
|
|
|||
|
//
|
|||
|
// Find a group with space for this notify block
|
|||
|
//
|
|||
|
Group = NULL;
|
|||
|
ListEntry = CpNotifyListHead.Flink;
|
|||
|
while (ListEntry != &CpNotifyListHead) {
|
|||
|
CurrentGroup = CONTAINING_RECORD(ListEntry,
|
|||
|
NOTIFY_GROUP,
|
|||
|
ListEntry);
|
|||
|
ListEntry = ListEntry->Flink;
|
|||
|
if (CurrentGroup->BlockCount < MAX_BLOCKS_PER_GROUP) {
|
|||
|
//
|
|||
|
// Found a group.
|
|||
|
//
|
|||
|
Group = CurrentGroup;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
if (Group == NULL) {
|
|||
|
DWORD ThreadId;
|
|||
|
HANDLE hThread;
|
|||
|
|
|||
|
//
|
|||
|
// Need to spin up a new group
|
|||
|
//
|
|||
|
Group = CsAlloc(sizeof(NOTIFY_GROUP));
|
|||
|
ZeroMemory(Group, sizeof(NOTIFY_GROUP));
|
|||
|
Group->hCommandEvent = CreateEventW(NULL,FALSE,FALSE,NULL);
|
|||
|
if ( Group->hCommandEvent == NULL ) {
|
|||
|
CL_UNEXPECTED_ERROR(ERROR_NOT_ENOUGH_MEMORY);
|
|||
|
}
|
|||
|
Group->hCommandComplete = CreateEventW(NULL,FALSE,FALSE,NULL);
|
|||
|
if ( Group->hCommandComplete == NULL ) {
|
|||
|
CL_UNEXPECTED_ERROR(ERROR_NOT_ENOUGH_MEMORY);
|
|||
|
}
|
|||
|
hThread = CreateThread(NULL,
|
|||
|
0,
|
|||
|
CppRegNotifyThread,
|
|||
|
Group,
|
|||
|
0,
|
|||
|
&ThreadId);
|
|||
|
if (hThread == NULL) {
|
|||
|
Status = GetLastError();
|
|||
|
ClRtlLogPrint(LOG_CRITICAL,
|
|||
|
"[CP] CppRegisterNotify failed to create new notify thread %1!d!\n",
|
|||
|
Status);
|
|||
|
LocalFree(Group);
|
|||
|
goto error_exit2;
|
|||
|
}
|
|||
|
Group->hThread = hThread;
|
|||
|
InsertHeadList(&CpNotifyListHead, &Group->ListEntry);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Wake up the notify thread to insert the RNB for us.
|
|||
|
//
|
|||
|
Block->NotifyGroup = Group;
|
|||
|
Group->Command = NotifyAddRNB;
|
|||
|
Group->CommandContext = (ULONG_PTR)Block;
|
|||
|
SetEvent(Group->hCommandEvent);
|
|||
|
WaitForSingleObject(Group->hCommandComplete, INFINITE);
|
|||
|
|
|||
|
Block->Next = (PRNB)Resource->CheckpointState;
|
|||
|
Resource->CheckpointState = Block;
|
|||
|
|
|||
|
LeaveCriticalSection(&CppNotifyLock);
|
|||
|
return(ERROR_SUCCESS);
|
|||
|
|
|||
|
error_exit2:
|
|||
|
LeaveCriticalSection(&CppNotifyLock);
|
|||
|
RegCloseKey(Block->hKey);
|
|||
|
CloseHandle(Block->hTimer);
|
|||
|
CloseHandle(Block->hEvent);
|
|||
|
CsFree(Block->KeyName);
|
|||
|
CsFree(Block);
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
CppRegNotifyThread(
|
|||
|
IN PNOTIFY_GROUP Group
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Worker thread that handles multiple registry notification subtrees.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Group - Supplies the NOTIFY_GROUP control structure owned by this thread.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PRNB Rnb;
|
|||
|
DWORD Signalled;
|
|||
|
DWORD Index;
|
|||
|
BOOL Success;
|
|||
|
DWORD Slot;
|
|||
|
DWORD Status;
|
|||
|
|
|||
|
Group->BlockCount = 0;
|
|||
|
Group->WaitArray[0] = Group->hCommandEvent;
|
|||
|
do {
|
|||
|
Signalled = WaitForMultipleObjects(Group->BlockCount*2 + 1,
|
|||
|
Group->WaitArray,
|
|||
|
FALSE,
|
|||
|
INFINITE);
|
|||
|
if (Signalled == Group->BlockCount*2) {
|
|||
|
switch (Group->Command) {
|
|||
|
case NotifyAddRNB:
|
|||
|
//
|
|||
|
// Add this notify block to our list.
|
|||
|
//
|
|||
|
CL_ASSERT(Group->BlockCount < MAX_BLOCKS_PER_GROUP);
|
|||
|
Rnb = (PRNB)Group->CommandContext;
|
|||
|
|
|||
|
Status = RegNotifyChangeKeyValue(Rnb->hKey,
|
|||
|
TRUE,
|
|||
|
REG_LEGAL_CHANGE_FILTER,
|
|||
|
Rnb->hEvent,
|
|||
|
TRUE);
|
|||
|
if (Status != ERROR_SUCCESS) {
|
|||
|
CL_UNEXPECTED_ERROR(Status);
|
|||
|
}
|
|||
|
|
|||
|
Index = Group->BlockCount*2;
|
|||
|
Group->WaitArray[Index] = Rnb->hEvent;
|
|||
|
Group->WaitArray[Index+1] = Rnb->hTimer;
|
|||
|
Rnb->NotifySlot = Group->BlockCount;
|
|||
|
Group->NotifyBlock[Rnb->NotifySlot] = Rnb;
|
|||
|
++Group->BlockCount;
|
|||
|
Group->WaitArray[Group->BlockCount*2] = Group->hCommandEvent;
|
|||
|
break;
|
|||
|
|
|||
|
case NotifyRemoveRNB:
|
|||
|
Rnb = (PRNB)Group->CommandContext;
|
|||
|
|
|||
|
//
|
|||
|
// Check to see if the RNB is pending. If so, checkpoint it
|
|||
|
// now before we remove it.
|
|||
|
//
|
|||
|
if (Rnb->Pending) {
|
|||
|
|
|||
|
DWORD Count = 60;
|
|||
|
|
|||
|
ClRtlLogPrint(LOG_NOISE,
|
|||
|
"[CP] CppRegNotifyThread checkpointing key %1!ws! to id %2!d! due to removal while pending\n",
|
|||
|
Rnb->KeyName,
|
|||
|
Rnb->dwId);
|
|||
|
RetryCheckpoint:
|
|||
|
Status = CppNotifyCheckpoint(Rnb);
|
|||
|
if (Status != ERROR_SUCCESS)
|
|||
|
{
|
|||
|
WCHAR string[16];
|
|||
|
|
|||
|
|
|||
|
|
|||
|
ClRtlLogPrint(LOG_CRITICAL,
|
|||
|
"[CP] CppRegNotifyThread, CppNotifyCheckpoint failed with Status=%1!u!\n",
|
|||
|
Status);
|
|||
|
if ((Status == ERROR_ACCESS_DENIED) ||
|
|||
|
(Status == ERROR_INVALID_FUNCTION) ||
|
|||
|
(Status == ERROR_NOT_READY) ||
|
|||
|
(Status == RPC_X_INVALID_PIPE_OPERATION) ||
|
|||
|
(Status == ERROR_BUSY) ||
|
|||
|
(Status == ERROR_SWAPERROR))
|
|||
|
{
|
|||
|
//SS: we should retry forever??
|
|||
|
//SS: Since we allow the quorum to come
|
|||
|
//offline after 30 seconds of waiting on
|
|||
|
//pending resources, the checkpointing should
|
|||
|
//be able to succeed
|
|||
|
if (Count--)
|
|||
|
{
|
|||
|
Sleep(1000);
|
|||
|
goto RetryCheckpoint;
|
|||
|
}
|
|||
|
}
|
|||
|
#if DBG
|
|||
|
if (IsDebuggerPresent())
|
|||
|
DebugBreak();
|
|||
|
#endif
|
|||
|
wsprintfW(&(string[0]), L"%u", Status);
|
|||
|
CL_LOGCLUSERROR2(CP_SAVE_REGISTRY_FAILURE, Rnb->KeyName, string);
|
|||
|
}
|
|||
|
// irrespective of the failure set pending to FALSE
|
|||
|
Rnb->Pending = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Move everything down to take the previous RNB's slot.
|
|||
|
//
|
|||
|
Index = Rnb->NotifySlot * 2 ;
|
|||
|
Group->BlockCount--;
|
|||
|
for (Slot = Rnb->NotifySlot; Slot < Group->BlockCount; Slot++) {
|
|||
|
|
|||
|
Group->NotifyBlock[Slot] = Group->NotifyBlock[Slot+1];
|
|||
|
Group->NotifyBlock[Slot]->NotifySlot--;
|
|||
|
Group->WaitArray[Index] = Group->WaitArray[Index+2];
|
|||
|
Group->WaitArray[Index+1] = Group->WaitArray[Index+3];
|
|||
|
Index += 2;
|
|||
|
}
|
|||
|
Group->WaitArray[Index] = NULL;
|
|||
|
Group->WaitArray[Index+1] = NULL;
|
|||
|
Group->WaitArray[Index+2] = NULL;
|
|||
|
Group->NotifyBlock[Group->BlockCount] = NULL;
|
|||
|
Group->WaitArray[Group->BlockCount*2] = Group->hCommandEvent;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
CL_UNEXPECTED_ERROR( Group->Command );
|
|||
|
break;
|
|||
|
}
|
|||
|
SetEvent(Group->hCommandComplete);
|
|||
|
} else {
|
|||
|
//
|
|||
|
// Either a registry notification or a timer has fired.
|
|||
|
// Process this.
|
|||
|
//
|
|||
|
Rnb = Group->NotifyBlock[(Signalled)/2];
|
|||
|
if (!(Signalled & 1)) {
|
|||
|
LARGE_INTEGER DueTime;
|
|||
|
//
|
|||
|
// This is a registry notification.
|
|||
|
// All we do for registry notifications is set the timer, issue
|
|||
|
// the RegNotify again, mark the RNB as pending, and rewait.
|
|||
|
//
|
|||
|
DueTime.QuadPart = -10 * 1000 * 1000 * LAZY_CHECKPOINT;
|
|||
|
Success = SetWaitableTimer(Rnb->hTimer,
|
|||
|
&DueTime,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
FALSE);
|
|||
|
CL_ASSERT(Success);
|
|||
|
Status = RegNotifyChangeKeyValue(Rnb->hKey,
|
|||
|
TRUE,
|
|||
|
REG_LEGAL_CHANGE_FILTER,
|
|||
|
Rnb->hEvent,
|
|||
|
TRUE);
|
|||
|
if (Status != ERROR_SUCCESS) {
|
|||
|
ClRtlLogPrint(LOG_CRITICAL,
|
|||
|
"[CP] CppRegNotifyThread - error %1!d! attempting to reregister notification for %2!ws!\n",
|
|||
|
Status,
|
|||
|
Rnb->KeyName);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Mark it pending so if someone tries to remove it we know that
|
|||
|
// we should checkpoint first.
|
|||
|
//
|
|||
|
Rnb->Pending = TRUE;
|
|||
|
|
|||
|
} else {
|
|||
|
//
|
|||
|
// This must be a timer firing
|
|||
|
//
|
|||
|
CL_ASSERT(Rnb->Pending);
|
|||
|
|
|||
|
ClRtlLogPrint(LOG_NOISE,
|
|||
|
"[CP] CppRegNotifyThread checkpointing key %1!ws! to id %2!d! due to timer\n",
|
|||
|
Rnb->KeyName,
|
|||
|
Rnb->dwId);
|
|||
|
|
|||
|
|
|||
|
Status = CppNotifyCheckpoint(Rnb);
|
|||
|
if (Status == ERROR_SUCCESS)
|
|||
|
{
|
|||
|
Rnb->Pending = FALSE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
LARGE_INTEGER DueTime;
|
|||
|
|
|||
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|||
|
"[CP] CppRegNotifyThread CppNotifyCheckpoint due to timer failed, reset the timer.\n");
|
|||
|
//
|
|||
|
// This checkpoint on timer can fail because the quorum resource is
|
|||
|
// not available. This is because we do not sychronoze the quorum
|
|||
|
// state change with this timer and it is too inefficient to do so !
|
|||
|
|
|||
|
DueTime.QuadPart = -10 * 1000 * 1000 * LAZY_CHECKPOINT;
|
|||
|
Success = SetWaitableTimer(Rnb->hTimer,
|
|||
|
&DueTime,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
FALSE);
|
|||
|
CL_ASSERT(Success);
|
|||
|
|
|||
|
//Pending remains set to TRUE.
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
} while ( Group->BlockCount > 0 );
|
|||
|
|
|||
|
return(ERROR_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
CppNotifyCheckpoint(
|
|||
|
IN PRNB Rnb
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Checkpoints the registry subtree for the specified RNB
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Rnb - Supplies the registry notification block to be checkpointed
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD Status;
|
|||
|
|
|||
|
Status = CppCheckpoint(Rnb->Resource,
|
|||
|
Rnb->hKey,
|
|||
|
Rnb->dwId,
|
|||
|
Rnb->KeyName);
|
|||
|
|
|||
|
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
CppRundownCheckpoints(
|
|||
|
IN PFM_RESOURCE Resource
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Runs down, frees, and removes any registry notification blocks
|
|||
|
for the specified resource.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Resource - Supplies the resource
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS if successful
|
|||
|
|
|||
|
Win32 error code otherwise
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PRNB Rnb;
|
|||
|
PRNB NextRnb;
|
|||
|
PNOTIFY_GROUP Group;
|
|||
|
|
|||
|
EnterCriticalSection(&CppNotifyLock);
|
|||
|
NextRnb = (PRNB)Resource->CheckpointState;
|
|||
|
while (NextRnb) {
|
|||
|
Rnb = NextRnb;
|
|||
|
NextRnb = NextRnb->Next;
|
|||
|
|
|||
|
ClRtlLogPrint(LOG_NOISE,
|
|||
|
"[CP] CppRundownCheckpoints removing RNB for %1!ws!\n",
|
|||
|
Rnb->KeyName);
|
|||
|
|
|||
|
Group = Rnb->NotifyGroup;
|
|||
|
|
|||
|
//
|
|||
|
// Send a command to the group notify thread to remove the RNB.
|
|||
|
//
|
|||
|
if (Group->BlockCount == 1)
|
|||
|
{
|
|||
|
//
|
|||
|
// Remove this group, it is going to be empty. The worker thread
|
|||
|
// will exit after this command
|
|||
|
//
|
|||
|
ClRtlLogPrint(LOG_NOISE,
|
|||
|
"[CP] CppRundownCheckpoints removing empty group\n");
|
|||
|
RemoveEntryList(&Group->ListEntry);
|
|||
|
|
|||
|
//dont wait, the notification thread for this group will automatically
|
|||
|
//exit when the block count drops to 0. It cleans up the hCommandEvent
|
|||
|
//and the hCompleteEvent on exit, so do not do a waitforsingleobject()
|
|||
|
//in this case.
|
|||
|
Group->Command = NotifyRemoveRNB;
|
|||
|
Group->CommandContext = (ULONG_PTR)Rnb;
|
|||
|
SetEvent(Group->hCommandEvent);
|
|||
|
//wait for the thread to exit
|
|||
|
WaitForSingleObject(Group->hThread, INFINITE);
|
|||
|
// Clean up the group structure
|
|||
|
CloseHandle(Group->hCommandEvent);
|
|||
|
CloseHandle(Group->hCommandComplete);
|
|||
|
CloseHandle(Group->hThread);
|
|||
|
CsFree(Group);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//the block count is greater than 1, remove the rnb, signal
|
|||
|
//the thread and wait
|
|||
|
Group->Command = NotifyRemoveRNB;
|
|||
|
Group->CommandContext = (ULONG_PTR)Rnb;
|
|||
|
SetEvent(Group->hCommandEvent);
|
|||
|
WaitForSingleObject(Group->hCommandComplete, INFINITE);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Clean up all the allocations and handles in the RNB.
|
|||
|
//
|
|||
|
CsFree(Rnb->KeyName);
|
|||
|
RegCloseKey(Rnb->hKey);
|
|||
|
CloseHandle(Rnb->hEvent);
|
|||
|
CloseHandle(Rnb->hTimer);
|
|||
|
CsFree(Rnb);
|
|||
|
}
|
|||
|
Resource->CheckpointState = 0;
|
|||
|
|
|||
|
LeaveCriticalSection(&CppNotifyLock);
|
|||
|
|
|||
|
return(ERROR_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
CppRundownCheckpointById(
|
|||
|
IN PFM_RESOURCE Resource,
|
|||
|
IN DWORD dwId
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Runs down, frees, and removes the registry notification block
|
|||
|
for the specified resource and checkpoint ID.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Resource - Supplies the resource
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS if successful
|
|||
|
|
|||
|
Win32 error code otherwise
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PRNB Rnb;
|
|||
|
PRNB NextRnb;
|
|||
|
PRNB *ppLastRnb;
|
|||
|
PNOTIFY_GROUP Group;
|
|||
|
|
|||
|
EnterCriticalSection(&CppNotifyLock);
|
|||
|
NextRnb = (PRNB)Resource->CheckpointState;
|
|||
|
ppLastRnb = &((PRNB)Resource->CheckpointState);
|
|||
|
while (NextRnb) {
|
|||
|
Rnb = NextRnb;
|
|||
|
NextRnb = NextRnb->Next;
|
|||
|
if (Rnb->dwId == dwId) {
|
|||
|
ClRtlLogPrint(LOG_NOISE,
|
|||
|
"[CP] CppRundownCheckpointById removing RNB for %1!ws!\n",
|
|||
|
Rnb->KeyName);
|
|||
|
//remove from the list of checkpoint id's for the resource
|
|||
|
*ppLastRnb = NextRnb;
|
|||
|
|
|||
|
Group = Rnb->NotifyGroup;
|
|||
|
|
|||
|
//
|
|||
|
// Send a command to the group notify thread to remove the RNB.
|
|||
|
//
|
|||
|
if (Group->BlockCount == 1)
|
|||
|
{
|
|||
|
//
|
|||
|
// Remove this group, it is going to be empty. The worker thread
|
|||
|
// will exit after this command
|
|||
|
//
|
|||
|
ClRtlLogPrint(LOG_NOISE,
|
|||
|
"[CP] CppRundownCheckpointById removing empty group\n");
|
|||
|
|
|||
|
RemoveEntryList(&Group->ListEntry);
|
|||
|
|
|||
|
//dont wait, the notification thread for this group will automatically
|
|||
|
//exit when the block count drops to 0. It cleans up the hCommandEvent
|
|||
|
//and the hCompleteEvent on exit, so do not do a waitforsingleobject()
|
|||
|
//in this case.
|
|||
|
Group->Command = NotifyRemoveRNB;
|
|||
|
Group->CommandContext = (ULONG_PTR)Rnb;
|
|||
|
SetEvent(Group->hCommandEvent);
|
|||
|
//wait for the thread to exit
|
|||
|
WaitForSingleObject(Group->hThread, INFINITE);
|
|||
|
// Clean up the group structure
|
|||
|
CloseHandle(Group->hCommandEvent);
|
|||
|
CloseHandle(Group->hCommandComplete);
|
|||
|
CloseHandle(Group->hThread);
|
|||
|
CsFree(Group);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//the block count is greater than 1, remove the rnb, signal
|
|||
|
//the thread and wait
|
|||
|
Group->Command = NotifyRemoveRNB;
|
|||
|
Group->CommandContext = (ULONG_PTR)Rnb;
|
|||
|
SetEvent(Group->hCommandEvent);
|
|||
|
WaitForSingleObject(Group->hCommandComplete, INFINITE);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Clean up all the allocations and handles in the RNB.
|
|||
|
//
|
|||
|
CsFree(Rnb->KeyName);
|
|||
|
RegCloseKey(Rnb->hKey);
|
|||
|
CloseHandle(Rnb->hEvent);
|
|||
|
CloseHandle(Rnb->hTimer);
|
|||
|
CsFree(Rnb);
|
|||
|
LeaveCriticalSection(&CppNotifyLock);
|
|||
|
return(ERROR_SUCCESS);
|
|||
|
}
|
|||
|
ppLastRnb = &Rnb->Next;
|
|||
|
}
|
|||
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|||
|
"[CP] CppRundownCheckpointById - could not find checkpoint %1!d! in resource %2!ws!\n",
|
|||
|
dwId,
|
|||
|
OmObjectName(Resource));
|
|||
|
|
|||
|
LeaveCriticalSection(&CppNotifyLock);
|
|||
|
return(ERROR_FILE_NOT_FOUND);
|
|||
|
}
|