Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1905 lines
48 KiB

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
blockmgr.cpp
Abstract:
This module contains the implementation of the block memory manager
Author:
Keith Lau ([email protected])
Revision History:
keithlau 02/27/98 created
--*/
#include "windows.h"
#include "dbgtrace.h"
#include "filehc.h"
#include "signatur.h"
#include "blockmgr.h"
//
// I really wanted to keep the memory manager completely independent from
// the rest of the stuff, but I realized it makes more sense to have the
// memory manager be aware of the IMailMsgPropertyStream so hrer goes ...
//
// If you remove CommitDirtyBlocks, you can get rid of the include below.
//
#include "mailmsg.h"
//
// A commit writes the entire stream, but possibly using several iterations.
// This specifies how many blocks to write in each iteration.
//
#define CMAILMSG_COMMIT_PAGE_BLOCK_SIZE 256
// Global (set by registry key) indicating that property pages be filled with
// a byte pattern after allocation
extern DWORD g_fFillPropertyPages;
/***************************************************************************/
// Debug stuff
//
#ifndef _ASSERT
#define _ASSERT(x) if (!(x)) DebugBreak()
#endif
#ifdef DEBUG_TRACK_ALLOCATION_BOUNDARIES
HRESULT SetAllocationBoundary(
FLAT_ADDRESS faOffset,
LPBLOCK_HEAP_NODE pNode
)
{
DWORD dwBit;
faOffset &= BLOCK_HEAP_PAYLOAD_MASK;
faOffset >>= 2;
dwBit = (DWORD)(faOffset & 7);
faOffset >>= 3;
pNode->stAttributes.rgbBoundaries[faOffset] |= (0x80 >> dwBit);
return(S_OK);
}
HRESULT VerifyAllocationBoundary(
FLAT_ADDRESS faOffset,
DWORD dwLength,
LPBLOCK_HEAP_NODE pNode
)
{
DWORD dwStartingBit;
DWORD dwStartingByte;
DWORD dwBitsToScan;
// 7f because we can start on a boundary and be perfectly cool
BYTE bStartingMask = 0x7f;
BYTE bEndingMask = 0xff;
faOffset &= BLOCK_HEAP_PAYLOAD_MASK;
faOffset >>= 2; // DWORD per bit
// Determine the start
// note : these casts are safe because the value in faOffset is
// only 10-bits (BLOCK_HEAP_PAYLOAD_MASK) at this point.
dwStartingBit = (DWORD)(faOffset & 7);
dwStartingByte = (DWORD)(faOffset >> 3);
bStartingMask >>= dwStartingBit;
// Determine the number of bits to scan, each bit corresponds
// to a DWORD, rounded up to next DWORD
dwBitsToScan = dwLength + 3;
dwBitsToScan >>= 2;
// Scan it
// Case 1: Start and End bits within the same byte
if ((dwStartingBit + dwBitsToScan) <= 8)
{
DWORD dwBitsFromRight = 8 - (dwStartingBit + dwBitsToScan);
bEndingMask <<= dwBitsFromRight;
bStartingMask = bStartingMask & bEndingMask;
if (pNode->stAttributes.rgbBoundaries[dwStartingByte] & bStartingMask)
return(TYPE_E_OUTOFBOUNDS);
}
else
// Case 2: Multiple bytes
{
if (pNode->stAttributes.rgbBoundaries[dwStartingByte++] & bStartingMask)
return(TYPE_E_OUTOFBOUNDS);
dwBitsToScan -= (8 - dwStartingBit);
while (dwBitsToScan >= 8)
{
// See if we cross any boundaries
if (dwBitsToScan >= 32)
{
if (*(UNALIGNED DWORD *)(pNode->stAttributes.rgbBoundaries + dwStartingByte) != 0)
return(TYPE_E_OUTOFBOUNDS);
dwStartingByte += 4;
dwBitsToScan -= 32;
}
else if (dwBitsToScan >= 16)
{
if (*(UNALIGNED WORD *)(pNode->stAttributes.rgbBoundaries + dwStartingByte) != 0)
return(TYPE_E_OUTOFBOUNDS);
dwStartingByte += 2;
dwBitsToScan -= 16;
}
else
{
if (pNode->stAttributes.rgbBoundaries[dwStartingByte++] != 0)
return(TYPE_E_OUTOFBOUNDS);
dwBitsToScan -= 8;
}
}
// Final byte
if (dwBitsToScan)
{
bEndingMask <<= (8 - dwBitsToScan);
if (pNode->stAttributes.rgbBoundaries[dwStartingByte] & bEndingMask)
return(TYPE_E_OUTOFBOUNDS);
}
}
return(S_OK);
}
#endif
/***************************************************************************/
// Memory accessor class
//
CPool CBlockMemoryAccess::m_Pool((DWORD)'pBMv');
HRESULT CBlockMemoryAccess::AllocBlock(
LPVOID *ppvBlock,
DWORD dwBlockSize
)
{
TraceFunctEnterEx((LPARAM)this, "CBlockMemoryAccess::AllocBlock");
_ASSERT(dwBlockSize == BLOCK_HEAP_NODE_SIZE);
LPVOID pvBlock = m_Pool.Alloc();
if (pvBlock) {
((LPBLOCK_HEAP_NODE) pvBlock)->stAttributes.fFlags = 0;
} else if (SUCCEEDED(CMemoryAccess::AllocBlock(ppvBlock, BLOCK_HEAP_NODE_SIZE)))
{
pvBlock = *ppvBlock;
((LPBLOCK_HEAP_NODE) pvBlock)->stAttributes.fFlags = BLOCK_NOT_CPOOLED;
}
if (pvBlock)
{
ZeroMemory(((LPBLOCK_HEAP_NODE)pvBlock)->rgpChildren, sizeof(LPBLOCK_HEAP_NODE) * BLOCK_HEAP_ORDER);
#ifdef DEBUG_TRACK_ALLOCATION_BOUNDARIES
ZeroMemory(((LPBLOCK_HEAP_NODE)pvBlock)->stAttributes.rgbBoundaries, BLOCK_HEAP_PAYLOAD >> 5);
#endif
// If debugging registry key is set, init the payload to a byte pattern of '????'
if(g_fFillPropertyPages)
{
FillMemory(((LPBLOCK_HEAP_NODE)pvBlock)->rgbData,
sizeof(((LPBLOCK_HEAP_NODE)pvBlock)->rgbData), 0x3F);
}
*ppvBlock = pvBlock;
TraceFunctLeaveEx((LPARAM) this);
return(S_OK);
}
*ppvBlock = NULL;
ErrorTrace((LPARAM)this, "CBlockMemoryAccess::AllocBlock failed");
TraceFunctLeaveEx((LPARAM)this);
return(E_OUTOFMEMORY);
}
HRESULT CBlockMemoryAccess::FreeBlock(
LPVOID pvBlock
)
{
TraceFunctEnterEx((LPARAM)this, "CBlockMemoryAccess::FreeBlock");
if ((((LPBLOCK_HEAP_NODE) pvBlock)->stAttributes.fFlags) &
BLOCK_NOT_CPOOLED)
{
CMemoryAccess::FreeBlock(pvBlock);
} else {
m_Pool.Free(pvBlock);
}
return(S_OK);
}
HRESULT CMemoryAccess::AllocBlock(
LPVOID *ppvBlock,
DWORD dwBlockSize
)
{
TraceFunctEnterEx(0, "CMemoryAccess::AllocBlock");
LPVOID pvBlock = (LPVOID) new BYTE[dwBlockSize];
if (pvBlock)
{
ZeroMemory(pvBlock, dwBlockSize);
*ppvBlock = pvBlock;
return(S_OK);
}
*ppvBlock = NULL;
return(E_OUTOFMEMORY);
}
HRESULT CMemoryAccess::FreeBlock(
LPVOID pvBlock
)
{
TraceFunctEnterEx(0, "CMemoryAccess::FreeBlock");
delete[] pvBlock;
TraceFunctLeave();
return S_OK;
}
/***************************************************************************/
// CBlockContext implementation
//
BOOL CBlockContext::IsValid()
{
return((m_dwSignature == BLOCK_CONTEXT_SIGNATURE_VALID));
}
void CBlockContext::Set(
LPBLOCK_HEAP_NODE pLastAccessedNode,
FLAT_ADDRESS faLastAccessedNodeOffset
)
{
m_pLastAccessedNode = pLastAccessedNode;
m_faLastAccessedNodeOffset = faLastAccessedNodeOffset;
m_dwSignature = BLOCK_CONTEXT_SIGNATURE_VALID;
}
void CBlockContext::Invalidate()
{
m_dwSignature = BLOCK_CONTEXT_SIGNATURE_INVALID;
}
/***************************************************************************/
// CBlockManager implementation
//
CBlockManager::CBlockManager(
IMailMsgProperties *pMsg,
CBlockManagerGetStream *pParent
)
{
TraceFunctEnterEx((LPARAM)this, "CBlockManager::CBlockManager");
// Initialize
m_dwSignature = BLOCK_HEAP_SIGNATURE_VALID;
m_pRootNode = NULL;
m_faEndOfData = 0;
m_idNodeCount = 0;
m_pParent = pParent;
m_pMsg = pMsg;
SetDirty(FALSE);
#ifdef DEBUG
m_fCommitting = FALSE;
#endif
TraceFunctLeaveEx((LPARAM)this);
}
CBlockManager::~CBlockManager()
{
TraceFunctEnterEx((LPARAM)this, "CBlockManager::~CBlockManager");
// Releases all blocks
Release();
// Finally, invalidate signature
m_dwSignature = BLOCK_HEAP_SIGNATURE_INVALID;
TraceFunctLeaveEx((LPARAM)this);
}
HRESULT CBlockManager::SetStreamSize(
DWORD dwStreamSize
)
{
// Initialize the stream size, this is only used when binding a
// fresh MailMsg object to an existing stream.
m_faEndOfData = (FLAT_ADDRESS)dwStreamSize;
m_idNodeCount = ((dwStreamSize + BLOCK_HEAP_PAYLOAD_MASK) >> BLOCK_HEAP_PAYLOAD_BITS);
return(S_OK);
}
BOOL CBlockManager::IsValid()
{
return(m_dwSignature == BLOCK_HEAP_SIGNATURE_VALID);
}
HRESULT CBlockManager::GetStream(
IMailMsgPropertyStream **ppStream,
BOOL fLockAcquired
)
{
_ASSERT(ppStream);
if (!ppStream || !m_pParent)
return(E_POINTER);
HRESULT hrRes = m_pParent->GetStream(ppStream, fLockAcquired);
return(hrRes);
}
HRESULT CBlockManager::MoveToNode(
LPBLOCK_HEAP_NODE *ppNode,
HEAP_NODE_ID idTargetNode,
BOOL fLockAcquired
)
{
HRESULT hrRes = S_OK;
LPBLOCK_HEAP_NODE pNode;
HEAP_NODE_ID idNode;
if (!ppNode || !*ppNode)
return(E_POINTER);
if (idTargetNode >= m_idNodeCount)
return(STG_E_INVALIDPARAMETER);
pNode = *ppNode;
idNode = pNode->stAttributes.idNode;
// Jump if in the same parent node
if (idNode && idTargetNode)
{
if (((idNode - 1) >> BLOCK_HEAP_ORDER_BITS) ==
((idTargetNode - 1) >> BLOCK_HEAP_ORDER_BITS))
{
HEAP_NODE_ID idChildNode = (idTargetNode - 1) & BLOCK_HEAP_ORDER_MASK;
LPBLOCK_HEAP_NODE pParent = pNode->stAttributes.pParentNode;
*ppNode = pParent->rgpChildren[idChildNode];
if (!*ppNode)
hrRes = LoadBlockIfUnavailable(
idTargetNode,
pParent,
idChildNode,
ppNode,
fLockAcquired);
return(hrRes);
}
}
hrRes = GetNodeFromNodeId(
idTargetNode,
ppNode,
fLockAcquired);
return(hrRes);
}
HRESULT CBlockManager::GetNextNode(
LPBLOCK_HEAP_NODE *ppNode,
BOOL fLockAcquired
)
{
if (!ppNode || !*ppNode)
return(E_POINTER);
HRESULT hrRes = MoveToNode(
ppNode,
(*ppNode)->stAttributes.idNode + 1,
fLockAcquired);
if (FAILED(hrRes))
*ppNode = NULL;
return(hrRes);
}
HRESULT CBlockManager::LoadBlockIfUnavailable(
HEAP_NODE_ID idNode,
LPBLOCK_HEAP_NODE pParent,
HEAP_NODE_ID idChildNode,
LPBLOCK_HEAP_NODE *ppNode,
BOOL fLockAcquired
)
{
_ASSERT(ppNode);
if (*ppNode)
return(S_OK);
HRESULT hrRes = S_OK;
IMailMsgPropertyStream *pStream;
hrRes = GetStream(&pStream, fLockAcquired);
if (!SUCCEEDED(hrRes))
return(E_UNEXPECTED);
// Calculate the stream offset and load the block
// idNode shifted really contains an offset not a full pointer here so we
// can (and must) cast it for the call to ReadBlocks to be OK
DWORD dwOffset = (DWORD)(idNode << BLOCK_HEAP_PAYLOAD_BITS);
if (!fLockAcquired)
WriteLock();
if (!*ppNode)
{
LPBLOCK_HEAP_NODE pNode = NULL;
DWORD dwLength = BLOCK_HEAP_PAYLOAD;
hrRes = m_bma.AllocBlock(
(LPVOID *)&pNode,
BLOCK_HEAP_NODE_SIZE);
if (SUCCEEDED(hrRes))
{
LPBYTE pTemp = pNode->rgbData;
hrRes = pStream->ReadBlocks(
m_pMsg,
1,
&dwOffset,
&dwLength,
&pTemp,
NULL);
if (FAILED(hrRes) &&
(hrRes != HRESULT_FROM_WIN32(ERROR_HANDLE_EOF)))
{
HRESULT myRes = m_bma.FreeBlock(pNode);
_ASSERT(SUCCEEDED(myRes));
}
else
{
if (pParent)
pParent->rgpChildren[idChildNode] = pNode;
pNode->stAttributes.pParentNode = pParent;
RESET_BLOCK_FLAGS(pNode->stAttributes.fFlags);
pNode->stAttributes.idChildNode = idChildNode;
pNode->stAttributes.idNode = idNode;
pNode->stAttributes.faOffset = dwOffset;
*ppNode = pNode;
hrRes = S_OK;
}
}
}
if (!fLockAcquired)
WriteUnlock();
return(hrRes);
}
inline HRESULT CBlockManager::GetEdgeListFromNodeId(
HEAP_NODE_ID idNode,
HEAP_NODE_ID *rgEdgeList,
DWORD *pdwEdgeCount
)
{
DWORD dwCurrentLevel;
HEAP_NODE_ID *pEdge = rgEdgeList;
// This is a strictly internal call, we are assuming the caller
// will be optimized and will handle cases for idNode <=
// BLOCK_HEAP_ORDER. Processing only starts for 2 layers or more
// Debug: make sure we are within range
_ASSERT(idNode > BLOCK_HEAP_ORDER);
_ASSERT(idNode <= NODE_ID_ABSOLUTE_MAX);
// Strip off the root node
idNode--;
// We need to do depth minus 1 loops since the top edge will be
// the remainder of the final loop
for (dwCurrentLevel = 0;
dwCurrentLevel < (MAX_HEAP_DEPTH - 1);
)
{
// The quotient is the parent node in the upper level,
// the remainder is the the edge from the parent to the
// current node.
*pEdge++ = idNode & BLOCK_HEAP_ORDER_MASK;
idNode >>= BLOCK_HEAP_ORDER_BITS;
idNode--;
dwCurrentLevel++;
// If the node is less than the number of children per node,
// we are done.
if (idNode < BLOCK_HEAP_ORDER)
break;
}
*pEdge++ = idNode;
*pdwEdgeCount = dwCurrentLevel + 1;
return(S_OK);
}
//
// Inner-loop optimized for O(1) cost.
//
HRESULT CBlockManager::GetNodeFromNodeId(
HEAP_NODE_ID idNode,
LPBLOCK_HEAP_NODE *ppNode,
BOOL fLockAcquired
)
{
HRESULT hrRes = S_OK;
_ASSERT(IsValid());
_ASSERT(ppNode);
// If top level node, we return immediately. Note this is
// supposed to be the case 90% of the time
hrRes = LoadBlockIfUnavailable(0, NULL, 0, &m_pRootNode, fLockAcquired);
if (!idNode || FAILED(hrRes))
{
*ppNode = m_pRootNode;
return(hrRes);
}
LPBLOCK_HEAP_NODE pNode = m_pRootNode;
LPBLOCK_HEAP_NODE *ppMyNode = &m_pRootNode;
// Now, see if the referenced node exists
if (idNode >= m_idNodeCount)
return(STG_E_INVALIDPARAMETER);
// Optimize for 1 hop, we would scarcely have to go into
// the else case ...
if (idNode <= BLOCK_HEAP_ORDER)
{
ppMyNode = &(m_pRootNode->rgpChildren[idNode - 1]);
hrRes = LoadBlockIfUnavailable(idNode, m_pRootNode, idNode - 1, ppMyNode, fLockAcquired);
if (SUCCEEDED(hrRes))
*ppNode = *ppMyNode;
}
else
{
HEAP_NODE_ID rgEdgeList[MAX_HEAP_DEPTH];
DWORD dwEdgeCount;
HEAP_NODE_ID CurrentEdge;
HEAP_NODE_ID idFactor = 0;
// Debug: make sure we are within range
_ASSERT(idNode <= NODE_ID_ABSOLUTE_MAX);
// Get the edge list, backwards
GetEdgeListFromNodeId(idNode, rgEdgeList, &dwEdgeCount);
_ASSERT(dwEdgeCount >= 2);
// Walk the list backwards
while (dwEdgeCount--)
{
// Find the next bucket and calculate the node ID
CurrentEdge = rgEdgeList[dwEdgeCount];
ppMyNode = &(pNode->rgpChildren[CurrentEdge]);
idFactor <<= BLOCK_HEAP_ORDER_BITS;
idFactor += (CurrentEdge + 1);
hrRes = LoadBlockIfUnavailable(idFactor, pNode, CurrentEdge, ppMyNode, fLockAcquired);
if (FAILED(hrRes))
break;
// Set the current node to the bucket in the layer below
pNode = *ppMyNode;
}
// Fill in the results ...
*ppNode = pNode;
}
return(hrRes);
}
//
// Identical optimizations as GetNodeFromNodeId, O(1) cost.
//
HRESULT CBlockManager::GetParentNodeFromNodeId(
HEAP_NODE_ID idNode,
LPBLOCK_HEAP_NODE *ppNode
)
{
HRESULT hrRes = S_OK;
TraceFunctEnterEx((LPARAM)this, "CBlockManager::GetParentNodeFromNodeId");
_ASSERT(IsValid());
_ASSERT(ppNode);
// The root node has no parent, this should be avoided
// before calling this function, be we will fail gracefully
if (!idNode)
{
_ASSERT(idNode != 0);
*ppNode = NULL;
return(STG_E_INVALIDPARAMETER);
}
// Note m_pRootNode can be NULL if idNode is zero!
_ASSERT(m_pRootNode);
LPBLOCK_HEAP_NODE pNode = m_pRootNode;
LPBLOCK_HEAP_NODE *ppMyNode = &m_pRootNode;
// Optimize for 1 hop, we would scarcely have to go into
// the else case ...
if (idNode > BLOCK_HEAP_ORDER)
{
HEAP_NODE_ID rgEdgeList[MAX_HEAP_DEPTH];
DWORD dwEdgeCount;
HEAP_NODE_ID CurrentEdge;
HEAP_NODE_ID idFactor = 0;
// Debug: make sure we are within range
_ASSERT(idNode <= NODE_ID_ABSOLUTE_MAX);
// Get the edge list, backwards
GetEdgeListFromNodeId(idNode, rgEdgeList, &dwEdgeCount);
_ASSERT(dwEdgeCount >= 2);
// Walk the list backwards
--dwEdgeCount;
while (dwEdgeCount)
{
// Find the next bucket and calculate the node ID
CurrentEdge = rgEdgeList[dwEdgeCount];
ppMyNode = &(pNode->rgpChildren[CurrentEdge]);
idFactor <<= BLOCK_HEAP_ORDER_BITS;
idFactor += (CurrentEdge + 1);
hrRes = LoadBlockIfUnavailable(idFactor, pNode, CurrentEdge, ppMyNode, TRUE);
if (FAILED(hrRes))
break;
// Set the current node to the bucket in the layer below
pNode = *ppMyNode;
dwEdgeCount--;
}
}
// Fill in the results ...
*ppNode = *ppMyNode;
TraceFunctLeaveEx((LPARAM)this);
return(hrRes);
}
#define GetNodeIdFromOffset(faOffset) ((faOffset) >> BLOCK_HEAP_PAYLOAD_BITS)
HRESULT CBlockManager::InsertNodeGivenPreviousNode(
LPBLOCK_HEAP_NODE pNodeToInsert,
LPBLOCK_HEAP_NODE pPreviousNode
)
{
HRESULT hrRes = S_OK;
_ASSERT(IsValid());
_ASSERT(pNodeToInsert);
LPBLOCK_HEAP_NODE_ATTRIBUTES pAttrib = &pNodeToInsert->stAttributes;
TraceFunctEnterEx((LPARAM)this, "CBlockManager::InsertNodeGivenPreviousNode");
if (!pPreviousNode)
{
// This is the root node ...
DebugTrace((LPARAM)this, "Inserting the root node");
pAttrib->pParentNode = NULL;
pAttrib->idChildNode = 0;
pAttrib->idNode = 0;
pAttrib->faOffset = 0;
DEFAULT_BLOCK_FLAGS(pAttrib->fFlags);
m_pRootNode = pNodeToInsert;
TraceFunctLeaveEx((LPARAM)this);
return(S_OK);
}
else
{
LPBLOCK_HEAP_NODE_ATTRIBUTES pOldAttrib = &pPreviousNode->stAttributes;
// Fill out the attributes for the new node, we have a special case for the first node
// after the root, where we need to explicitly point its parent to the root node
if (pOldAttrib->idNode == 0)
{
pAttrib->pParentNode = m_pRootNode;
// We are child Id 0 again
pAttrib->idChildNode = 0;
}
else
{
pAttrib->pParentNode = pOldAttrib->pParentNode;
pAttrib->idChildNode = pOldAttrib->idChildNode + 1;
}
pAttrib->idNode = pOldAttrib->idNode + 1;
pAttrib->faOffset = pOldAttrib->faOffset + BLOCK_HEAP_PAYLOAD;
DEFAULT_BLOCK_FLAGS(pAttrib->fFlags);
if (pOldAttrib->idChildNode < BLOCK_HEAP_ORDER_MASK)
{
// We are in the same parent node, so it's simple
DebugTrace((LPARAM)this, "Inserting node at slot %u",
pAttrib->idChildNode);
pAttrib->pParentNode->rgpChildren[pAttrib->idChildNode] = pNodeToInsert;
TraceFunctLeaveEx((LPARAM)this);
return(S_OK);
}
}
// The previous node and the new node have different parents,
// so we got to work from scratch ...
LPBLOCK_HEAP_NODE pNode = NULL;
// We might as well search from the top ...
hrRes = GetParentNodeFromNodeId(pAttrib->idNode, &pNode);
if (SUCCEEDED(hrRes))
{
// Update the affected attributes
DebugTrace((LPARAM)this, "Inserting node at slot 0");
pAttrib->pParentNode = pNode;
pAttrib->idChildNode = 0;
// Hook up our parent
pNode->rgpChildren[0] = pNodeToInsert;
}
else
{
// The only reason for failre is that the parent
// of the requested parent is not allocated
_ASSERT(hrRes == STG_E_INVALIDPARAMETER);
}
TraceFunctLeaveEx((LPARAM)this);
return(hrRes);
}
HRESULT CBlockManager::GetAllocatedSize(
FLAT_ADDRESS *pfaSizeAllocated
)
{
HRESULT hrRes = S_OK;
_ASSERT(IsValid());
_ASSERT(pfaSizeAllocated);
TraceFunctEnterEx((LPARAM)this, "CBlockManager::GetAllocatedSize");
if (!pfaSizeAllocated)
hrRes = STG_E_INVALIDPARAMETER;
else
*pfaSizeAllocated = AtomicAdd(&m_faEndOfData, 0);
TraceFunctLeaveEx((LPARAM)this);
return(hrRes);
}
HRESULT CBlockManager::AllocateMemory(
DWORD dwSizeDesired,
FLAT_ADDRESS *pfaOffsetToAllocatedMemory,
DWORD *pdwSizeAllocated,
CBlockContext *pContext // Optional
)
{
HRESULT hrRes = S_OK;
_ASSERT(IsValid());
_ASSERT(pfaOffsetToAllocatedMemory);
_ASSERT(pdwSizeAllocated);
TraceFunctEnterEx((LPARAM)this, "CBlockManager::AllocateMemory");
hrRes = AllocateMemoryEx(
TRUE,
dwSizeDesired,
pfaOffsetToAllocatedMemory,
pdwSizeAllocated,
pContext);
TraceFunctLeaveEx((LPARAM)this);
return (hrRes);
}
HRESULT CBlockManager::AllocateMemoryEx(
BOOL fAcquireLock,
DWORD dwSizeDesired,
FLAT_ADDRESS *pfaOffsetToAllocatedMemory,
DWORD *pdwSizeAllocated,
CBlockContext *pContext // Optional
)
{
DWORD dwSize;
FLAT_ADDRESS faOffset;
FLAT_ADDRESS faStartOfBlock;
HEAP_NODE_ID idNode;
HEAP_NODE_ID idCurrentNode = 0;
HEAP_NODE_ID idLastNodeToCreate = 0;
HRESULT hrRes = S_OK;
BOOL fMarkStart = FALSE;
LPBLOCK_HEAP_NODE pNode = NULL;
_ASSERT(IsValid());
_ASSERT(pfaOffsetToAllocatedMemory);
_ASSERT(pdwSizeAllocated);
TraceFunctEnterEx((LPARAM)this, "CBlockManager::AllocateMemoryEx");
// First of all, we do an atomic reservation of the memory
// which allows multiple threads to concurrently call
// AllocateMemory
// DWORD-align the allocation
dwSizeDesired += BLOCK_DWORD_ALIGN_MASK;
dwSizeDesired &= ~(BLOCK_DWORD_ALIGN_MASK);
faStartOfBlock = AtomicAdd(&m_faEndOfData, dwSizeDesired);
// Fill this in first so if we succeed, we won't have to fill
// this in everywhere and if this fails, it's no big deal.
*pdwSizeAllocated = dwSizeDesired;
DebugTrace((LPARAM)this, "Allocating %u bytes", dwSizeDesired);
// OK, we have two scenarios.
// 1) The current block is large enough to honor the request
// 2) We need one or more extra blocks to accomodate the
// request.
idNode = GetNodeIdFromOffset(faStartOfBlock);
// Calculate all the required parameters
faOffset = faStartOfBlock & BLOCK_HEAP_PAYLOAD_MASK;
dwSize = BLOCK_HEAP_PAYLOAD - (DWORD)faOffset;
// Invalidate the context
if (pContext)
pContext->Invalidate();
if (idNode < m_idNodeCount)
{
// The starting node exists
hrRes = GetNodeFromNodeId(idNode, &pNode);
if (FAILED(hrRes)) {
TraceFunctLeave();
return hrRes;
}
_ASSERT(pNode);
#ifdef DEBUG_TRACK_ALLOCATION_BOUNDARIES
// Set the beginning of the allocation
SetAllocationBoundary(faStartOfBlock, pNode);
#endif
// Set the context here, most likely a write will follow immediately
if (pContext)
pContext->Set(pNode, pNode->stAttributes.faOffset);
if (dwSize >= dwSizeDesired)
{
// Scenario 1: enough space left
DebugTrace((LPARAM)this, "Allocated from existing node");
// Just fill in the output parameters
*pfaOffsetToAllocatedMemory = faStartOfBlock;
TraceFunctLeaveEx((LPARAM)this);
return(S_OK);
}
// Scenario 2a: More blocks needed, starting from the
// next block, see how many more we need
dwSizeDesired -= dwSize;
}
else
{
// Scenario 2b: More blocks needed.
// NOTE: This should be a rare code path except for
// high contention ...
// Now we have again 2 cases:
// 1) If our offset is in the middle of a block, then
// we know another thread is creating the current block
// and all we have to do is to wait for the block to be
// created, but create any subsequent blocks.
// 2) If we are exactly at the start of the block, then
// it is the responsibility of the current thread to
// create the block.
if (faOffset != 0)
{
// Scenario 1: We don't have to create the current block.
// so skip the current block
dwSizeDesired -= dwSize;
}
}
DebugTrace((LPARAM)this, "Creating new node");
// We must grab an exclusive lock before we go ahead and
// create any blocks
#ifndef BLOCKMGR_DISABLE_CONTENTION_CONTROL
if (fAcquireLock) WriteLock();
#endif
// At this point, we can do whatever we want with the node
// list and nodes. We will try to create all the missing
// nodes, whether or not it lies in our desired region or not.
//
// We need to do this because if an allocation failed before,
// we have missing nodes between the end of the allocated nodes
// and the current node we are allocating. Since these nodes
// contain links to the deeper nodes, we will break if we have
// missing nodes.
//
// This is necessary since this function is not serailzed
// elsewhere. So a thread entering later than another can
// grab the lock before the earlier thread. If we don't
// fill in the bubbles, the current thread will still have
// to wait for the earlier blocks to be created by the
// earlier thread. We would also have chaos if our allocations
// worked and the ones in front of us failed. This may be
// a bottleneck for all threads on this message, but once
// we're done this lock, they'll all unblock. Moreover, if
// we fail, they will all have to fail!
// Figure out how many blocks to create, up to the known limit
idLastNodeToCreate =
(m_faEndOfData + BLOCK_HEAP_PAYLOAD_MASK) >> BLOCK_HEAP_PAYLOAD_BITS;
// We know where the block starts, question is whether we're
// successful or not.
*pfaOffsetToAllocatedMemory = faStartOfBlock;
// The node count could have changed while we were waiting
// for the lock, so we have to refresh our records.
// Better yet, if another thread already created our blocks
// for us, we can just leave ...
idCurrentNode = m_idNodeCount;
if (idCurrentNode < idLastNodeToCreate)
{
LPBLOCK_HEAP_NODE pNewNode = NULL;
BOOL fSetContext = TRUE;
// No such luck, gotta go in and do the hard work ...
if (!pContext)
fSetContext = FALSE;
// Now, we have a function that inserts a node given
// the pervious node (not the parent), so we have to
// go find the previous node. This has got to be
// there unless our node list is messed up.
pNode = NULL;
if (idCurrentNode > 0)
{
// This is not the root, so we can find its prev.
hrRes = GetNodeFromNodeId(idCurrentNode - 1, &pNode, TRUE);
if (FAILED(hrRes)) {
#ifndef BLOCKMGR_DISABLE_CONTENTION_CONTROL
if (fAcquireLock) WriteUnlock();
#endif
TraceFunctLeave();
return hrRes;
}
_ASSERT(pNode);
_ASSERT(pNode->stAttributes.idNode == (idCurrentNode -1));
}
while (idCurrentNode < idLastNodeToCreate)
{
hrRes = m_bma.AllocBlock((LPVOID *)&pNewNode, sizeof(BLOCK_HEAP_NODE));
if (!SUCCEEDED(hrRes))
{
// We can't proceed, but what we've got is cool
DebugTrace((LPARAM)this,
"Failed to allocate node %u", idCurrentNode);
break;
}
DebugTrace((LPARAM)this, "Allocated node %u", idCurrentNode);
#ifdef DEBUG_TRACK_ALLOCATION_BOUNDARIES
// Need to do some work here
ZeroMemory(pNewNode->stAttributes.rgbBoundaries,
sizeof(pNewNode->stAttributes.rgbBoundaries));
// See if we have to mark the start of the
#endif
// Got the block, fill in the info and insert the block
// Again, we shouldn't fail if we get this far.
hrRes = InsertNodeGivenPreviousNode(pNewNode, pNode);
_ASSERT(SUCCEEDED(hrRes));
// Set the context value here if we need to note if the
// following condition is TRUE, we were in scenario 2b above.
if (idCurrentNode == idNode)
{
if (fSetContext)
{
// The context is actually the node that marks the
// start of the reserved block
// Note we only need to do this if we were in scenario
// 2b above.
pContext->Set(pNewNode, pNewNode->stAttributes.faOffset);
fSetContext = FALSE;
}
#ifdef DEBUG_TRACK_ALLOCATION_BOUNDARIES
// Set the beginning of the allocation
SetAllocationBoundary(faStartOfBlock, pNewNode);
#endif
}
// Next
pNode = pNewNode;
idCurrentNode++;
}
// Now update the counter to reflect what we've created.
m_idNodeCount = idCurrentNode;
}
#ifndef BLOCKMGR_DISABLE_CONTENTION_CONTROL
if (fAcquireLock) WriteUnlock();
#endif
TraceFunctLeaveEx((LPARAM)this);
return (hrRes);
}
BOOL CBlockManager::IsMemoryAllocated(
FLAT_ADDRESS faOffset,
DWORD dwLength
)
{
// Note we chack for actually allocated memory by checking
// m_idNodeCount, where m_faEndOfData includes data that is
// reserved but not yet allocated.
HEAP_NODE_ID idNode = GetNodeIdFromOffset(faOffset);
if (idNode < m_idNodeCount)
{
idNode = GetNodeIdFromOffset(faOffset + dwLength - 1);
if (idNode < m_idNodeCount)
return(TRUE);
_ASSERT(FALSE);
}
_ASSERT(FALSE);
return(FALSE);
}
HRESULT CBlockManager::OperateOnMemory(
DWORD dwOperation,
LPBYTE pbBuffer,
FLAT_ADDRESS faTargetOffset,
DWORD dwBytesToDo,
DWORD *pdwBytesDone,
CBlockContext *pContext // Optional
)
{
BOOL fUseContext = (pContext != NULL);
BOOL fBounddaryCheck = !(dwOperation & BOP_NO_BOUNDARY_CHECK);
BOOL fLockAcquired = (dwOperation & BOP_LOCK_ACQUIRED);
DWORD dwHopsAway = 0;
HRESULT hrRes = S_OK;
LPBLOCK_HEAP_NODE pNode = NULL;
_ASSERT(IsValid());
_ASSERT(pbBuffer);
_ASSERT(pdwBytesDone);
TraceFunctEnterEx((LPARAM)this, "CBlockManager::OperateOnMemory");
// Mask out the operation
dwOperation &= BOP_OPERATION_MASK;
if (fUseContext)
{
FLAT_ADDRESS faOffset = pContext->m_faLastAccessedNodeOffset;
// We will not continue if a bad context is passed in
if (!pContext->IsValid())
fUseContext = FALSE;
else
{
// More debug sanity checks
_ASSERT(pContext->m_pLastAccessedNode->stAttributes.faOffset
== faOffset);
// We will see if the context really helps
if (faOffset <= faTargetOffset)
{
// Let's see how many hops away
dwHopsAway = (DWORD)
((faTargetOffset - faOffset) >> BLOCK_HEAP_PAYLOAD_BITS);
// Not worth it if more than a number of hops away
if (dwHopsAway > BLOCK_MAX_ALLOWED_LINEAR_HOPS)
fUseContext = FALSE;
}
else
fUseContext = FALSE;
}
}
if (fUseContext)
{
DebugTrace((LPARAM) this, "using context");
// Quickly access the starting target node ...
pNode = pContext->m_pLastAccessedNode;
while (dwHopsAway--)
{
hrRes = GetNextNode(&pNode, fLockAcquired);
if (FAILED(hrRes))
{
fUseContext = FALSE;
break;
}
}
}
if (!fUseContext)
{
DebugTrace((LPARAM) this, "ignoring context");
// Okay, gotta find the desired node from scratch ...
hrRes = GetNodeFromNodeId( GetNodeIdFromOffset(faTargetOffset),
&pNode,
fLockAcquired);
if (!SUCCEEDED(hrRes))
{
ErrorTrace((LPARAM)this, "GetNodeIdFromOffset failed");
TraceFunctLeaveEx((LPARAM)this);
return(STG_E_INVALIDPARAMETER);
}
_ASSERT(pNode);
}
DebugTrace((LPARAM) this, "pNode = 0x%x", pNode);
_ASSERT(pNode != NULL);
if (!IsMemoryAllocated(faTargetOffset, dwBytesToDo))
{
ErrorTrace((LPARAM)this,
"Specified range is unallocated");
TraceFunctLeaveEx((LPARAM)this);
return(STG_E_INVALIDPARAMETER);
}
// Clear the counter ...
*pdwBytesDone = 0;
// Do the actual processing
switch (dwOperation)
{
case BOP_READ:
case BOP_WRITE:
{
DWORD dwChunkSize;
DWORD dwBytesDone = 0;
faTargetOffset &= BLOCK_HEAP_PAYLOAD_MASK;
dwChunkSize = (DWORD)(BLOCK_HEAP_PAYLOAD - faTargetOffset);
while (dwBytesToDo)
{
if (dwBytesToDo < dwChunkSize)
dwChunkSize = dwBytesToDo;
#ifdef DEBUG_TRACK_ALLOCATION_BOUNDARIES
if (fBounddaryCheck)
{
// Make sure we are not stepping over boundaries
hrRes = VerifyAllocationBoundary(faTargetOffset,
dwChunkSize,
pNode);
if (!SUCCEEDED(hrRes))
break;
}
#endif
if (dwOperation == BOP_READ)
{
DebugTrace((LPARAM)this,
"Reading %u bytes", dwChunkSize);
MoveMemory((LPVOID)pbBuffer,
(LPVOID)&(pNode->rgbData[faTargetOffset]),
dwChunkSize);
}
else
{
DebugTrace((LPARAM)this,
"Writing %u bytes", dwChunkSize);
MoveMemory((LPVOID)&(pNode->rgbData[faTargetOffset]),
(LPVOID)pbBuffer,
dwChunkSize);
// Set the block to dirty
pNode->stAttributes.fFlags |= BLOCK_IS_DIRTY;
SetDirty(TRUE);
}
// Adjust the read buffer for the next read/write
pbBuffer += dwChunkSize;
// Adjust the counters
dwBytesToDo -= dwChunkSize;
dwBytesDone += dwChunkSize;
// After the first operation, the offset will always
// be zero, and the default chunk size is a full payload
faTargetOffset = 0;
dwChunkSize = BLOCK_HEAP_PAYLOAD;
// Read next chunk
if (dwBytesToDo)
{
// See if we have to load this ...
hrRes = GetNextNode(&pNode, fLockAcquired);
if (FAILED(hrRes))
break;
}
}
// Fill out how much we've done
*pdwBytesDone = dwBytesDone;
}
break;
default:
ErrorTrace((LPARAM)this,
"Invalid operation %u", dwOperation);
hrRes = STG_E_INVALIDFUNCTION;
}
// Update context if succeeded
if (SUCCEEDED(hrRes) && pContext)
{
pContext->Set(pNode, pNode->stAttributes.faOffset);
}
TraceFunctLeaveEx((LPARAM)this);
return(hrRes);
}
HRESULT CBlockManager::ReadMemory(
LPBYTE pbBuffer,
FLAT_ADDRESS faTargetOffset,
DWORD dwBytesToRead,
DWORD *pdwBytesRead,
CBlockContext *pContext // Optional
)
{
return(OperateOnMemory(
BOP_READ,
pbBuffer,
faTargetOffset,
dwBytesToRead,
pdwBytesRead,
pContext));
}
HRESULT CBlockManager::WriteMemory(
LPBYTE pbBuffer,
FLAT_ADDRESS faTargetOffset,
DWORD dwBytesToWrite,
DWORD *pdwBytesWritten,
CBlockContext *pContext // Optional
)
{
return(OperateOnMemory(
BOP_WRITE,
pbBuffer,
faTargetOffset,
dwBytesToWrite,
pdwBytesWritten,
pContext));
}
HRESULT CBlockManager::ReleaseNode(
LPBLOCK_HEAP_NODE pNode
)
{
HRESULT hrRes = S_OK;
HRESULT tempRes;
// Release all children recursively
for (DWORD i = 0; i < BLOCK_HEAP_ORDER; i++)
if (pNode->rgpChildren[i])
{
tempRes = ReleaseNode(pNode->rgpChildren[i]);
if (FAILED(tempRes))
hrRes = tempRes;
pNode->rgpChildren[i] = NULL;
}
// Release self
m_bma.FreeBlock(pNode);
return(hrRes);
}
HRESULT CBlockManager::Release()
{
HRESULT hrRes = S_OK;
_ASSERT(IsValid());
TraceFunctEnterEx((LPARAM)this, "CBlockManager::Release");
// This function assumes that no more threads are using this
// class and no new threads are inside trying to reserve
// memory. Though, for good measure, this function still
// grabs a write lock so that at least it does not get
// corrupt when stray threads are still lingering around.
// Grab the lock before we go in and destroy the node list
#ifndef BLOCKMGR_DISABLE_CONTENTION_CONTROL
WriteLock();
#endif
if (m_pRootNode)
{
hrRes = ReleaseNode(m_pRootNode);
if (SUCCEEDED(hrRes))
m_pRootNode = NULL;
}
#ifndef BLOCKMGR_DISABLE_CONTENTION_CONTROL
WriteUnlock();
#endif
TraceFunctLeaveEx((LPARAM)this);
return (hrRes);
}
HRESULT CBlockManager::AtomicDereferenceAndRead(
LPBYTE pbBuffer,
DWORD *pdwBufferSize,
LPBYTE pbInfoStruct,
FLAT_ADDRESS faOffsetToInfoStruct,
DWORD dwSizeOfInfoStruct,
DWORD dwOffsetInInfoStructToOffset,
DWORD dwOffsetInInfoStructToSize,
CBlockContext *pContext // Optional
)
{
HRESULT hrRes = S_OK;
FLAT_ADDRESS faOffset;
DWORD dwSizeToRead;
DWORD dwSizeRead;
_ASSERT(IsValid());
_ASSERT(pbBuffer);
_ASSERT(pdwBufferSize);
_ASSERT(pbInfoStruct);
// pContext can be NULL
TraceFunctEnterEx((LPARAM)this,
"CBlockManager::AtomicDereferenceAndRead");
#ifndef BLOCKMGR_DISABLE_ATOMIC_FUNCS
// Acquire the synchronization object
WriteLock();
#endif
do
{
BOOL fInsufficient = FALSE;
DWORD dwBufferSize = *pdwBufferSize;
// Read the info struct
DebugTrace((LPARAM)this, "Reading information structure");
hrRes = OperateOnMemory(
BOP_READ | BOP_LOCK_ACQUIRED,
pbInfoStruct,
faOffsetToInfoStruct,
dwSizeOfInfoStruct,
&dwSizeRead,
pContext);
if (!SUCCEEDED(hrRes))
break;
// Fill out the parameters
faOffset = *(UNALIGNED FLAT_ADDRESS *)(pbInfoStruct + dwOffsetInInfoStructToOffset);
dwSizeToRead = *(UNALIGNED DWORD *)(pbInfoStruct + dwOffsetInInfoStructToSize);
DebugTrace((LPARAM)this, "Reading %u bytes from offset %u",
dwSizeToRead, (DWORD)faOffset);
// See if we have enough buffer
if (dwBufferSize < dwSizeToRead)
{
fInsufficient = TRUE;
DebugTrace((LPARAM)this,
"Insufficient buffer, only reading %u bytes",
dwBufferSize);
}
else
dwBufferSize = dwSizeToRead;
// Do the read
hrRes = OperateOnMemory(
BOP_READ | BOP_LOCK_ACQUIRED,
pbBuffer,
faOffset,
dwBufferSize,
&dwSizeRead,
pContext);
if (!SUCCEEDED(hrRes))
break;
*pdwBufferSize = dwSizeToRead;
// If we had insufficient buffer, we must return the
// correct HRESULT
if (fInsufficient)
hrRes = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
} while (0);
#ifndef BLOCKMGR_DISABLE_ATOMIC_FUNCS
WriteUnlock();
#endif
TraceFunctLeaveEx((LPARAM)this);
return (hrRes);
}
inline HRESULT CBlockManager::WriteAndIncrement(
LPBYTE pbBuffer,
FLAT_ADDRESS faOffset,
DWORD dwBytesToWrite,
DWORD *pdwValueToIncrement,
DWORD dwIncrementValue,
CBlockContext *pContext // Optional
)
{
HRESULT hrRes = S_OK;
DWORD dwSize;
_ASSERT(IsValid());
_ASSERT(pbBuffer);
// pdwValueToIncrement and pContext can be NULL
TraceFunctEnterEx((LPARAM)this, "CBlockManager::WriteAndIncrement");
// Very simple, this function assumes no contention since the caller
// is already supposed to be in some sort of atomic operation
hrRes = OperateOnMemory(
BOP_WRITE | BOP_LOCK_ACQUIRED,
pbBuffer,
faOffset,
dwBytesToWrite,
&dwSize,
pContext);
if (SUCCEEDED(hrRes))
{
// This must be true if the write succeeded, but then ...
_ASSERT(dwBytesToWrite == dwSize);
// The write is successful, then increment the value in
// an interlocked fashion. We do that such that simultaneous
// reads will be locked out properly. Simultaneous writes
// should be serialized by design but we do this for good
// measure in case the caller is not aware of this requirement.
if (pdwValueToIncrement)
AtomicAdd(pdwValueToIncrement, dwIncrementValue);
}
TraceFunctLeaveEx((LPARAM)this);
return (hrRes);
}
HRESULT CBlockManager::AtomicWriteAndIncrement(
LPBYTE pbBuffer,
FLAT_ADDRESS faOffset,
DWORD dwBytesToWrite,
DWORD *pdwValueToIncrement,
DWORD dwReferenceValue,
DWORD dwIncrementValue,
CBlockContext *pContext // Optional
)
{
HRESULT hrRes = S_OK;
_ASSERT(IsValid());
_ASSERT(pbBuffer);
// pdwValueToIncrement and pContext can be NULL
TraceFunctEnterEx((LPARAM)this,
"CBlockManager::AtomicWriteAndIncrement");
// Since acquiring the synchronization is potentially costly,
// we do a final sanity check to make sure no thread had
// beaten us in taking this slot
if (pdwValueToIncrement &&
*pdwValueToIncrement != dwReferenceValue)
{
DebugTrace((LPARAM)this, "Aborting due to change in property count");
TraceFunctLeaveEx((LPARAM)this);
return(HRESULT_FROM_WIN32(ERROR_RETRY));
}
#ifndef BLOCKMGR_DISABLE_ATOMIC_FUNCS
// This is a pass-thru call to the WriteAndIncrement but
// after acquiring the synchronization object
WriteLock();
#endif
// The wait for the lock could have been long, so we do a second
// check to see if we're out of luck after all this ...
if (pdwValueToIncrement &&
*pdwValueToIncrement != dwReferenceValue)
{
#ifndef BLOCKMGR_DISABLE_ATOMIC_FUNCS
// Gotta release it!
WriteUnlock();
#endif
DebugTrace((LPARAM)this, "Aborting after acquiring lock");
TraceFunctLeaveEx((LPARAM)this);
return(HRESULT_FROM_WIN32(ERROR_RETRY));
}
hrRes = WriteAndIncrement(
pbBuffer,
faOffset,
dwBytesToWrite,
pdwValueToIncrement,
dwIncrementValue,
pContext);
#ifndef BLOCKMGR_DISABLE_ATOMIC_FUNCS
WriteUnlock();
#endif
TraceFunctLeaveEx((LPARAM)this);
return (hrRes);
}
HRESULT CBlockManager::AtomicAllocWriteAndIncrement(
DWORD dwDesiredSize,
FLAT_ADDRESS *pfaOffsetToAllocatedMemory,
FLAT_ADDRESS faOffsetToWriteOffsetToAllocatedMemory,
FLAT_ADDRESS faOffsetToWriteSizeOfAllocatedMemory,
LPBYTE pbInitialValueForAllocatedMemory,
DWORD dwSizeOfInitialValue,
LPBYTE pbBufferToWriteFrom,
DWORD dwOffsetInAllocatedMemoryToWriteTo,
DWORD dwSizeofBuffer,
DWORD *pdwValueToIncrement,
DWORD dwReferenceValue,
DWORD dwIncrementValue,
CBlockContext *pContext // Optional
)
{
HRESULT hrRes = S_OK;
DWORD dwAllocatedSize;
DWORD dwSize;
_ASSERT(IsValid());
_ASSERT(pfaOffsetToAllocatedMemory);
_ASSERT(pbBufferToWriteFrom);
_ASSERT(pdwValueToIncrement);
// pContext can be NULL
TraceFunctEnterEx((LPARAM)this,
"CBlockManager::AtomicAllocWriteAndIncrement");
// Since acquiring the synchronization is potentially costly,
// we do a final sanity check to make sure no thread had
// beaten us in taking this slot
if (*pdwValueToIncrement != dwReferenceValue)
{
DebugTrace((LPARAM)this, "Aborting due to change in property count");
TraceFunctLeaveEx((LPARAM)this);
return(HRESULT_FROM_WIN32(ERROR_RETRY));
}
#ifndef BLOCKMGR_DISABLE_ATOMIC_FUNCS
// This is a pass-thru call to AllocateMemoryEx and
// WriteAndIncrement after acquiring the synchronization object
WriteLock();
#endif
// The wait for the lock could have been long, so we do a second
// check to see if we're out of luck after all this ...
if (*pdwValueToIncrement != dwReferenceValue)
{
#ifndef BLOCKMGR_DISABLE_ATOMIC_FUNCS
// Gotta release it!
WriteUnlock();
#endif
DebugTrace((LPARAM)this, "Aborting after acquiring lock");
TraceFunctLeaveEx((LPARAM)this);
return(HRESULT_FROM_WIN32(ERROR_RETRY));
}
// Try to allocate the requested block
hrRes = AllocateMemoryEx(
FALSE,
dwDesiredSize,
pfaOffsetToAllocatedMemory,
&dwAllocatedSize,
pContext);
if (SUCCEEDED(hrRes))
{
// Okay, initialize the memory allocated
if (pbInitialValueForAllocatedMemory)
{
hrRes = WriteMemory(
pbInitialValueForAllocatedMemory,
*pfaOffsetToAllocatedMemory,
dwSizeOfInitialValue,
&dwSize,
pContext);
// See if we need to write the size and offset info
if (SUCCEEDED(hrRes))
{
if (faOffsetToWriteOffsetToAllocatedMemory !=
INVALID_FLAT_ADDRESS)
hrRes = WriteMemory(
(LPBYTE)pfaOffsetToAllocatedMemory,
faOffsetToWriteOffsetToAllocatedMemory,
sizeof(FLAT_ADDRESS),
&dwSize,
pContext);
if (SUCCEEDED(hrRes) &&
faOffsetToWriteSizeOfAllocatedMemory !=
INVALID_FLAT_ADDRESS)
hrRes = WriteMemory(
(LPBYTE)&dwAllocatedSize,
faOffsetToWriteSizeOfAllocatedMemory,
sizeof(DWORD),
&dwSize,
pContext);
}
}
if (SUCCEEDED(hrRes))
{
// OK, since we got the memory, the write should not
// fail, but we check the result anyway.
hrRes = WriteAndIncrement(
pbBufferToWriteFrom,
*pfaOffsetToAllocatedMemory +
dwOffsetInAllocatedMemoryToWriteTo,
dwSizeofBuffer,
pdwValueToIncrement,
dwIncrementValue,
pContext);
}
}
#ifndef BLOCKMGR_DISABLE_ATOMIC_FUNCS
WriteUnlock();
#endif
TraceFunctLeaveEx((LPARAM)this);
return (hrRes);
}
HRESULT CBlockManager::MarkBlockAs(
LPBYTE pbData,
BOOL fClean
)
{
LPBLOCK_HEAP_NODE pNode;
TraceFunctEnterEx((LPARAM)this, "CBlockManager::MarkBlockAs");
// Find the attributes record from the data pointer
pNode = CONTAINING_RECORD(pbData, BLOCK_HEAP_NODE, rgbData);
_ASSERT(pNode);
_ASSERT(pNode->stAttributes.fFlags & BLOCK_PENDING_COMMIT);
// Cannot be pending and dirty
_ASSERT(!(pNode->stAttributes.fFlags & BLOCK_IS_DIRTY));
// Undo the dirty bit and mark as pending
pNode->stAttributes.fFlags &= ~(BLOCK_PENDING_COMMIT);
if (!fClean) {
pNode->stAttributes.fFlags |= BLOCK_IS_DIRTY;
SetDirty(TRUE);
}
TraceFunctLeaveEx((LPARAM)this);
return(S_OK);
}
HRESULT CBlockManager::CommitDirtyBlocks(
FLAT_ADDRESS faStartingOffset,
FLAT_ADDRESS faLengthToScan,
DWORD dwFlags,
IMailMsgPropertyStream *pStream,
BOOL fDontMarkAsCommit,
BOOL fComputeBlockCountsOnly,
DWORD *pcBlocksToWrite,
DWORD *pcTotalBytesToWrite,
IMailMsgNotify *pNotify
)
{
HRESULT hrRes = S_OK;
HEAP_NODE_ID idNode;
LPBLOCK_HEAP_NODE pNode;
DWORD dwBlocksToScan;
BOOL fLimitedLength;
DWORD dwCount = 0;
DWORD rgdwOffset[CMAILMSG_COMMIT_PAGE_BLOCK_SIZE];
DWORD rgdwSize[CMAILMSG_COMMIT_PAGE_BLOCK_SIZE];
LPBYTE rgpData[CMAILMSG_COMMIT_PAGE_BLOCK_SIZE];
DWORD *pdwOffset;
DWORD *pdwSize;
LPBYTE *ppbData;
_ASSERT(pStream);
TraceFunctEnterEx((LPARAM)this, "CBlockManager::CommitDirtyBlocks");
fLimitedLength = FALSE;
pNode = NULL;
if (faStartingOffset != INVALID_FLAT_ADDRESS)
{
idNode = GetNodeIdFromOffset(faStartingOffset);
if (idNode >= m_idNodeCount)
{
hrRes = STG_E_INVALIDPARAMETER;
goto Cleanup;
}
hrRes = GetNodeFromNodeId(idNode, &pNode);
if (!SUCCEEDED(hrRes))
goto Cleanup;
if (faLengthToScan != INVALID_FLAT_ADDRESS)
{
// See how many blocks to scan, rounding up
faLengthToScan += (faStartingOffset & BLOCK_HEAP_PAYLOAD_MASK);
faLengthToScan += BLOCK_HEAP_PAYLOAD_MASK;
dwBlocksToScan = (DWORD)(faLengthToScan >> BLOCK_HEAP_PAYLOAD_BITS);
fLimitedLength = TRUE;
}
else
dwBlocksToScan = 0;
}
else
{
hrRes = STG_E_INVALIDPARAMETER;
goto Cleanup;
}
// Loop until we fill up the array or have no more blocks
dwCount = 0;
pdwOffset = rgdwOffset;
pdwSize = rgdwSize;
ppbData = rgpData;
while (pNode)
{
if (fLimitedLength && !dwBlocksToScan--)
break;
if ((dwFlags & MAILMSG_GETPROPS_COMPLETE) ||
(pNode->stAttributes.fFlags & BLOCK_IS_DIRTY))
{
// Make sure we are not full ...
if (dwCount == CMAILMSG_COMMIT_PAGE_BLOCK_SIZE)
{
*pcBlocksToWrite += dwCount;
if (!fComputeBlockCountsOnly) {
// We are full, then write out the blocks
hrRes = pStream->WriteBlocks(
m_pMsg,
dwCount,
rgdwOffset,
rgdwSize,
rgpData,
pNotify);
if (!SUCCEEDED(hrRes))
break;
if (!fDontMarkAsCommit) {
// Go back and mark all blocks as clean
ppbData = rgpData;
while (--dwCount)
MarkBlockAs(*ppbData++, TRUE);
}
}
dwCount = 0;
// Reset our pointers and go on
pdwOffset = rgdwOffset;
pdwSize = rgdwSize;
ppbData = rgpData;
}
if (!fComputeBlockCountsOnly && !fDontMarkAsCommit) {
// Undo the dirty bit and mark as pending
pNode->stAttributes.fFlags &= BLOCK_CLEAN_MASK;
pNode->stAttributes.fFlags |= BLOCK_PENDING_COMMIT;
}
// Fill in the array elements
// faOffset really contains an offset not a full pointer here so we
// can (and must) cast it for the calls to WriteBlocks to be OK
*pdwOffset++ = (DWORD)pNode->stAttributes.faOffset;
*pdwSize++ = BLOCK_HEAP_PAYLOAD;
*ppbData++ = pNode->rgbData;
*pcTotalBytesToWrite += BLOCK_HEAP_PAYLOAD;
dwCount++;
}
// Next node, pNode == NULL if no more nodes
hrRes = GetNextNode(&pNode, FALSE);
if (hrRes == STG_E_INVALIDPARAMETER) hrRes = S_OK;
DebugTrace((LPARAM) this, "hrRes = %x", hrRes);
}
if (SUCCEEDED(hrRes) && dwCount)
{
*pcBlocksToWrite += dwCount;
if (!fComputeBlockCountsOnly) {
// Write out the remaining blocks
hrRes = pStream->WriteBlocks(
m_pMsg,
dwCount,
rgdwOffset,
rgdwSize,
rgpData,
pNotify);
}
}
if (FAILED(hrRes)) SetCommitMode(FALSE);
if (!fComputeBlockCountsOnly && !fDontMarkAsCommit && dwCount) {
// Go back and mark all blocks to the correct state
ppbData = rgpData;
while (--dwCount)
MarkBlockAs(*ppbData++, SUCCEEDED(hrRes));
}
Cleanup:
TraceFunctLeaveEx((LPARAM)this);
return(hrRes);
}