d09fdd61cb
Many include statements use forward slashes, while others use backwards slashes. This is inconsistent formatting. For this reason, I changed the backward slashes to forward slashes since that is the standard.
127 lines
4.6 KiB
C++
127 lines
4.6 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
#include "precomp.h"
|
|
|
|
#include "WaitQueue.h"
|
|
#include "WaitBlock.h"
|
|
|
|
#include "../host/globals.h"
|
|
#include "../host/utils.hpp"
|
|
|
|
// Routine Description:
|
|
// - Instantiates a new ConsoleWaitQueue
|
|
ConsoleWaitQueue::ConsoleWaitQueue() :
|
|
_blocks()
|
|
{
|
|
}
|
|
|
|
// Routine Description:
|
|
// - Destructs a ConsoleWaitQueue
|
|
// - This will notify any remaining waiting items that the associated process/object is dying.
|
|
ConsoleWaitQueue::~ConsoleWaitQueue()
|
|
{
|
|
// Notify all blocks that the thread or object is dying when destroyed.
|
|
NotifyWaiters(TRUE, WaitTerminationReason::ThreadDying);
|
|
}
|
|
|
|
// Routine Description:
|
|
// - Establishes a wait (call me back later) for a particular message with a given callback routine and its parameter
|
|
// Arguments:
|
|
// - pWaitReplyMessage - The API message that we're deferring until data is available later.
|
|
// - pWaiter - The context/callback information to restore and dispatch the call later.
|
|
// Return Value:
|
|
// - S_OK if enqueued appropriately and everything is alright. Or suitable HRESULT failure otherwise.
|
|
[[nodiscard]] HRESULT ConsoleWaitQueue::s_CreateWait(_Inout_ CONSOLE_API_MSG* const pWaitReplyMessage,
|
|
_In_ IWaitRoutine* const pWaiter)
|
|
{
|
|
// Normally we'd have the Wait Queue handle the insertion of the block into the queue, but
|
|
// the console does queues in a somewhat special way.
|
|
//
|
|
// Each block belongs in two queues:
|
|
// 1. The process queue of the client that dispatched the request
|
|
// 2. The object queue that the request will be serviced by
|
|
// As such, when a wait occurs, it gets added to both queues.
|
|
//
|
|
// It will end up being serviced by one or the other queue, but when it is serviced, it must be
|
|
// removed from both so it is not double processed.
|
|
//
|
|
// Therefore, I've inverted the queue management responsibility into the WaitBlock itself
|
|
// and made it a friend to this WaitQueue class.
|
|
|
|
return ConsoleWaitBlock::s_CreateWait(pWaitReplyMessage,
|
|
pWaiter);
|
|
}
|
|
|
|
// Routine Description:
|
|
// - Instructs this queue to attempt to callback waiting requests
|
|
// Arguments:
|
|
// - fNotifyAll - If true, we will notify all items in the queue. If false, we will only notify the first item.
|
|
// Return Value:
|
|
// - True if any block was successfully notified. False if no blocks were successful.
|
|
bool ConsoleWaitQueue::NotifyWaiters(const bool fNotifyAll)
|
|
{
|
|
return NotifyWaiters(fNotifyAll, WaitTerminationReason::NoReason);
|
|
}
|
|
|
|
// Routine Description:
|
|
// - Instructs this queue to attempt to callback waiting requests and request termination with the given reason
|
|
// Arguments:
|
|
// - fNotifyAll - If true, we will notify all items in the queue. If false, we will only notify the first item.
|
|
// - TerminationReason - A reason/message to pass to each waiter signaling it should terminate appropriately.
|
|
// Return Value:
|
|
// - True if any block was successfully notified. False if no blocks were successful.
|
|
bool ConsoleWaitQueue::NotifyWaiters(const bool fNotifyAll,
|
|
const WaitTerminationReason TerminationReason)
|
|
{
|
|
bool fResult = false;
|
|
|
|
auto it = _blocks.cbegin();
|
|
while (!_blocks.empty() && it != _blocks.cend())
|
|
{
|
|
ConsoleWaitBlock* const WaitBlock = (*it);
|
|
if (nullptr == WaitBlock)
|
|
{
|
|
break;
|
|
}
|
|
|
|
auto const nextIt = std::next(it); // we have to capture next before it is potentially erased
|
|
|
|
if (_NotifyBlock(WaitBlock, TerminationReason))
|
|
{
|
|
fResult = true;
|
|
}
|
|
|
|
if (!fNotifyAll)
|
|
{
|
|
break;
|
|
}
|
|
|
|
it = nextIt;
|
|
}
|
|
|
|
return fResult;
|
|
}
|
|
|
|
// Routine Description:
|
|
// - A helper to delete successfully notified callbacks
|
|
// Arguments:
|
|
// - pWaitBlock - A block containing callback data
|
|
// - TerminationReason - Optional reason to tell the callback to terminate. If 0, we're not requesting termination.
|
|
// Return Value:
|
|
// - True if callback successfully delivered data. False if callback still needs to wait longer.
|
|
bool ConsoleWaitQueue::_NotifyBlock(_In_ ConsoleWaitBlock* pWaitBlock,
|
|
_In_ WaitTerminationReason TerminationReason)
|
|
{
|
|
// Attempt to notify block with the given reason.
|
|
bool const fResult = pWaitBlock->Notify(TerminationReason);
|
|
|
|
if (fResult)
|
|
{
|
|
// If it was successful, delete it. (It will remove itself from appropriate queues.)
|
|
delete pWaitBlock;
|
|
}
|
|
|
|
return fResult;
|
|
}
|