reactos/sdk/lib/drivers/wdf/shared/support/fxtransactionedlist.cpp
Victor Perevertkin 1f377076d7
[WDF] Fix KMDF so it can compile with ReactOS SDK
Not all files are included, but these are necessary to compile cdrom driver.
So far it can only be statically linked with drivers, a proper
implementation requires wdfldr helper driver
2020-11-03 00:06:27 +03:00

553 lines
13 KiB
C++

/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
FxTransactionedList.cpp
Abstract:
This module implements a simple transactioned list which allows the caller
to lock the list and then iterate over it without worrying about deletes
and adds.
Author:
Environment:
Both kernel and user mode
Revision History:
--*/
#include "fxsupportpch.hpp"
FxTransactionedList::FxTransactionedList()
{
m_ListLockedRecursionCount = 0;
m_DeleteOnRemove = FALSE;
m_Deleting = FALSE;
m_Retries = 0;
m_DeletingDoneEvent = NULL;
InitializeListHead(&m_ListHead);
InitializeListHead(&m_TransactionHead);
}
FxTransactionedList::~FxTransactionedList()
{
FxTransactionedEntry* pEntry;
PLIST_ENTRY ple;
//
// If m_DeleteOnRemove is FALSE, there is no need to iterate over any of the
// lists to free anything.
//
if (m_DeleteOnRemove == FALSE) {
ASSERT(IsListEmpty(&m_ListHead));
ASSERT(IsListEmpty(&m_TransactionHead));
return;
}
ASSERT(m_ListLockedRecursionCount == 0);
while (!IsListEmpty(&m_ListHead)) {
ple = RemoveHeadList(&m_ListHead);
InitializeListHead(ple);
pEntry = FxTransactionedEntry::_FromEntry(ple);
switch (pEntry->m_Transaction) {
case FxTransactionActionNothing:
//
// Nothing to do, no pending transaction
//
break;
case FxTransactionActionAdd:
//
// Should not have an add transaction and be on the main list at the
// same time!
//
ASSERT(FALSE);
break;
case FxTransactionActionRemove:
//
// Make sure it is not on the transaction list
//
RemoveEntryList(&pEntry->m_TransactionLink);
InitializeListHead(&pEntry->m_TransactionLink);
//
// When inserted as a remove transaction, we add this reference in
// RemoveLocked
//
pEntry->GetTransactionedObject()->RELEASE(pEntry);
break;
}
pEntry->GetTransactionedObject()->DeleteObject();
}
while (!IsListEmpty(&m_TransactionHead)) {
ple = RemoveHeadList(&m_TransactionHead);
InitializeListHead(ple);
pEntry = CONTAINING_RECORD(ple, FxTransactionedEntry, m_TransactionLink);
//
// We yanked out all of the removes in the previous loop
//
ASSERT(pEntry->m_Transaction == FxTransactionActionAdd);
//
// Delete the object since this list owns it.
//
pEntry->GetTransactionedObject()->DeleteObject();
}
}
VOID
FxTransactionedList::LockForEnum(
__in PFX_DRIVER_GLOBALS FxDriverGlobals
)
{
KIRQL irql;
AcquireLock(FxDriverGlobals, &irql);
m_ListLockedRecursionCount++;
ReleaseLock(FxDriverGlobals, irql);
}
VOID
FxTransactionedList::UnlockFromEnum(
__in PFX_DRIVER_GLOBALS FxDriverGlobals
)
{
LIST_ENTRY releaseHead;
KIRQL irql;
MxEvent* event;
InitializeListHead(&releaseHead);
event = NULL;
AcquireLock(FxDriverGlobals, &irql);
m_ListLockedRecursionCount--;
ProcessTransactionList(&releaseHead);
if (m_ListLockedRecursionCount == 0 && m_Deleting) {
event = m_DeletingDoneEvent;
m_DeletingDoneEvent = NULL;
}
ReleaseLock(FxDriverGlobals, irql);
ProcessObjectsToRelease(&releaseHead);
if (event != NULL) {
event->Set();
}
}
VOID
FxTransactionedList::ProcessTransactionList(
__in PLIST_ENTRY ReleaseHead
)
{
LIST_ENTRY *ple;
FxTransactionedEntry* pEntry;
//
// If there are other iterators, do not process transactions until they are
// done.
//
if (m_ListLockedRecursionCount != 0) {
return;
}
while (!IsListEmpty(&m_TransactionHead)) {
ple = RemoveHeadList(&m_TransactionHead);
InitializeListHead(ple);
pEntry = CONTAINING_RECORD(ple, FxTransactionedEntry, m_TransactionLink);
ASSERT(pEntry->m_Transaction != FxTransactionActionNothing);
if (pEntry->m_Transaction == FxTransactionActionAdd) {
//
// Add to the main list
//
InsertTailList(&m_ListHead, &pEntry->m_ListLink);
//
// Virtual notification of addition
//
EntryAdded(pEntry);
}
else if (pEntry->m_Transaction == FxTransactionActionRemove) {
//
// Remove it from the main list and move it to a free list
//
RemoveEntryList(&pEntry->m_ListLink);
InsertTailList(ReleaseHead, &pEntry->m_TransactionLink);
//
// Virtual notification of removal
//
EntryRemoved(pEntry);
}
pEntry->m_Transaction = FxTransactionActionNothing;
}
}
VOID
FxTransactionedList::ProcessObjectsToRelease(
__in PLIST_ENTRY ReleaseHead
)
{
LIST_ENTRY *ple;
FxTransactionedEntry* pEntry;
while (!IsListEmpty(ReleaseHead)) {
ple = RemoveHeadList(ReleaseHead);
InitializeListHead(ple);
pEntry = CONTAINING_RECORD(ple, FxTransactionedEntry, m_TransactionLink);
//
// We always release our reference we took when we post the change
// to the list
//
pEntry->GetTransactionedObject()->RELEASE(pEntry);
//
// 2ndary release if the list is set to do this
//
if (m_DeleteOnRemove) {
pEntry->GetTransactionedObject()->DeleteObject();
}
}
}
BOOLEAN
FxTransactionedList::Deleting(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in_opt MxEvent* DeleteDoneEvent
)
{
KIRQL irql;
BOOLEAN result;
result = TRUE;
AcquireLock(FxDriverGlobals, &irql);
m_Deleting = TRUE;
if (m_ListLockedRecursionCount != 0) {
m_DeletingDoneEvent = DeleteDoneEvent;
result = FALSE;
}
ReleaseLock(FxDriverGlobals, irql);
return result;
}
_Must_inspect_result_
NTSTATUS
FxTransactionedList::Add(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in FxTransactionedEntry* Entry
)
{
NTSTATUS status;
KIRQL irql;
AcquireLock(FxDriverGlobals, &irql);
if (m_Deleting) {
status = STATUS_INVALID_DEVICE_STATE;
}
else {
status = ProcessAdd(Entry);
}
if (NT_SUCCESS(status)) {
if (m_ListLockedRecursionCount == 0) {
//
// We can insert the entry now, do so
//
InsertTailList(&m_ListHead, &Entry->m_ListLink);
EntryAdded(Entry);
}
else {
//
// List is locked, queue a transaction
//
Entry->m_Transaction = FxTransactionActionAdd;
InsertTailList(&m_TransactionHead, &Entry->m_TransactionLink);
}
}
ReleaseLock(FxDriverGlobals, irql);
return status;
}
VOID
FxTransactionedList::SearchForAndRemove(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in PVOID EntryData
)
{
KIRQL irql;
FxTransactionedEntry* pEntry;
PLIST_ENTRY ple;
BOOLEAN removed;
removed = FALSE;
AcquireLock(FxDriverGlobals, &irql);
for (ple = m_TransactionHead.Flink;
ple != &m_TransactionHead;
ple = ple->Flink) {
pEntry = CONTAINING_RECORD(ple, FxTransactionedEntry, m_TransactionLink);
if (Compare(pEntry, EntryData)) {
if (pEntry->GetTransactionAction() == FxTransactionActionAdd) {
RemoveEntryList(&pEntry->m_TransactionLink);
InitializeListHead(&pEntry->m_TransactionLink);
removed = TRUE;
}
else {
//
// Already being removed, just return
//
ASSERT(pEntry->GetTransactionAction() ==
FxTransactionActionRemove);
}
goto Done;
}
}
//
// Walk the committed list
//
pEntry = NULL;
while ((pEntry = GetNextEntryLocked(pEntry)) != NULL) {
if (Compare(pEntry, EntryData)) {
removed = RemoveLocked(pEntry);
break;
}
}
Done:
ReleaseLock(FxDriverGlobals, irql);
if (removed && m_DeleteOnRemove) {
pEntry->GetTransactionedObject()->DeleteObject();
}
}
VOID
FxTransactionedList::Remove(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in FxTransactionedEntry* Entry
)
{
BOOLEAN removed;
KIRQL irql;
AcquireLock(FxDriverGlobals, &irql);
removed = RemoveLocked(Entry);
ReleaseLock(FxDriverGlobals,irql);
if (removed && m_DeleteOnRemove) {
Entry->GetTransactionedObject()->DeleteObject();
}
}
BOOLEAN
FxTransactionedList::RemoveLocked(
__in FxTransactionedEntry* Entry
)
{
BOOLEAN removed;
removed = FALSE;
if (Entry->m_Transaction == FxTransactionActionAdd) {
//
// Not yet added to the list proper, remove it from the transaction list
//
removed = TRUE;
RemoveEntryList(&Entry->m_TransactionLink);
InitializeListHead(&Entry->m_TransactionLink);
Entry->m_Transaction = FxTransactionActionNothing;
}
else {
ASSERT(!IsListEmpty(&Entry->m_ListLink));
if (m_ListLockedRecursionCount == 0) {
//
// List is not locked, remove it now
//
RemoveEntryList(&Entry->m_ListLink);
InitializeListHead(&Entry->m_ListLink);
//
// Virtual notification
//
EntryRemoved(Entry);
removed = TRUE;
}
else {
//
// List is locked for enumeration, queue a transaction
//
Entry->m_Transaction = FxTransactionActionRemove;
InsertTailList(&m_TransactionHead, &Entry->m_TransactionLink);
Entry->GetTransactionedObject()->ADDREF(Entry);
}
}
return removed;
}
_Must_inspect_result_
FxTransactionedEntry*
FxTransactionedList::GetNextEntry(
__in_opt FxTransactionedEntry* Entry
)
/*++
Routine Description:
Gets the next entry. Assumes the caller has called LockedForEnum
Arguments:
Entry the current entry in the iteratation, NULL for the first
Return Value:
next entry in the iteration, NULL if there are no more entries
--*/
{
//
// The caller should have locked the list for enumeration
//
ASSERT(m_ListLockedRecursionCount > 0 || m_Deleting);
return GetNextEntryLocked(Entry);
}
_Must_inspect_result_
FxTransactionedEntry*
FxTransactionedList::GetNextEntryLocked(
__in_opt FxTransactionedEntry* Entry
)
/*++
Routine Description:
Returns the next entry. Assumes that the caller has the list locked through
a call to AcquireLock() or through LockForEnum()
Arguments:
Entry the current entry in the iteratation, NULL for the first
Return Value:
next entry in the iteration, NULL if there are no more entries
--*/
{
PLIST_ENTRY ple;
if (Entry == NULL) {
ple = m_ListHead.Flink;
}
else {
ple = Entry->m_ListLink.Flink;
}
//
// Find the next entry which does not have a pending transaction on it
//
for ( ; ple != &m_ListHead; ple = ple->Flink) {
FxTransactionedEntry* pNext;
pNext = FxTransactionedEntry::_FromEntry(ple);
if (pNext->m_Transaction == FxTransactionActionNothing) {
return pNext;
}
}
//
// Reached the end of the list
//
return NULL;
}
FxSpinLockTransactionedList::FxSpinLockTransactionedList() :
FxTransactionedList()
{
}
__drv_raisesIRQL(DISPATCH_LEVEL)
__drv_maxIRQL(DISPATCH_LEVEL)
VOID
FxSpinLockTransactionedList::AcquireLock(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__out PKIRQL Irql
)
{
UNREFERENCED_PARAMETER(FxDriverGlobals);
m_ListLock.Acquire(Irql);
}
__drv_requiresIRQL(DISPATCH_LEVEL)
VOID
FxSpinLockTransactionedList::ReleaseLock(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in __drv_restoresIRQL KIRQL Irql
)
{
UNREFERENCED_PARAMETER(FxDriverGlobals);
m_ListLock.Release(Irql);
}
_Acquires_lock_(_Global_critical_region_)
VOID
FxWaitLockTransactionedList::AcquireLock(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__out PKIRQL Irql
)
{
UNREFERENCED_PARAMETER(Irql);
m_StateChangeListLock.AcquireLock(FxDriverGlobals);
}
_Releases_lock_(_Global_critical_region_)
VOID
FxWaitLockTransactionedList::ReleaseLock(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in KIRQL Irql
)
{
UNREFERENCED_PARAMETER(Irql);
m_StateChangeListLock.ReleaseLock(FxDriverGlobals);
}