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.

1051 lines
35 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. blockmgr.h
  5. Abstract:
  6. This module contains the definition of the block memory manager
  7. Author:
  8. Keith Lau (keithlau@microsoft.com)
  9. Revision History:
  10. keithlau 02/27/98 created
  11. --*/
  12. #ifndef __BLOCKMGR_H__
  13. #define __BLOCKMGR_H__
  14. #include "rwnew.h"
  15. #include "cpoolmac.h"
  16. #include "mailmsg.h"
  17. //
  18. // Nasty forwards for these interfaces ...
  19. //
  20. struct IMailMsgPropertyStream;
  21. struct IMailMsgNotify;
  22. /***************************************************************************/
  23. // Define this to remove contention control
  24. //
  25. // #define BLOCKMGR_DISABLE_ATOMIC_FUNCS
  26. // #define BLOCKMGR_DISABLE_CONTENTION_CONTROL
  27. //
  28. #ifdef BLOCKMGR_DISABLE_CONTENTION_CONTROL
  29. #define BLOCKMGR_DISABLE_ATOMIC_FUNCS
  30. #endif
  31. /***************************************************************************/
  32. // Debug stuff ...
  33. //
  34. #ifdef DEBUG
  35. #define DEBUG_TRACK_ALLOCATION_BOUNDARIES
  36. #endif
  37. /***************************************************************************/
  38. // CBlockManager - Implementation of pseudo flat memory space using a
  39. // heap that works like an I-Node. The underlying memory utilizes
  40. // disjoint, fixed-size memory blocks.
  41. //
  42. // Each node is as follows:
  43. //
  44. // +---------------------------------------------------------+
  45. // | Pointers to other nodes | Space for arbitrary data |
  46. // +---------------------------------------------------------+
  47. //
  48. // Analysis:
  49. // We assume in some way or another memory allocation is based on
  50. // 4K pages or some multiple thereof. The first thing we want to
  51. // determine is how many pointers to other nodes we want to have in
  52. // the node (order of the heap). We know that each node would probably
  53. // be some size between 1K to 2K so that we won't waste space in the
  54. // average case of small email messages, yet provide scalability for
  55. // huge email messages that potentially may have millions of email
  56. // addresses. 2 intuitive candidates are 32 and 64 pointers.
  57. //
  58. // We consider the worst case scenario of MSN, which has about 2.5
  59. // million users. Assuming the averace recipient record is about
  60. // 45 bytes (name & attributes, etc.), then we need 112.5M of
  61. // storage, which is about 2 ^ 27. Assume the average data payload
  62. // is 1K per node (2 ^ 10), then we need 2 ^ 17 nodes. Thus, for
  63. // 23 pointers (2 ^ 5) per node, we need 4 layers to cover the
  64. // required address space of 112M. However, this turns out to be
  65. // an overkill since 4 layers covers 1G (2 ^ 20) where we only need
  66. // about 10% of that. As for the 64 pointers case, we only need 3
  67. // layers to cover 256M (2 ^ (18 + 10)), which roughly covers 5
  68. // million users. We will choose 64 pointer per node (256 bytes).
  69. //
  70. // As for the size of the payload, I considered using 1K allocations
  71. // and using the remaining 768 bytes as data payload. But since this
  72. // is not a round power of two, it would be expensive to do a
  73. // div and mod operation. As an alternative, I suggest allocating
  74. // 1024 + 256 byte blocks. This makes both numbers round powers of
  75. // two, which makes div and mod operations simple AND and SHR
  76. // operations, which typically take 2-4 cycles to complete. Also,
  77. // when the wasted space is considered, turns out that a 4K page
  78. // fits 3 such blocks, and only 256 bytes is wasted per page. This
  79. // comes to 93.3% utilization of space.
  80. //
  81. // So each node would look like this:
  82. // +---------------------------------------------------------+
  83. // | 64 pointers = 256 bytes | 1K block for arbitrary data |
  84. // +---------------------------------------------------------+
  85. //
  86. // It is an explicit objective that a typical mail message header
  87. // fits in a single block. Each block fans out to 64 other blocks
  88. // and each block's payload maps to 1K of the flat data address
  89. // space. The root node maps to the first 1K of the data space
  90. // (i.e. absolute address 0 to 1023 in data space), then each
  91. // of the next 64 nodes in the next layer represents the next 64K,
  92. // respectively, and so on for each subsequent layer. Nodes for
  93. // the next layer is not created until the current layer is
  94. // depleted. Collpasing of nodes is not required due to the fact
  95. // that the heap can only grow.
  96. //
  97. // During commit of the whole heap, the scatter-gather list is
  98. // built by traversing the entire heap. The average number of
  99. // dereferences is n*(log64(n))/2).
  100. //
  101. // All items in a message object is allocated off this heap.
  102. //
  103. // An slight modification can be used to track dirty or unused
  104. // bits. We can actually add a block of flags and attributes to
  105. // each node to track dirty regions and other flags. This will
  106. // probably not be implemented in the initial implementation,
  107. // but such capability will be factored in. In terms of allocation
  108. // optimization, we can have a block of up to 64 bytes without
  109. // disrupting the 4K page allocation scheme. In fact, adding a
  110. // 64-byte block to each node boosts memory utilization to up
  111. // to 98.4% without any real extra cost while still keeping each
  112. // node 64-byte aligned.
  113. //
  114. // Synchronization:
  115. // Allocation of memory in the data space is done through a
  116. // reservation model where multiple threads can concurrently
  117. // reserve memory and be guaranteed to get a unique block.
  118. // A lightweight critical section is used to synchronize block
  119. // creation should the reservation span into blocks that are
  120. // not yet allocated. Allocation of new blocks is serialized.
  121. //
  122. // Synchronization for concurrent access to the same data space
  123. // must be enforced at a higher level, if desired.
  124. //
  125. // Define the constants chosen for this implementation
  126. #ifdef _WIN64
  127. // The order will be 5 bits in 64-bit (8 * 32 = 256 bytes)
  128. #define BLOCK_HEAP_ORDER_BITS (5)
  129. #else
  130. // The order will be 6 bits in 32-bit (4 * 64 = 256 bytes)
  131. #define BLOCK_HEAP_ORDER_BITS (6)
  132. #endif
  133. #define BLOCK_HEAP_ORDER (1 << BLOCK_HEAP_ORDER_BITS)
  134. #define BLOCK_HEAP_ORDER_MASK (BLOCK_HEAP_ORDER - 1)
  135. #define BLOCK_HEAP_PAYLOAD_BITS (10)
  136. #define BLOCK_HEAP_PAYLOAD (1 << BLOCK_HEAP_PAYLOAD_BITS)
  137. #define BLOCK_HEAP_PAYLOAD_MASK (BLOCK_HEAP_PAYLOAD - 1)
  138. #define BLOCK_DWORD_ALIGN_MASK (sizeof(DWORD) - 1)
  139. #define BLOCK_DEFAULT_FLAGS (BLOCK_IS_DIRTY)
  140. #define BLOCK_MAX_ALLOWED_LINEAR_HOPS 3
  141. // Define the underlying data type for a flat address in the
  142. // linear address space, and the type that we use to count nodes.
  143. // This is for scalability so when we want to use 64-bit
  144. // quantities, we can simply replace this section of data-size
  145. // specific values
  146. //
  147. // Note: you need to make sure that the data size is AT LEAST:
  148. // 1 + (BLOCK_HEAP_ORDER_BITS * MAX_HEAP_DEPTH) + BLOCK_HEAP_PAYLOAD_BITS
  149. //
  150. // Note: In order for this type to be used as the base address
  151. // type, the following operations must be supported:
  152. // - Assignment
  153. // - Comparison
  154. // - Arithmetic operators
  155. // - Bitwise operators
  156. // - Interlocked operations
  157. //
  158. // Start data-size-specific values
  159. typedef SIZE_T HEAP_BASE_ADDRESS_TYPE;
  160. typedef HEAP_BASE_ADDRESS_TYPE HEAP_NODE_ID;
  161. typedef HEAP_NODE_ID *LPHEAP_NODE_ID;
  162. typedef HEAP_BASE_ADDRESS_TYPE FLAT_ADDRESS;
  163. typedef FLAT_ADDRESS *LPFLAT_ADDRESS;
  164. // These must be changed if HEAP_BASE_ADDRESS_TYPE is not DWORD
  165. #define NODE_ID_MAPPING_FACTOR \
  166. (HEAP_BASE_ADDRESS_TYPE)( \
  167. 1 | \
  168. (1 << BLOCK_HEAP_ORDER_BITS) | \
  169. (1 << (BLOCK_HEAP_ORDER_BITS * 2)) \
  170. )
  171. // And so on, etc ...
  172. // (1 << (BLOCK_HEAP_ORDER_BITS * 3))
  173. // (1 << (BLOCK_HEAP_ORDER_BITS * 4))
  174. #define NODE_ID_ABSOLUTE_MAX \
  175. (HEAP_BASE_ADDRESS_TYPE)( \
  176. (1 << BLOCK_HEAP_ORDER_BITS) | \
  177. (1 << (BLOCK_HEAP_ORDER_BITS * 2)) |\
  178. (1 << (BLOCK_HEAP_ORDER_BITS * 3)) \
  179. )
  180. // And so on, etc ...
  181. // (1 << (BLOCK_HEAP_ORDER_BITS * 4))
  182. // (1 << (BLOCK_HEAP_ORDER_BITS * 5))
  183. #define NODE_ID_BORROW_BIT \
  184. (HEAP_BASE_ADDRESS_TYPE)(1 << (BLOCK_HEAP_ORDER_BITS * 3))
  185. // Depth of heap allowed by base data type
  186. #define MAX_HEAP_DEPTH 4
  187. // Node Id space mask
  188. #define MAX_FLAT_ADDRESS \
  189. (FLAT_ADDRESS)((1 << (MAX_HEAP_DEPTH * BLOCK_HEAP_ORDER_BITS)) - 1)
  190. // Same as a NULL pointer
  191. #define INVALID_FLAT_ADDRESS ((FLAT_ADDRESS)-1)
  192. // Number of bits to rotate the mapped result
  193. #define NODE_ID_ROR_FACTOR ((MAX_HEAP_DEPTH - 1) * BLOCK_HEAP_ORDER_BITS)
  194. // Define the rotate functions
  195. #define ROTATE_LEFT(v, n) _lrotl((v), (n))
  196. #define ROTATE_RIGHT(v, n) _lrotr((v), (n))
  197. // Define the interlocked functions
  198. #define AtomicAdd(pv, a) \
  199. (HEAP_BASE_ADDRESS_TYPE)InterlockedExchangeAdd((long *)(pv), (a))
  200. // End data-size-specific values
  201. // Forward declaration of the _BLOCK_HEAP_NODE structure
  202. struct _BLOCK_HEAP_NODE;
  203. // Define the attribute block for each node
  204. typedef struct _BLOCK_HEAP_NODE_ATTRIBUTES
  205. {
  206. struct _BLOCK_HEAP_NODE *pParentNode; // Pointer to parent node
  207. HEAP_NODE_ID idChildNode; // Which child am I?
  208. HEAP_NODE_ID idNode; // Id of node in block heap
  209. FLAT_ADDRESS faOffset; // Starting offset the node
  210. DWORD fFlags; // Attributes of the block
  211. #ifdef DEBUG_TRACK_ALLOCATION_BOUNDARIES
  212. // This tracks the allocation boundaries between memory
  213. // allocations so that we can check whether a read or write
  214. // crosses an allocation boundary. We use a bit to represent
  215. // the start of a block. Since the allocations are DWORD-aligned,
  216. // we need BLOCK_HEAP_PAYLOAD >> 2 >> 3 bits to track
  217. // all allocation boundaries per block.
  218. BYTE rgbBoundaries[BLOCK_HEAP_PAYLOAD >> 5];
  219. #endif
  220. } BLOCK_HEAP_NODE_ATTRIBUTES, *LPBLOCK_HEAP_NODE_ATTRIBUTES;
  221. // Define each node in the heap
  222. typedef struct _BLOCK_HEAP_NODE
  223. {
  224. struct _BLOCK_HEAP_NODE *rgpChildren[BLOCK_HEAP_ORDER];
  225. BLOCK_HEAP_NODE_ATTRIBUTES stAttributes;
  226. BYTE rgbData[BLOCK_HEAP_PAYLOAD];
  227. } BLOCK_HEAP_NODE, *LPBLOCK_HEAP_NODE;
  228. #define BLOCK_HEAP_NODE_SIZE (sizeof(BLOCK_HEAP_NODE))
  229. #define BOP_LOCK_ACQUIRED 0x80000000
  230. #define BOP_NO_BOUNDARY_CHECK 0x40000000
  231. #define BOP_OPERATION_MASK 0x0000ffff
  232. typedef enum _BLOCK_OPERATION_CODES
  233. {
  234. BOP_READ = 0,
  235. BOP_WRITE
  236. } BLOCK_OPERATION_CODES;
  237. // Define the block attribute flags
  238. #define BLOCK_IS_DIRTY 0x00000001
  239. #define BLOCK_PENDING_COMMIT 0x00000002
  240. // block allocation flags
  241. // the block was allocated with CMemoryAccess instead of cpool
  242. #define BLOCK_NOT_CPOOLED 0x00010000
  243. #define BLOCK_CLEAN_MASK (~(BLOCK_IS_DIRTY))
  244. #define RESET_BLOCK_FLAGS(_flags_) _flags_ &= 0xffff0000
  245. #define DEFAULT_BLOCK_FLAGS(_flags_) _flags_ &= (0xffff0000 | BLOCK_IS_DIRTY)
  246. //
  247. // Define a method signature for acquiring a stream pointer
  248. //
  249. typedef IMailMsgPropertyStream *(*PFN_STREAM_ACCESSOR)(LPVOID);
  250. /***************************************************************************/
  251. // Context class for memory access
  252. //
  253. class CBlockContext
  254. {
  255. private:
  256. DWORD m_dwSignature;
  257. public:
  258. CBlockContext() { Invalidate(); }
  259. ~CBlockContext() { Invalidate(); }
  260. BOOL IsValid();
  261. void Set(
  262. LPBLOCK_HEAP_NODE pLastAccessedNode,
  263. FLAT_ADDRESS faLastAccessedNodeOffset
  264. );
  265. void Invalidate();
  266. LPBLOCK_HEAP_NODE m_pLastAccessedNode;
  267. FLAT_ADDRESS m_faLastAccessedNodeOffset;
  268. };
  269. /***************************************************************************/
  270. // Memory allocator classes
  271. //
  272. class CBlockMemoryAccess
  273. {
  274. public:
  275. CBlockMemoryAccess() {}
  276. ~CBlockMemoryAccess() {}
  277. HRESULT AllocBlock(
  278. LPVOID *ppvBlock,
  279. DWORD dwBlockSize
  280. );
  281. HRESULT FreeBlock(
  282. LPVOID pvBlock
  283. );
  284. //
  285. // CPool
  286. //
  287. static CPool m_Pool;
  288. };
  289. class CMemoryAccess
  290. {
  291. public:
  292. CMemoryAccess() {}
  293. ~CMemoryAccess() {}
  294. static HRESULT AllocBlock(
  295. LPVOID *ppvBlock,
  296. DWORD dwBlockSize
  297. );
  298. static HRESULT FreeBlock(
  299. LPVOID pvBlock
  300. );
  301. };
  302. /***************************************************************************/
  303. // Class for accessing stream
  304. //
  305. class CBlockManagerGetStream
  306. {
  307. public:
  308. virtual HRESULT GetStream(
  309. IMailMsgPropertyStream **ppStream,
  310. BOOL fLockAcquired
  311. ) = 0;
  312. };
  313. /***************************************************************************/
  314. // Block heap manager
  315. //
  316. class CBlockManager
  317. {
  318. public:
  319. CBlockManager(
  320. IMailMsgProperties *pMsg,
  321. CBlockManagerGetStream *pParent = NULL
  322. );
  323. ~CBlockManager();
  324. // Sanity check
  325. BOOL IsValid();
  326. // This initializes an empty MailMsg to a certain size.
  327. // CAUTION: This should only be used to initialize an empty MailMsg
  328. // when binding to a non-empty stream. Any other uses will cause
  329. // unpredictable results and/or corruption or even crashes.
  330. HRESULT SetStreamSize(
  331. DWORD dwStreamSize
  332. );
  333. //
  334. // Synopsis:
  335. // Allocate the desired amount of memory.
  336. // Thread safe.
  337. //
  338. // Arguments:
  339. // dwSizeDesired - the size of the block desired
  340. // pfaOffsetToReservedMemory - returns the offset to the
  341. // reserved block of memory, if successful, in the
  342. // flat memory space managed by the block manager.
  343. // pdwSizeAllocated - returns the actual size allocated, which
  344. // is greater than or equal to the desired size, if successful.
  345. // pContext (Optional) - fills in a context that describes
  346. // the reserved block. This context can be used in
  347. // subsequent reads and writes to the block. Accesses
  348. // using this context are faster than using the
  349. // offset alone. Ignored if NULL. The caller must allocate
  350. // the context structure prior to calling ReserveMemory.
  351. //
  352. // Return values:
  353. // S_OK - Success, the memory of requested size is
  354. // successfully reserved.
  355. // STG_E_INSUFFICIENTMEMORY - Error, the required amount of memory
  356. // is not available to honor the request.
  357. // STG_E_INVALIDPARAMETER - Internal error, mostly used
  358. // for debug considerations.
  359. //
  360. HRESULT AllocateMemory(
  361. DWORD dwSizeDesired,
  362. FLAT_ADDRESS *pfaOffsetToAllocatedMemory,
  363. DWORD *pdwSizeAllocated,
  364. CBlockContext *pContext // Optional
  365. );
  366. //
  367. // Synopsis:
  368. // Returns the total size allocated by this block manager.
  369. // Thread safe.
  370. //
  371. // Arguments:
  372. // pfaSizeAllocated - returns the total size allocated.
  373. //
  374. // Return values:
  375. // S_OK - Success, the memory of requested size is
  376. // successfully reserved.
  377. // STG_E_INVALIDPARAMETER - Internal error, mostly used
  378. // for debug considerations.
  379. //
  380. HRESULT GetAllocatedSize(
  381. FLAT_ADDRESS *pfaSizeAllocated
  382. );
  383. //
  384. // Synopsis:
  385. // Reads a chunk of contiguous memory in flat address space into a
  386. // user-supplied buffer. Synchronization not supported at this level.
  387. //
  388. // Arguments:
  389. // pbBuffer - buffer to return contents read, must be large enough
  390. // to store the data read.
  391. // faTargetOffset - offset measured in flat address space to start
  392. // reading from.
  393. // dwBytesToRead - number of contiguous bytes to read
  394. // pdwBytesRead - returns number of bytes actually read
  395. // pContext (Optional) - if specified, uses an alternate optimized
  396. // algorithm to access the memory, otherwise, the system looks
  397. // up the node in question using a full lookup, which is slower.
  398. // The system decides which algorithm to use based on some heuristics.
  399. //
  400. // Return values:
  401. // S_OK - Success, the read is successful.
  402. // STG_E_INVALIDPARAMETER - Error, one or more parameters are invalid, or
  403. // otherwise inconsistent.
  404. // STG_E_READFAULT - Error, The read failed to complete, pdwBytesRead
  405. // reflects the actual number of bytes read into pbBuffer.
  406. // TYPE_E_OUTOFBOUNDS - Debug Error, a read is issued to read past
  407. // the current allocated block.
  408. //
  409. HRESULT ReadMemory(
  410. LPBYTE pbBuffer,
  411. FLAT_ADDRESS faTargetOffset,
  412. DWORD dwBytesToRead,
  413. DWORD *pdwBytesRead,
  414. CBlockContext *pContext // Optional
  415. );
  416. //
  417. // Synopsis:
  418. // Writes a chunk of contiguous memory from a specified buffer into
  419. // a specified offset in the flat address space. Synchronization not
  420. // supported at this level.
  421. //
  422. // Arguments:
  423. // pbBuffer - source buffer of bytes to be written
  424. // faTargetOffset - offset measured in flat address space to start
  425. // writing to.
  426. // dwBytesToWrite - number of contiguous bytes to write
  427. // pdwBytesWritten - returns number of bytes actually written
  428. // pContext (Optional) - if specified, uses an alternate optimized
  429. // algorithm to access the memory, otherwise, the system looks
  430. // up the node in question using a full lookup, which is slower.
  431. // The system decides which algorithm to use based on some heuristics.
  432. //
  433. // Return values:
  434. // S_OK - Success, the read is successful.
  435. // STG_E_INVALIDPARAMETER - Error, one or more parameters are invalid, or
  436. // otherwise inconsistent.
  437. // STG_E_WRITEFAULT - Error, The read failed to complete, pdwBytesRead
  438. // reflects the actual number of bytes read into pbBuffer.
  439. // TYPE_E_OUTOFBOUNDS - Debug Error, a write is issued to write past
  440. // the current allocated block.
  441. //
  442. HRESULT WriteMemory(
  443. LPBYTE pbBuffer,
  444. FLAT_ADDRESS faTargetOffset,
  445. DWORD dwBytesToWrite,
  446. DWORD *pdwBytesWritten,
  447. CBlockContext *pContext // Optional
  448. );
  449. //
  450. // Synopsis:
  451. // Copies a chunk of contiguous memory of a specified size from a specified
  452. // starting offset into the same starting offset of the target address space
  453. // managed by the target block manager. Synchronization is not supported at this
  454. // level.
  455. //
  456. // Note that a copy is special in that it can cross allocation boundaries.
  457. //
  458. // Arguments:
  459. // faOffset - offset measured in flat address space to start copying.
  460. // dwBytesToCopy - number of contiguous bytes to copy.
  461. // pTargetBlockManager - Target block manager whose address space to
  462. // copy into.
  463. //
  464. // Return values:
  465. // S_OK - Success, the copy is successful.
  466. // STG_E_INVALIDPARAMETER - Error, one or more parameters are invalid, or
  467. // otherwise inconsistent.
  468. // STG_E_READFAULT - Error, a read failed to complete, the copy is not
  469. // comleted.
  470. // STG_E_WRITEFAULT - Error, a write failed to complete, the copy is not
  471. // comleted.
  472. //
  473. HRESULT CopyTo(
  474. FLAT_ADDRESS faOffset,
  475. DWORD dwBytesToCopy,
  476. CBlockManager *pTargetBlockManager,
  477. BOOL fLockAcquired = FALSE
  478. );
  479. //
  480. // Synopsis:
  481. // Atomically reads the length and size of a data block, and loads the
  482. // data block from the offset of the size specified.
  483. //
  484. // Arguments:
  485. // pbBuffer - target buffer of bytes to write the read data
  486. // pdwBufferSize - Contains the length of the supplied buffer going in,
  487. // and returns the length of data actually read.
  488. // pbInfoStruct - Structure containing the information structure
  489. // faOffsetToInfoStruct - Offset to the info structure
  490. // dwSizeOfInfoStruct - Size of the info struct to load
  491. // dwOffsetInInfoStructToOffset - Offset to the address of the data block.
  492. // this is measured w.r.t. the info structure
  493. // dwOffsetInInfoStructToOffset - Offset to the size of the data block.
  494. // this is measured w.r.t. the info structure
  495. // pContext (Optional) - if specified, uses an alternate optimized
  496. // algorithm to access the memory, otherwise, the system looks
  497. // up the node in question using a full lookup, which is slower.
  498. // The system decides which algorithm to use based on some heuristics.
  499. //
  500. // Return values:
  501. // S_OK - Success, the read is successful.
  502. // STG_E_INVALIDPARAMETER - Error, one or more parameters are invalid, or
  503. // otherwise inconsistent.
  504. // HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) - Error/Informational,
  505. // the supplied buffer is not large enough to hold all the data.
  506. // *pdwBufferSize returns the actual number of bytes read.
  507. // STG_E_READFAULT - Error, The read failed to complete.
  508. // TYPE_E_OUTOFBOUNDS - Debug Error, a read is issued to read past
  509. // the current allocated block.
  510. //
  511. HRESULT AtomicDereferenceAndRead(
  512. LPBYTE pbBuffer,
  513. DWORD *pdwBufferSize,
  514. LPBYTE pbInfoStruct,
  515. FLAT_ADDRESS faOffsetToInfoStruct,
  516. DWORD dwSizeOfInfoStruct,
  517. DWORD dwOffsetInInfoStructToOffset,
  518. DWORD dwOffsetInInfoStructToSize,
  519. CBlockContext *pContext // Optional
  520. );
  521. //
  522. // Synopsis:
  523. // Atomically writes the contents of a buffer to memory in flat space and
  524. // increments a DWORD value by a specified amount. The write is attempted
  525. // first, and if it succeeds, the value is incremented. If the write fails
  526. // for some reason, the value will not be incremented. This is to ensure that
  527. // all the data is written before the increment so the data "exists" by the
  528. // time the counter is updated.
  529. //
  530. // Arguments:
  531. // pbBuffer - source buffer of bytes to be written
  532. // faOffset - offset measured in flat address space to start
  533. // writing to.
  534. // dwBytesToWrite - number of contiguous bytes to write
  535. // pdwValueToIncrement - Pointer to the value to be atomically incremented
  536. // after the write successfully written. If this value is NULL, the
  537. // increment is ignored and only a protected write is performed.
  538. // dwReferenceValue - If the value in pdwValueToIncrement differs from this
  539. // value, the call will be aborted.
  540. // dwIncrementValue - Amount to increment pdwValueToIncrement.
  541. // pContext (Optional) - if specified, uses an alternate optimized
  542. // algorithm to access the memory, otherwise, the system looks
  543. // up the node in question using a full lookup, which is slower.
  544. // The system decides which algorithm to use based on some heuristics.
  545. //
  546. // Return values:
  547. // S_OK - Success, the write is successful.
  548. // HRESULT_FROM_WIN32(ERROR_RETRY) - Informational, The reference value
  549. // changed during processing and the call cannot complete. A retry
  550. // should be performed immediately.
  551. // STG_E_INVALIDPARAMETER - Error, one or more parameters are invalid, or
  552. // otherwise inconsistent.
  553. // STG_E_WRITEFAULT - Error, The write failed to complete.
  554. // TYPE_E_OUTOFBOUNDS - Debug Error, a write is issued to write past
  555. // the current allocated block.
  556. //
  557. HRESULT AtomicWriteAndIncrement(
  558. LPBYTE pbBuffer,
  559. FLAT_ADDRESS faOffset,
  560. DWORD dwBytesToWrite,
  561. DWORD *pdwValueToIncrement,
  562. DWORD dwReferenceValue,
  563. DWORD dwIncrementValue,
  564. CBlockContext *pContext // Optional
  565. );
  566. //
  567. // Synopsis:
  568. // Atomically allocates memory, writes the contents of a buffer to memory
  569. // in flat space and increments a DWORD value by a specified amount. The
  570. // allocation is preceeded by a synchronization object and the allocation
  571. // takes place only if the value of the value to increment is identical before
  572. // and after the synchronization object is acquired. This allows multiple threads
  573. // to call this function for the same base object and only one such allocation
  574. // will succeed. The user can specify a buffer containing content data that will
  575. // be copied to the allocated buffer should the allocation succeed.
  576. // There can be 3 outcomes from the allocation:
  577. // 1) Allocation succeeded
  578. // 2) Allocation failed due to memory system problems
  579. // 3) Allocation was not done because the increment value changed during the
  580. // acquisition of the synchronization object.
  581. //
  582. // If the allocation failed due to memory problems, this function will fail without
  583. // performing the rest of the duties. For scenario 1, the function will
  584. // continue. For scenario 3, the function will return a specific error code
  585. // indicating that it had been beaten and the caller will have to do something else
  586. //
  587. // After the allocation phase, the write is attempted first, and if it succeeds,
  588. // the value is incremented. If the write fails for some reason, the value will
  589. // not be incremented. This is to ensure that all the data is written before the
  590. // increment so the data "exists" by the time the counter is updated. On the
  591. // event of a write failure, the memory cannot be salvaged.
  592. //
  593. // Arguments:
  594. // dwDesiredSize - Size of memory block to allocate
  595. // pfaOffsetToAllocatedMemory - returns the starting offset to the
  596. // allocated block, in flat address space
  597. // faOffsetToWriteOffsetToAllocatedMemory - Specifies a location in
  598. // which to store the offset of the allocated block
  599. // faOffsetToWriteSizeOfAllocatedMemory - Specifies a location in
  600. // which to store the actual size of the allocated block
  601. // pbInitialValueForAllocatedMemory - Specifies a buffer that contains
  602. // the initial value for the allocated block. This will be copied
  603. // to the allocated block if the allocation succeeds.
  604. // pbBufferToWriteFrom - source buffer of bytes to be written
  605. // dwOffsetInAllocatedMemoryToWriteTo - offset from the start of the
  606. // allocated block to start writing to.
  607. // dwSizeofBuffer - number of contiguous bytes to write
  608. // pdwValueToIncrement - Pointer to the value to be atomically incremented
  609. // after the write successfully written. This value MUST NOT be NULL.
  610. // dwReferenceValue - If the value in pdwValueToIncrement differs from this
  611. // value, the call will be aborted.
  612. // dwIncrementValue - Amount to increment pdwValueToIncrement.
  613. // pContext (Optional) - if specified, uses an alternate optimized
  614. // algorithm to access the memory, otherwise, the system looks
  615. // up the node in question using a full lookup, which is slower.
  616. // The system decides which algorithm to use based on some heuristics.
  617. //
  618. // Return values:
  619. // S_OK - Success, the write is successful.
  620. // HRESULT_FROM_WIN32(ERROR_RETRY) - Informational, The reference value
  621. // changed during processing and the call cannot complete. A retry
  622. // should be performed immediately.
  623. // STG_E_INVALIDPARAMETER - Error, one or more parameters are invalid, or
  624. // otherwise inconsistent.
  625. // STG_E_WRITEFAULT - Error, The write failed to complete.
  626. // TYPE_E_OUTOFBOUNDS - Debug Error, a write is issued to write past
  627. // the current allocated block.
  628. //
  629. HRESULT AtomicAllocWriteAndIncrement(
  630. DWORD dwDesiredSize,
  631. FLAT_ADDRESS *pfaOffsetToAllocatedMemory,
  632. FLAT_ADDRESS faOffsetToWriteOffsetToAllocatedMemory,
  633. FLAT_ADDRESS faOffsetToWriteSizeOfAllocatedMemory,
  634. LPBYTE pbInitialValueForAllocatedMemory,
  635. DWORD dwSizeOfInitialValue,
  636. LPBYTE pbBufferToWriteFrom,
  637. DWORD dwOffsetInAllocatedMemoryToWriteTo,
  638. DWORD dwSizeofBuffer,
  639. DWORD *pdwValueToIncrement,
  640. DWORD dwReferenceValue,
  641. DWORD dwIncrementValue,
  642. CBlockContext *pContext // Optional
  643. );
  644. //
  645. // Synopsis:
  646. // Traverses the list of allocated blocks, from the specified address, and
  647. // finds dirty blocks. For each dirty block encountered, the block will be
  648. // changed from "DIRTY" to "PENDING COMMIT". The flat address offset, block
  649. // size, and memory pointer to that block will be stored in the pdwOffset,
  650. // pdwSize, and ppbData arrays, respectively. An optional faLengthToScan
  651. // specifies the number of bytes from the starting offset to scan for
  652. // dirty blocks, if this is INVALID_FLAT_ADDRESS, then this function scans
  653. // to the end of all allocated blocks. It is not an error if there are
  654. // less allocated bytes than the length specified, only the allocated blocks
  655. // are scanned.
  656. //
  657. // The number of elements in each of these arrays is specified therough
  658. // pdwCount, which returns the number of dirty pages returned.
  659. //
  660. // When this function is first called, a strat address should be specified
  661. // (e.g. 0). When the function returns, pContext will be filled with the
  662. // next block to start traversing the next time this function is called.
  663. // Subsequent calls should pass in INVALID_FLAT_ADDRESS for start address
  664. // and use the pContext previously returned.
  665. //
  666. // Arguments:
  667. // faStartingOffset - Starting offset to start scanning for dirty blocks
  668. // dwLengthToScan - Length of memory from start to scan for dirty blocks
  669. // pdwCount - Specifies the number of entries allocated for each of the
  670. // offset, size and pointer arrays. Returns the number of dirty
  671. // pages actually found.
  672. // pdwOffset - Array to hold the offsets of pages
  673. // pdwSize - Array to hold the sizes of pages
  674. // ppbData - Array to hold the memory pointer to each page
  675. // pContext - Causes an alternate optimized algorithm to be used to access
  676. // the memory, otherwise, the system looks up the node in question using
  677. // a full lookup, which is slower. The system decides which algorithm to
  678. // use based on some heuristics.
  679. //
  680. // Return values:
  681. // S_OK - Success, one or more dirty blocks are returned.
  682. // HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) - Informational, no more dirty
  683. // blocks are found from the starting address to the end of the list.
  684. // STG_E_INVALIDPARAMETER - Error, one or more parameters are invalid, or
  685. // otherwise inconsistent.
  686. //
  687. HRESULT BuildDirtyBlockList(
  688. FLAT_ADDRESS faStartingOffset,
  689. FLAT_ADDRESS faLengthToScan,
  690. DWORD *pdwCount,
  691. FLAT_ADDRESS *pfaOffset,
  692. DWORD *pdwSize,
  693. LPBYTE *ppbData,
  694. CBlockContext *pContext
  695. );
  696. //
  697. // Synopsis:
  698. // Walks the list of allocated blocks ans changes all blocks whose state
  699. // is "PENDING COMMIT" to "CLEAN" or "DIRTY".
  700. //
  701. // In the debug version, any block that is both "DIRTY" and "PENDING COMMIT"
  702. // is invalid and results in an ASSERT.
  703. //
  704. // Arguments:
  705. // fClean - TRUE to mark the blocks as "CLEAN", FALSE for "DIRTY"
  706. //
  707. // Return values:
  708. // S_OK - Success.
  709. //
  710. HRESULT MarkAllPendingBlocks(
  711. BOOL fClean
  712. );
  713. //
  714. // Synopsis:
  715. // Sets the state of a specified block to the specified state.
  716. //
  717. // In the debug version, any block that is both "DIRTY" and "PENDING COMMIT"
  718. // is invalid and results in an ASSERT.
  719. //
  720. // Arguments:
  721. // pbData - block as specified by its data pointer
  722. // fClean - TRUE to mark the blocks as "CLEAN", FALSE for "DIRTY"
  723. //
  724. // Return values:
  725. // S_OK - Success.
  726. //
  727. HRESULT MarkBlockAs(
  728. LPBYTE pbData,
  729. BOOL fClean
  730. );
  731. //
  732. // Synopsis:
  733. // Traverses the list of allocated blocks, from the specified address, and
  734. // finds dirty blocks. For each dirty block encountered, the block will be
  735. // changed from "DIRTY" to "PENDING COMMIT" and the block will marked for
  736. // commit. When enough of these blocks are encountered, they will be
  737. // committed in a batch and the committed blocks will be marked as "CLEAN".
  738. // The process will iterate until no more dirty blocks. An optional faLengthToScan
  739. // specifies the number of bytes from the starting offset to scan for
  740. // dirty blocks, if this is INVALID_FLAT_ADDRESS, then this function scans
  741. // to the end of all allocated blocks. It is not an error if there are
  742. // less allocated bytes than the length specified, only the allocated blocks
  743. // are scanned.
  744. //
  745. // Arguments:
  746. // faStartingOffset - Starting offset to start scanning for dirty blocks
  747. // dwLengthToScan - Length of memory from start to scan for dirty blocks
  748. // pStream - specifies the IMailMsgPropertyStore to use to commit the blocks.
  749. // fComputeBlockCountsOnly - don't make calls to WriteBlocks, just
  750. // compute counters for what would be sent to WriteBlocks.
  751. // pcBlocksToWrite - incremented by how many blocks we would write
  752. // pcTotalBytesToWrite - incremented by the total byte count of what we
  753. // would write
  754. //
  755. // Return values:
  756. // S_OK - Success, one or more dirty blocks are returned.
  757. // STG_E_INVALIDPARAMETER - Error, one or more parameters are invalid, or
  758. // otherwise inconsistent.
  759. // Plus the error codomain of IMailMsgPropertyStream
  760. //
  761. HRESULT CommitDirtyBlocks(
  762. FLAT_ADDRESS faStartingOffset,
  763. FLAT_ADDRESS faLengthToScan,
  764. DWORD dwFlags,
  765. IMailMsgPropertyStream *pStream,
  766. BOOL fDontMarkAsCommit,
  767. BOOL fComputeBlockCountsOnly,
  768. DWORD *pcBlocksToWrite,
  769. DWORD *pcTotalBytesToWrite,
  770. IMailMsgNotify *pNotify
  771. );
  772. //
  773. // Synopsis:
  774. // Releases the entire list of nodes managed by this object.
  775. //
  776. // Arguments:
  777. // None.
  778. //
  779. // Return values:
  780. // S_OK - Success.
  781. //
  782. HRESULT Release();
  783. //
  784. // Synopsis:
  785. // Exposes the lock in the block manager, attempts to access the internal lock
  786. //
  787. // Arguments:
  788. // None.
  789. //
  790. // Remarks:
  791. // These locks will cause deadlocks if a thread tries to acquire it twice.
  792. // In debug builds, there will be some sort of deadlock detection, in
  793. // retail, you will deadlock.
  794. //
  795. // Return values:
  796. // S_OK - Success, the lock operation succeeded.
  797. // !(SUCCESS(HRESULT)) - An error occurred and the lock operaiton failed.
  798. //
  799. HRESULT ReadLock() { m_rwLock.ShareLock(); return(S_OK); }
  800. HRESULT ReadUnlock() { m_rwLock.ShareUnlock(); return(S_OK); }
  801. HRESULT WriteLock() { m_rwLock.ExclusiveLock(); return(S_OK); }
  802. HRESULT WriteUnlock() { m_rwLock.ExclusiveUnlock(); return(S_OK); }
  803. // return the state of the dirty flag
  804. BOOL IsDirty() { return m_fDirty; }
  805. // change the value of the dirty flag. this is used by MailMsg to
  806. // set it to FALSE when a successful Commit has occured.
  807. void SetDirty(BOOL fDirty) {
  808. m_fDirty = fDirty;
  809. #ifdef DEBUG
  810. // _ASSERT(!(m_fCommitting && m_fDirty));
  811. #endif
  812. }
  813. void SetCommitMode(BOOL fCommitting) {
  814. #ifdef DEBUG
  815. m_fCommitting = fCommitting;
  816. #endif
  817. }
  818. private:
  819. // GetNodeIdFromOffset() defined as a macro in the source
  820. // Method to load a block from the stream if required
  821. /*
  822. HRESULT ConnectLeftSibling(
  823. LPBLOCK_HEAP_NODE pNode,
  824. LPBLOCK_HEAP_NODE pParent,
  825. DWORD dwChildId
  826. );
  827. HRESULT ConnectRightSibling(
  828. LPBLOCK_HEAP_NODE pNode,
  829. LPBLOCK_HEAP_NODE pParent,
  830. DWORD dwChildId
  831. );
  832. */
  833. HRESULT GetStream(
  834. IMailMsgPropertyStream **ppStream,
  835. BOOL fLockAcquired
  836. );
  837. HRESULT MoveToNode(
  838. LPBLOCK_HEAP_NODE *ppNode,
  839. HEAP_NODE_ID idTargetNode,
  840. BOOL fLockAcquired
  841. );
  842. HRESULT GetNextNode(
  843. LPBLOCK_HEAP_NODE *ppNode,
  844. BOOL fLockAcquired
  845. );
  846. HRESULT LoadBlockIfUnavailable(
  847. HEAP_NODE_ID idNode,
  848. LPBLOCK_HEAP_NODE pParent,
  849. HEAP_NODE_ID idChildNode,
  850. LPBLOCK_HEAP_NODE *ppNode,
  851. BOOL fLockAcquired
  852. );
  853. HRESULT GetEdgeListFromNodeId(
  854. HEAP_NODE_ID idNode,
  855. HEAP_NODE_ID *rgEdgeList,
  856. DWORD *pdwEdgeCount
  857. );
  858. HRESULT GetNodeFromNodeId(
  859. HEAP_NODE_ID idNode,
  860. LPBLOCK_HEAP_NODE *ppNode,
  861. BOOL fLockAcquired = FALSE
  862. );
  863. HRESULT GetParentNodeFromNodeId(
  864. HEAP_NODE_ID idNode,
  865. LPBLOCK_HEAP_NODE *ppNode
  866. );
  867. HRESULT GetPointerFromOffset(
  868. FLAT_ADDRESS faOffset,
  869. LPBYTE *ppbPointer,
  870. DWORD *pdwRemainingSize,
  871. LPBLOCK_HEAP_NODE *ppNode
  872. );
  873. HRESULT InsertNodeGivenPreviousNode(
  874. LPBLOCK_HEAP_NODE pNodeToInsert,
  875. LPBLOCK_HEAP_NODE pPreviousNode
  876. );
  877. BOOL IsMemoryAllocated(
  878. FLAT_ADDRESS faOffset,
  879. DWORD dwLength
  880. );
  881. HRESULT AllocateMemoryEx(
  882. BOOL fAcquireLock,
  883. DWORD dwSizeDesired,
  884. FLAT_ADDRESS *pfaOffsetToAllocatedMemory,
  885. DWORD *pdwSizeAllocated,
  886. CBlockContext *pContext // Optional
  887. );
  888. HRESULT WriteAndIncrement(
  889. LPBYTE pbBuffer,
  890. FLAT_ADDRESS faOffset,
  891. DWORD dwBytesToWrite,
  892. DWORD *pdwValueToIncrement,
  893. DWORD dwIncrementValue,
  894. CBlockContext *pContext // Optional
  895. );
  896. HRESULT OperateOnMemory(
  897. DWORD dwOperation,
  898. LPBYTE pbBuffer,
  899. FLAT_ADDRESS faTargetOffset,
  900. DWORD dwBytesToDo,
  901. DWORD *pdwBytesDone,
  902. CBlockContext *pContext // Optional
  903. );
  904. HRESULT ReleaseNode(
  905. LPBLOCK_HEAP_NODE pNode
  906. );
  907. DWORD m_dwSignature;
  908. // This value indicates the current end of data. This is
  909. // always changed with interlocked operations such that
  910. // multiple threads can increment this variable and the
  911. // increments are properly serialized
  912. FLAT_ADDRESS m_faEndOfData;
  913. HEAP_NODE_ID m_idNodeCount;
  914. LPBLOCK_HEAP_NODE m_pRootNode;
  915. CBlockManagerGetStream *m_pParent;
  916. CBlockMemoryAccess m_bma;
  917. #ifndef BLOCKMGR_DISABLE_CONTENTION_CONTROL
  918. CShareLockNH m_rwLock;
  919. #endif
  920. IMailMsgProperties *m_pMsg;
  921. BOOL m_fDirty;
  922. #ifdef DEBUG
  923. BOOL m_fCommitting;
  924. #endif
  925. };
  926. #endif