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.
555 lines
17 KiB
555 lines
17 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
locker.c
|
|
|
|
Abstract:
|
|
|
|
Routines for managing the locker node of the GUM component.
|
|
|
|
Author:
|
|
|
|
John Vert (jvert) 17-Apr-1996
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
#include "gump.h"
|
|
|
|
|
|
DWORD
|
|
GumpDoLockingUpdate(
|
|
IN GUM_UPDATE_TYPE Type,
|
|
IN DWORD NodeId,
|
|
OUT LPDWORD Sequence,
|
|
OUT LPDWORD pdwGenerationNum
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Waits for the GUM lock, captures the sequence number, and issues
|
|
the update on the current node.
|
|
|
|
Arguments:
|
|
|
|
Type - Supplies the type of update
|
|
|
|
NodeId - Supplies the node id of the locking node.
|
|
|
|
Sequence - Returns the sequence number the update will be issued with
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PGUM_INFO GumInfo;
|
|
|
|
CL_ASSERT(Type < GumUpdateMaximum);
|
|
|
|
GumInfo = &GumTable[Type];
|
|
ClRtlLogPrint(LOG_NOISE,"[GUM] Thread 0x%1!x! UpdateLock wait on Type %2!u!\n", GetCurrentThreadId(), Type);
|
|
|
|
//
|
|
// Acquire the critical section and see if a GUM update is in progress.
|
|
//
|
|
EnterCriticalSection(&GumpLock);
|
|
EnterCriticalSection(&GumpUpdateLock);
|
|
//SS: instead of protecting this with GumpLock, I could simply protect
|
|
//GumNodeGeneration with GumpUpdateLock and remove GumpLock from here and
|
|
//add it to the sync handler. Chittur, Comments??
|
|
//SS: Actually, I am not sure why we cant fold GumpLock and GumpUpdate lock
|
|
//into one lock
|
|
|
|
//because the session cleanup is not synchronized with regroup
|
|
//and there is no hold-io and release-io
|
|
if (GumpLockerNode != NmLocalNodeId)
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[GUM] GumpDoLockingUpdate: I, node id %1!d!, am not the locker any more\r\n",
|
|
NmLocalNodeId);
|
|
LeaveCriticalSection(&GumpLock);
|
|
LeaveCriticalSection(&GumpUpdateLock);
|
|
return(ERROR_CLUSTER_GUM_NOT_LOCKER);
|
|
}
|
|
if (GumpLockingNode == -1) {
|
|
|
|
//
|
|
// Nobody owns the lock, therefore we can acquire it and continue immediately.
|
|
// There should also be no waiters.
|
|
//
|
|
CL_ASSERT(IsListEmpty(&GumpLockQueue));
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[GUM] GumpDoLockingUpdate: lock was free, granted to %1!d!\n",
|
|
NodeId);
|
|
GumpLockingNode = NodeId;
|
|
*pdwGenerationNum = GumNodeGeneration[NodeId];
|
|
LeaveCriticalSection(&GumpLock);
|
|
LeaveCriticalSection(&GumpUpdateLock);
|
|
} else {
|
|
GUM_WAITER WaitBlock;
|
|
|
|
//
|
|
// Another node owns the lock. Put ourselves onto the GUM lock queue and
|
|
// release the critical section.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,"[GUM] GumpDoLockingUpdate: waiting.\n");
|
|
WaitBlock.WaitType = GUM_WAIT_SYNC;
|
|
WaitBlock.NodeId = NodeId;
|
|
WaitBlock.Sync.WakeEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
|
|
CL_ASSERT(WaitBlock.Sync.WakeEvent != NULL);
|
|
InsertTailList(&GumpLockQueue, &WaitBlock.ListEntry);
|
|
LeaveCriticalSection(&GumpLock);
|
|
LeaveCriticalSection(&GumpUpdateLock);
|
|
|
|
//
|
|
// We are on the GUM queue, so just wait for the unlocker to wake
|
|
// us up. When we are woken up, we will have ownership of the GUM
|
|
// lock.
|
|
//
|
|
WaitForSingleObject(WaitBlock.Sync.WakeEvent,INFINITE);
|
|
*pdwGenerationNum = WaitBlock.GenerationNum; //waitblock contains the generation number of this node wrt to locker when it obtains the lock
|
|
CloseHandle(WaitBlock.Sync.WakeEvent);
|
|
CL_ASSERT(GumpLockingNode == NodeId);
|
|
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[GUM] GumpDoLockingUpdate : waiter awakened, lock granted to %1!d!\n",
|
|
NodeId);
|
|
|
|
}
|
|
*Sequence = GumpSequence;
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[GUM] GumpDoLockingUpdate successful, Sequence=%1!u! Generation=%2!u!\n",
|
|
*Sequence, *pdwGenerationNum);
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef GUM_POST_SUPPORT
|
|
|
|
John Vert (jvert) 11/18/1996
|
|
POST is disabled for now since nobody uses it.
|
|
|
|
DWORD
|
|
GumpDoLockingPost(
|
|
IN GUM_UPDATE_TYPE Type,
|
|
IN LONG NodeId,
|
|
OUT LPDWORD Sequence,
|
|
IN DWORD Context,
|
|
IN DWORD LockerNodeId,
|
|
IN DWORD BufferLength,
|
|
IN DWORD BufferPtr,
|
|
IN UCHAR Buffer[]
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Posts an update.
|
|
|
|
If the GUM lock can be immediately acquired, this routine
|
|
behaves exactly like GumpDoLockingUpdate and returns
|
|
ERROR_SUCCESS.
|
|
|
|
If the GUM lock is held, this routine queues an asynchronous
|
|
wait block onto the GUM queue and returns ERROR_IO_PENDING.
|
|
When the wait block is removed from the GUM queue, the unlocking
|
|
thread will call GumpDeliverPostUpdate on the specified node
|
|
and supply the passed in context. The calling node can then
|
|
deliver the update.
|
|
|
|
Arguments:
|
|
|
|
Type - Supplies the type of update
|
|
|
|
NodeId - Supplies the node id of the locking node.
|
|
|
|
Context - Supplies a DWORD context to be used by the post callback.
|
|
|
|
Sequence - Returns the sequence number the update will be issued with.
|
|
This is only valid if ERROR_SUCCESS is returned.
|
|
|
|
Context - Supplies a DWORD context to be used by the post callback.
|
|
|
|
BufferLength - Supplies the length of the buffer to be used by the post callback
|
|
|
|
BufferPtr - Supplies the pointer to the actual data on the originating node.
|
|
|
|
Buffer - Supplies a pointer to the buffer to be used by the post callback.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the lock was immediately acquired.
|
|
|
|
ERROR_IO_PENDING if the request was queued and the caller will be called back.
|
|
|
|
--*/
|
|
|
|
{
|
|
PGUM_INFO GumInfo;
|
|
PGUM_WAITER WaitBlock;
|
|
|
|
CL_ASSERT(Type < GumUpdateMaximum);
|
|
|
|
GumInfo = &GumTable[Type];
|
|
ClRtlLogPrint(LOG_NOISE,"[GUM] Thread 0x%1!x! UpdateLock post on Type %2!u!\n", GetCurrentThreadId(), Type);
|
|
|
|
//
|
|
// Acquire the critical section and see if a GUM update is in progress.
|
|
//
|
|
EnterCriticalSection(&GumpUpdateLock);
|
|
if (GumpLockingNode == -1) {
|
|
|
|
//
|
|
// Nobody owns the lock, therefore we can acquire it and continue immediately.
|
|
// There should also be no waiters.
|
|
//
|
|
CL_ASSERT(IsListEmpty(&GumpLockQueue));
|
|
ClRtlLogPrint(LOG_NOISE,"[GUM] PostLockingUpdate successful.\n");
|
|
GumpLockingNode = NodeId;
|
|
LeaveCriticalSection(&GumpUpdateLock);
|
|
*Sequence = GumpSequence;
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
//
|
|
// Another node owns the lock. Put ourselves onto the GUM lock queue and
|
|
// release the critical section.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,"[GUM] PostLockingUpdate posting.\n");
|
|
WaitBlock = LocalAlloc(LMEM_FIXED, sizeof(GUM_WAITER));
|
|
CL_ASSERT(WaitBlock != NULL);
|
|
if (WaitBlock ! = NULL)
|
|
{
|
|
ClRtlLogPrint(LOG_UNUSUAL,"[GUM] GumpDoLockingPost : LocalAlloc failed\r\n");
|
|
CL_UNEXPECTED_ERROR(GetLastError());
|
|
}
|
|
|
|
WaitBlock->WaitType = GUM_WAIT_ASYNC;
|
|
WaitBlock->NodeId = NodeId;
|
|
WaitBlock->Async.Context = Context;
|
|
WaitBlock->Async.LockerNodeId = LockerNodeId;
|
|
WaitBlock->Async.BufferLength = BufferLength;
|
|
WaitBlock->Async.BufferPtr = BufferPtr;
|
|
WaitBlock->Async.Buffer = Buffer;
|
|
|
|
InsertTailList(&GumpLockQueue, &WaitBlock->ListEntry);
|
|
|
|
LeaveCriticalSection(&GumpUpdateLock);
|
|
|
|
//
|
|
// We are on the GUM queue, so just return ERROR_IO_PENDING. When the
|
|
// unlocking thread pulls us off the GUM queue, it will call our callback
|
|
// and the update can proceed.
|
|
//
|
|
return(ERROR_IO_PENDING);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
BOOL
|
|
GumpTryLockingUpdate(
|
|
IN GUM_UPDATE_TYPE Type,
|
|
IN DWORD NodeId,
|
|
IN DWORD Sequence,
|
|
OUT LPDWORD pdwGenerationNum
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Trys to acquire the GUM lock (does not wait). If successful, compares the
|
|
passed in sequence number to the current sequence number. If they match,
|
|
the locking update is performed.
|
|
|
|
Arguments:
|
|
|
|
Type - Supplies the type of update
|
|
|
|
NodeId - Supplies the node id of the locking node.
|
|
|
|
Sequence - Supplies the sequence number the update must be issued with
|
|
|
|
pdwGenerationNum - If TRUE is returned, then this contains the generation number of the
|
|
NodeId when it is granted the lock.
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful
|
|
|
|
FALSE if unsuccessful
|
|
|
|
--*/
|
|
|
|
{
|
|
PGUM_INFO GumInfo;
|
|
BOOL Success;
|
|
|
|
CL_ASSERT(Type < GumUpdateMaximum);
|
|
|
|
GumInfo = &GumTable[Type];
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[GUM] GumpTryLockingUpdate Thread 0x%1!x! UpdateLock wait on Type %2!u!\n",
|
|
GetCurrentThreadId(), Type);
|
|
|
|
//
|
|
// Acquire the critical section and see if a GUM update is in progress.
|
|
//
|
|
// SS: Acquire the GumpLock to protect the node generation number
|
|
// Or should we just use the GumpUpdateLock to protect the generation number of nodes
|
|
EnterCriticalSection(&GumpLock);
|
|
EnterCriticalSection(&GumpUpdateLock);
|
|
|
|
CL_ASSERT(GumpLockerNode == NmLocalNodeId);
|
|
if (GumpSequence != Sequence)
|
|
{
|
|
|
|
//
|
|
// The supplied sequence number does not match.
|
|
//
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[GUM] GumpTryLockingUpdate supplied sequence %1!d! doesn't match %2!d!\n",
|
|
Sequence,
|
|
GumpSequence);
|
|
Success = FALSE;
|
|
goto FnExit;
|
|
}
|
|
if (GumpLockingNode == -1) {
|
|
|
|
//
|
|
// Nobody owns the lock, therefore we can acquire it and continue immediately.
|
|
// There should also be no waiters.
|
|
//
|
|
CL_ASSERT(IsListEmpty(&GumpLockQueue));
|
|
GumpLockingNode = NodeId;
|
|
*pdwGenerationNum = GumNodeGeneration[NodeId];
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[GUM] GumpTryLockingUpdate successful. Lock granted to node %1!d! at GenerationNum %2!u!\n",
|
|
NodeId, *pdwGenerationNum);
|
|
Success = TRUE;;
|
|
} else {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[GUM] GumpTryLockingUpdate update lock held\n");
|
|
Success = FALSE;
|
|
}
|
|
|
|
//release the critical section and return
|
|
FnExit:
|
|
LeaveCriticalSection(&GumpLock);
|
|
LeaveCriticalSection(&GumpUpdateLock);
|
|
return(Success);
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
GumpDoUnlockingUpdate(
|
|
IN GUM_UPDATE_TYPE Type,
|
|
IN DWORD Sequence,
|
|
IN DWORD NodeId,
|
|
IN DWORD GenerationNum
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unlocks an earlier locking update
|
|
|
|
Arguments:
|
|
|
|
Type - Supplies the type of update to unlock
|
|
|
|
Sequence - Supplies the sequence number to unlock
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PGUM_INFO GumInfo;
|
|
PGUM_WAITER Waiter;
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
//Dont use the gumupdate type in this function, otherwise
|
|
//know that it may be set to gumupdatemaximum in case the
|
|
//forming node fails immediately after a join and the joiner
|
|
//node becomes the locker node. The new locker might then
|
|
//call reupdate/unlock with type=gumupdatemaximum
|
|
CL_ASSERT(Type <= GumUpdateMaximum);
|
|
|
|
GumInfo = &GumTable[Type];
|
|
|
|
//SS: should we remove this assert
|
|
//CL_ASSERT(Sequence == GumpSequence - 1);
|
|
|
|
if (Sequence != GumpSequence - 1) {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[GUM] GumpDoLockingUpdate: Sequence Mismatch, Type %1!u!, Sequence %2!u!, GumpSequence %3!u!\n",
|
|
Type, Sequence, GumpSequence);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Acquire the critical section and see if there are any waiters.
|
|
//
|
|
// SS: acquire the GumpLock to protect the Gumnodegeneration array
|
|
EnterCriticalSection(&GumpLock);
|
|
EnterCriticalSection(&GumpUpdateLock);
|
|
|
|
// If the unlock is made in a different generation from which the lock was issued in
|
|
// then fail the request. If the request comes from a win2K node, the node id will
|
|
// be set to ClusterInvalidNodeId in which case we will simply not perform the check
|
|
// This implies that we dont fix the bug where a dead node can unlock the gumlock
|
|
// while the locking node has assumed its ownership in a mixed mode cluster
|
|
// Oh well, I dont think that is too bad :-)
|
|
|
|
if ((NodeId != ClusterInvalidNodeId) &&
|
|
(GumNodeGeneration[NodeId] != GenerationNum))
|
|
{
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[GUM] GumpDoUnlockingUpdate: Generation Mismatch, Type %1!u! Sequence %2!u! NodeId %3!u! GenNum %4!u! CurrentGen %5!u!\n",
|
|
Type, Sequence, NodeId, GenerationNum, GumNodeGeneration[NodeId]);
|
|
LeaveCriticalSection(&GumpLock);
|
|
LeaveCriticalSection(&GumpUpdateLock);
|
|
return;
|
|
|
|
}
|
|
//
|
|
// Pull the next waiter off the queue. If it is an async waiter,
|
|
// issue that update now. If it is a sync waiter, grant ownership
|
|
// of the GUM lock and wake the waiting thread.
|
|
//
|
|
while (!IsListEmpty(&GumpLockQueue)) {
|
|
ListEntry = RemoveHeadList(&GumpLockQueue);
|
|
Waiter = CONTAINING_RECORD(ListEntry,
|
|
GUM_WAITER,
|
|
ListEntry);
|
|
|
|
//
|
|
// Set the new locking node, then process the update
|
|
//
|
|
|
|
// The new locker node may not be a part of the cluster any more.
|
|
// We check if the Waiter node has rebooted when we wake up.
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[GUM] GumpDoUnlockingUpdate granting lock ownership to node %1!d!\n",
|
|
Waiter->NodeId);
|
|
GumpLockingNode = Waiter->NodeId;
|
|
|
|
#ifndef GUM_POST_SUPPORT
|
|
CL_ASSERT(Waiter->WaitType == GUM_WAIT_SYNC);
|
|
|
|
Waiter->GenerationNum = GumNodeGeneration[GumpLockingNode];
|
|
SetEvent(Waiter->Sync.WakeEvent);
|
|
//
|
|
// The waiting thread now has ownership and is responsible
|
|
// for any other items on the queue. Drop the lock and
|
|
// return now.
|
|
//
|
|
LeaveCriticalSection(&GumpLock);
|
|
LeaveCriticalSection(&GumpUpdateLock);
|
|
return;
|
|
|
|
#else
|
|
if (Waiter->WaitType == GUM_WAIT_SYNC) {
|
|
Waiter.Generation = GumNodeGeneration[GumpLockingNode]
|
|
SetEvent(Waiter->Sync.WakeEvent);
|
|
|
|
//
|
|
// The waiting thread now has ownership and is responsible
|
|
// for any other items on the queue. Drop the lock and
|
|
// return now.
|
|
//
|
|
LeaveCriticalSection(&GumpUpdateLock);
|
|
LeaveCriticalSection(GumpLock);
|
|
return;
|
|
} else {
|
|
|
|
CL_ASSERT(Waiter->WaitType == GUM_WAIT_ASYNC);
|
|
//
|
|
// If the update originated on this node, go ahead and do the work
|
|
// right here. Otherwise, issue the GUM callback to the originating
|
|
// node to let them complete the post.
|
|
//
|
|
LeaveCriticalSection(&GumpUpdateLock);
|
|
if (Waiter->NodeId == NmGetNodeId(NmLocalNode)) {
|
|
|
|
//
|
|
// Deliver the updates to the other nodes.
|
|
//
|
|
//SS:BUG BUG sort the locker details
|
|
GumpDeliverPosts(NmGetNodeId(NmLocalNode)+1,
|
|
Type,
|
|
GumpSequence,
|
|
Waiter->Async.Context,
|
|
FALSE,
|
|
Waiter->Async.BufferLength,
|
|
Waiter->Async.Buffer);
|
|
GumpSequence += 1; // update ourself to stay in sync.
|
|
|
|
} else {
|
|
|
|
//
|
|
// Call back to the originating node to deliver the posts.
|
|
// First dispatch the update locally to save a round-trip.
|
|
//
|
|
//SS: sort thelocker details
|
|
GumpDispatchUpdate(Type,
|
|
Waiter->Async.Context,
|
|
FALSE,
|
|
FALSE,
|
|
Waiter->Async.BufferLength,
|
|
Waiter->Async.Buffer);
|
|
|
|
CL_ASSERT(GumpRpcBindings[Waiter->NodeId] != NULL);
|
|
GumDeliverPostCallback(GumpRpcBindings[Waiter->NodeId],
|
|
NmGetNodeId(NmLocalNode)+1,
|
|
Type,
|
|
GumpSequence-1,
|
|
Waiter->Async.Context,
|
|
Waiter->Async.BufferLength,
|
|
Waiter->Async.BufferPtr);
|
|
MIDL_user_free(Waiter->Async.Buffer);
|
|
}
|
|
|
|
//
|
|
// Free the wait block and process the next entry on the queue.
|
|
//
|
|
LocalFree(Waiter);
|
|
|
|
EnterCriticalSection(&GumpUpdateLock);
|
|
|
|
}
|
|
#endif
|
|
}
|
|
//
|
|
// No more waiters, just unlock and we are done.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[GUM] GumpDoUnlockingUpdate releasing lock ownership\n");
|
|
GumpLockingNode = (DWORD)-1;
|
|
LeaveCriticalSection(&GumpUpdateLock);
|
|
LeaveCriticalSection(&GumpLock);
|
|
return;
|
|
}
|