Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

382 lines
8.8 KiB

/*++
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.
//
}
}