windows-nt/Source/XPSP1/NT/com/rpc/runtime/mtrt/bcache.hxx
2020-09-26 16:20:57 +08:00

266 lines
6.4 KiB
C++

/*++
Copyright (C) Microsoft Corporation, 1997 - 1999
Module Name:
bcache.hxx
Abstract:
RPC's buffer cache class
Author:
Mario Goertzel [MarioGo]
Revision History:
MarioGo 9/25/1997 Bits 'n pieces
--*/
/*++
Copyright (C) Microsoft Corporation, 1997 - 1999
Module Name:
bcache.hxx
Abstract:
Cached buffer allocation class
Author:
Mario Goertzel [MarioGo]
Revision History:
MarioGo 9/7/1997 Bits 'n pieces
--*/
#ifndef __BCACHE_HXX
#define __BCACHE_HXX
//
// The RPC buffer cache uses several levels of caching to improve
// performance. The cache has either 2 or 4 fixed buffer sizes that
// it caches. The first level of cache is per-thread. The second
// level is process wide.
//
// Their are two performance goals: make a single alloc/free loop
// execute with a minimum of cycles and scale well on MP machines.
// This implementation can do 8000000 allocs in 761ms using 8 threads
// on a 4xPPro 200.
//
// In the default mode, elements which are the right size for various
// runtime allocations as cached.
//
// In paged bcache mode (used for testing) 1 and 2 page buffers are cached.
// In this mode we allocate each buffer at the end of the page, and we
// add a read only page after that. This allows NDR to temporarily read
// past the end of the buffer without raising exceptions, but writes
// will AV. That's we we can't use paged heap BTW - we need a page
// after the buffer with RO access, not without any access.
//
struct BUFFER_CACHE_HINTS
{
// When the thread cache is empty, this many blocks are moved
// from the global cache (if possible) to the thread cache.
// When freeing from the thread cache, this many buffers
// are left in the thread cache.
UINT cLowWatermark;
// When per thread cache will reach mark due to a free, blocks
// will be moved to the global cache.
UINT cHighWatermark;
// Summary: The difference between high and low is the number
// of blocks allocated/freed from the global cache at a time.
//
// Lowwater should be the average number of free buffers - 1
// you expect a thread to have. Highwater should be the
// maximum number of free buffers + 1 you expect a thread
// to need.
//
// **** Note: The difference must be two or more. ***
// Example: 1 3
// Alloc called with the thread cache empty, two blocks are removed
// from the global list. One is saved the thread list. The other
// is returned.
//
// Free called with two free buffers in the thread list. A total
// of three buffers. Two buffers are moved to the global cache, one
// stays in the thread cache.
//
//
// The size of the buffers
UINT cSize;
};
extern CONST BUFFER_CACHE_HINTS gCacheHints[4];
extern BUFFER_CACHE_HINTS gPagedBCacheHints[4];
extern BUFFER_CACHE_HINTS *pHints;
struct PAGED_BCACHE_SECTION_MANAGER
{
ULONG NumberOfSegments;
ULONG NumberOfUsedSegments;
ULONG SegmentSize; // doesn't include guard page. In bytes. I.e. size
// of read-write committed segment
void *VirtualMemorySection;
LIST_ENTRY SectionList; // all sections are chained. This both makes leak
// tracking easier and it allows us to maintain
// good locality by allocating off the first section
// first.
BOOLEAN SegmentBusy[1]; // actually the array size is the number of segments
// we use boolean as arbitrary tradeoff b/n speed (ULONG)
// and size (true bit vector).
};
// Used in all modes, sits at the front of the buffer allocation.
struct BUFFER_HEAD
{
union {
BUFFER_HEAD *pNext; // Valid only in free lists
INT index; // Valid only when allocated
// 1-4 for cachable, -1 for big
};
union {
// Used in paged bcache mode only.
SIZE_T size; // if index == -1, this is the size. Used only
// for debugging.
PAGED_BCACHE_SECTION_MANAGER *SectionManager; // points to a small heap block
// containing control information
};
};
typedef BUFFER_HEAD *PBUFFER;
// This structure is imbedded into the RPC thread object
struct BCACHE_STATE
{
BUFFER_HEAD *pList;
ULONG cBlocks;
};
// The strucutre parrallels the global cache
struct BCACHE_STATS
{
UINT cBufferCacheCap;
UINT cAllocationHits;
UINT cAllocationMisses;
};
class THREAD;
class BCACHE
{
private:
BCACHE_STATE _bcGlobalState[4];
BCACHE_STATS _bcGlobalStats[4];
MUTEX _csBufferCacheLock;
LIST_ENTRY Sections; // used in guard page mode only
PVOID AllocHelper(size_t, INT, BCACHE_STATE *);
VOID FreeHelper(PVOID, INT, BCACHE_STATE *);
VOID FreeBuffers(PBUFFER, INT, UINT);
PBUFFER AllocBigBlock(IN size_t);
VOID FreeBigBlock(IN PBUFFER);
PBUFFER
BCACHE::AllocPagedBCacheSection (
IN UINT size,
IN ULONG OriginalSize
);
ULONG
GetSegmentIndexFromBuffer (
IN PBUFFER pBuffer,
IN PVOID Allocation
);
#if DBG
void
VerifyPagedBCacheState (
void
);
void
VerifySectionState (
IN PAGED_BCACHE_SECTION_MANAGER *Section
);
void
VerifySegmentState (
IN PVOID Segment,
IN BOOL IsSegmentBusy,
IN ULONG SegmentSize,
IN PAGED_BCACHE_SECTION_MANAGER *OwningSection
);
#endif
PVOID
PutBufferAtEndOfAllocation (
IN PVOID Allocation,
IN ULONG AllocationSize,
IN ULONG BufferSize
);
PVOID
ConvertBufferToAllocation (
IN PBUFFER Buffer,
IN BOOL IsBufferInitialized
);
PVOID
CommitSegment (
IN PVOID SegmentStart,
IN ULONG SegmentSize
);
public:
BCACHE(RPC_STATUS &);
~BCACHE();
PVOID Allocate(CONST size_t cSize);
VOID Free(PVOID);
VOID ThreadDetach(THREAD *);
};
extern BCACHE *gBufferCache;
// Helper APIs
inline PVOID
RpcAllocateBuffer(CONST size_t cSize)
{
return(gBufferCache->Allocate(cSize));
}
inline VOID
RpcFreeBuffer(PVOID pBuffer)
{
if (pBuffer == 0)
{
return;
}
gBufferCache->Free(pBuffer);
}
#endif // __BCACHE_HXX