mirror of
https://github.com/reactos/reactos.git
synced 2024-08-05 19:11:16 +00:00
1f377076d7
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
553 lines
13 KiB
C++
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);
|
|
}
|
|
|
|
|