266 lines
6.4 KiB
C++
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
|
||
|
|