|
|
/*++
Copyright (c) 1995-2000 Microsoft Corporation
Module Name:
mrsw.c
Abstract:
This module implements a multiple reader single write synchronization method. Author:
Dave Hastings (daveh) creation-date 26-Jul-1995
Revision History:
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include "wx86.h"
#include "wx86nt.h"
#include "cpuassrt.h"
#include "config.h"
#include "mrsw.h"
#include "cpumain.h"
#include "atomic.h"
ASSERTNAME;
MRSWOBJECT MrswEP; // Entrypoint MRSW synchronization object
MRSWOBJECT MrswTC; // Translation cache MRSW synchronization object
MRSWOBJECT MrswIndirTable; // Indirect Control Transfer Table synchronization object
BOOL MrswInitializeObject( PMRSWOBJECT Mrsw ) /*++
Routine Description:
This routine initializes the fields of Mrsw to their default values, and creates the events.
Arguments:
Mrsw -- Supplies a pointer to an MRSWOBJECT to initialize Return Value:
TRUE on success, FALSE on failure.
--*/ { NTSTATUS Status;
//
// Initialize the counters
//
ZeroMemory(Mrsw, sizeof(MRSWOBJECT)); //
// Create the ReaderEvent and WriterEvent
//
Status = NtCreateEvent(&Mrsw->ReaderEvent, EVENT_ALL_ACCESS, NULL, // POBJECT_ATTRIBUTES
NotificationEvent, // ManualReset
FALSE); // InitialState
if (!NT_SUCCESS(Status)) { return FALSE; }
Status = NtCreateEvent(&Mrsw->WriterEvent, EVENT_ALL_ACCESS, NULL, // POBJECT_ATTRIBUTES
SynchronizationEvent, // AutoReset
FALSE); // InitialState
if (!NT_SUCCESS(Status)) { NtClose(Mrsw->ReaderEvent); return FALSE; } return TRUE; }
VOID PossibleMrswTimeout( PMRSWOBJECT Mrsw ) /*++
Routine Description:
This function is called whenever an Mrsw function times out. It prompts the user, and if the user chooses Retry, the Mrsw function re-waits. If the user chooses Cancel, the CPU will attempt to launch NTSD and break into the debugger.
Arguments:
Mrsw -- Supplies the Mrsw which may have a deadlock Return Value:
--*/ { NTSTATUS Status; ULONG ErrorResponse;
LOGPRINT((ERRORLOG, "WX86CPU: Possible deadlock in Mrsw %x\n", Mrsw)); Status = NtRaiseHardError( STATUS_POSSIBLE_DEADLOCK | 0x10000000, 0, 0, NULL, OptionRetryCancel, &ErrorResponse); if (!NT_SUCCESS(Status) || ErrorResponse == ResponseCancel) { DbgBreakPoint(); } }
VOID MrswWriterEnter( PMRSWOBJECT Mrsw ) /*++
Routine Description:
This function causes the caller to enter the Mrsw as the (single) writer.
Arguments:
Mrsw -- Supplies the Mrsw to enter Return Value:
--*/ { DWORD dwCounters; MRSWCOUNTERS Counters; NTSTATUS r;
//
// reset the reader event so that any readers that find the
// WriterCount > 0 will actually wait. We have to do that now,
// because if we wait, the reader might wait on the event before we
// got it reset.
//
r= NtClearEvent(Mrsw->ReaderEvent); if (!NT_SUCCESS(r)) { #if DBG
LOGPRINT((ERRORLOG, "WX86CPU: Got status %x from NtClearEvent\n", r)); #endif
RtlRaiseStatus(r); } //
// Get the counters and increment the writer count
// This is done atomically
//
dwCounters = MrswFetchAndIncrementWriter((DWORD *)&(Mrsw->Counters)); Counters = *(PMRSWCOUNTERS)&dwCounters; CPUASSERTMSG(Counters.WriterCount != 0, "WriterCount overflowed");
//
// If there is a writer or a reader already, wait for them to finish
//
if ( (Counters.WriterCount > 1) || (Counters.ReaderCount) ) { NTSTATUS r;
// Ensure We are not about to wait on ourselves.
CPUASSERTMSG(Mrsw->WriterThreadId != ProxyGetCurrentThreadId(), "MrswWriterEnter() called twice by the same thread");
for (;;) { r = NtWaitForSingleObject( Mrsw->WriterEvent, FALSE, &MrswTimeout ); if (r == STATUS_TIMEOUT) { PossibleMrswTimeout(Mrsw); } else if (NT_SUCCESS(r)) { break; } else { #if DBG
LOGPRINT((ERRORLOG, "WX86CPU: Got status %x from NtWaitForCriticalSection\n", r)); #endif
RtlRaiseStatus(r); } } }
#if DBG
CPUASSERTMSG(Mrsw->WriterThreadId == 0, "Another writer still is active."); Mrsw->WriterThreadId = ProxyGetCurrentThreadId(); #endif
}
VOID MrswWriterExit( PMRSWOBJECT Mrsw ) /*++
Routine Description:
This function causes the caller to exit the Mrsw. It will restart the next writer if there is one, or the readers if there are any
Arguments:
Mrsw -- Supplies the Mrsw to exit Return Value:
--*/ { DWORD dwCounters; MRSWCOUNTERS Counters;
// Ensure we are the active writer
CPUASSERTMSG(Mrsw->WriterThreadId == ProxyGetCurrentThreadId(), "MrswWriterExit: current thread is not the writer");
//
// Decrement the count of writers
//
#if DBG
//
// Set the thread id to 0 first, so if another writer comes along,
// we don't zero out its thread id.
//
Mrsw->WriterThreadId = 0; #endif
dwCounters = MrswFetchAndDecrementWriter((DWORD *)&(Mrsw->Counters)); Counters = *(PMRSWCOUNTERS)&dwCounters;
CPUASSERTMSG(Counters.WriterCount != 0xffff, "Writer underflow");
//
// Start a waiting writer if there is one. If there is no writer
// start the waiting readers
//
if (Counters.WriterCount) {
NtSetEvent(Mrsw->WriterEvent, NULL);
} else {
NtSetEvent(Mrsw->ReaderEvent, NULL); } }
VOID MrswReaderEnter( PMRSWOBJECT Mrsw ) /*++
Routine Description:
This function causes the caller to enter the Mrsw as a reader.
Arguments:
Mrsw -- Supplies the Mrsw to enter Return Value:
--*/ { DWORD dwCounters; MRSWCOUNTERS Counters;
for (;;) { //
// Increment the count of readers. If a writer is active, DO NOT
// increment the read count. In that case, we must block until the
// writer is done, then try again.
//
dwCounters = MrswFetchAndIncrementReader((DWORD *)&(Mrsw->Counters)); Counters = *(PMRSWCOUNTERS)&dwCounters; CPUASSERTMSG(Counters.WriterCount || Counters.ReaderCount != 0, "Reader underflow");
if (Counters.WriterCount) { NTSTATUS r;
// Ensure we are not about to wait on ourselves.
CPUASSERTMSG(Mrsw->WriterThreadId != ProxyGetCurrentThreadId(), "MRSWReaderEnter(): Thread already has write lock");
//
// There is a writer, wait for it to finish
//
for (;;) { r = NtWaitForSingleObject( Mrsw->ReaderEvent, FALSE, &MrswTimeout ); if (r == STATUS_TIMEOUT) { PossibleMrswTimeout(Mrsw); } else if (NT_SUCCESS(r)) { break; } else { #if DBG
LOGPRINT((ERRORLOG, "WX86CPU: Got status %x from NtWaitForCriticalSection\n", r)); #endif
RtlRaiseStatus(r); } } } else { //
// No writer, so MrswFetchAndIncrementReader() incremented the
// reader count - OK to exit out of the loop.
//
break; } } }
VOID MrswReaderExit( PMRSWOBJECT Mrsw ) /*++
Routine Description:
This function causes the caller to exit the Mrsw. If this was the last reader, it will restart the a writer if there is one.
Arguments:
Mrsw -- Supplies the Mrsw to exit Return Value:
--*/ { DWORD dwCounters; MRSWCOUNTERS Counters;
//
// Decrement the count of active readers
//
dwCounters = MrswFetchAndDecrementReader((DWORD *)&(Mrsw->Counters)); Counters = *(PMRSWCOUNTERS)&dwCounters; CPUASSERTMSG(Counters.ReaderCount != 0xffff, "Reader underflow");
if (Counters.WriterCount) {
if (Counters.ReaderCount == 0) { //
// This thread is the last reader, and there is a writer
// waiting. Start the writer.
//
NtSetEvent(Mrsw->WriterEvent, NULL); }
} else { //
// There are no waiting readers and no writers, so do nothing.
//
} }
|