2025-04-27 07:49:33 -04:00

284 lines
8.6 KiB
C++

/*==========================================================================*\
Module: _smseg.h
Copyright Microsoft Corporation 1996, All Rights Reserved.
Author: mikepurt
Descriptions: The Shared Memory Segment or SMS.
There's one of these for each file mapping and view.
Each file mapping and view is 64KB in size.
\*==========================================================================*/
#ifndef ___SMSEG_H__
#define ___SMSEG_H__
//
// SharedMemoryBlockAddress (SMBA) bit assignments:
//
// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +-------------------------------+-----------------------+-------+
// | Shared Memory Segment # | Segment Offset | BlkSz |
// +-------------------------------+-----------------------+-------+
//
// where
//
// Shared Memory Segment # - used to find the named shared memory segment
//
// Segment Offset - Mask off these bits to get the offset into the segment
// for the block.
//
// BlkSz - Block Size == (1 << BlkSz)
//
// The Shared Memory Segment #, the BlkSz, and the instance name uniquely identify
// a share memory segment.
//
// An SMBA handle with a value of NULL means what you'd generally expect it to mean.
//
//
// Some Utility functions
//
/*$--SegmentOffsetFromSMBA==================================================*\
Given an SMBA handle, returns offset into the segment to get the virtual
address of the block referred to by the handle.
\*==========================================================================*/
inline
DWORD
SegmentOffsetFromSMBA(IN SHMEMHANDLE hSMBA)
{
return (DWORD)hSMBA & 0x0000fff0;
}
/*$--FBlkSzFromSMBA=========================================================*\
Given an SMBA handle, returns the bit number that detemines the block size
of the block of memory referred to by the handle. The bit number is used to
determine how many bits a 1 is shifted to the left to get the block size.
\*==========================================================================*/
inline
DWORD
FBlkSzFromSMBA(IN SHMEMHANDLE hSMBA)
{
return (DWORD)hSMBA & 0x0000000f;
}
/*$--SegmentIdFromSMBA======================================================*\
Given an SMBA handle, returns the Segment ID for the block of memory referred
to by the handle.
\*==========================================================================*/
inline
DWORD
SegmentIdFromSMBA(IN SHMEMHANDLE hSMBA)
{
return ((DWORD)hSMBA >> 16);
}
/*$--MakeSMBA===============================================================*\
Composes an SMBA handle given the components that make up an SMBA handle.
\*==========================================================================*/
inline
SHMEMHANDLE
MakeSMBA(IN DWORD dwSegmentId,
IN DWORD dwSegmentOffset,
IN DWORD fBlkSz)
{
Assert(!(dwSegmentId & 0xffff0000));
Assert(!(dwSegmentOffset & 0xffff000f));
Assert(!(fBlkSz & 0xfffffff0));
return (SHMEMHANDLE)((dwSegmentId << 16) | dwSegmentOffset | fBlkSz);
}
//
// The following is the size of the smallest chunk of virtual address space that
// can be reserved. Making it smaller than this wastes virtual address space.
// Making it larger than this reduces the chance of successfully getting the
// address space if the virtual address space of the process is fragmented.
//
const DWORD SMH_SEGMENT_SIZE = 64 * 1024;
//
// The FREE_LIST_HANDLE is composed of two values.
// The high order 13 bits is the offset into the segment for the next free block
// The low order 19 bits is the operation sequence number and is incremented
// each time an element is removed from the list.
//
// Without using a sequence number it's possible for another thread (or threads)
// to change the list in some way and still have the next free block be the same
// one that was there when you checked it. Although it's still possible that
// this may happen with a sequence number, it would require 524288 allocates
// and the same free block offset in the flhFirstFree member between
// when we first copy the flhFirstFree member and when we attempt to update it.
//
// When adding (pushing) something onto this list (freeing), incrementing the
// sequence isn't necessary because you don't care if the list changed. All
// you care about is that the same element is on the top of the list because
// this is how your setting the "next" link in the element that you're freeing.
//
typedef DWORD FREE_LIST_HANDLE;
const DWORD BLOCK_OFFSET_NULL = 0x0000fff8;
const DWORD FLH_BLOCK_OFFSET_SHIFT = 16;
const DWORD FLH_BLOCK_OFFSET_MASK = 0xfff80000;
const DWORD FLH_SEQUENCE_MASK = 0x0007ffff;
/*$--FreeList===============================================================*\
There's one of these for each Shared Memory Segment (SMS). This structure
may be expanded to facilitate debugging or timestamping last accesses to
facilitate freeing segments that haven't been used recently.
\*==========================================================================*/
struct FreeList
{
volatile FREE_LIST_HANDLE flhFirstFree;
};
class CSharedMemoryBlockHeap;
/*$--CSharedMemorySegment===================================================*\
\*==========================================================================*/
class CSharedMemorySegment : public CSMBase
{
public:
CSharedMemorySegment(IN CSharedMemoryBlockHeap * psmbh);
~CSharedMemorySegment();
BOOL FInitialize(IN LPCWSTR pwszInstanceName,
IN DWORD cbBlockSize,
IN DWORD dwSegmentId);
void Deinitialize();
void InitializeBlocks();
void Free(IN SHMEMHANDLE hSMBA);
PVOID PvAlloc(OUT SHMEMHANDLE * phSMBA);
PVOID PvFromSMBA(IN SHMEMHANDLE hSMBA);
//
// CSharedMemoryBlockHeap needs to get to m_pbMappedView so that it can
// calculate the virtual address of the freelist for other SMSs.
//
BYTE * PbGetMappedView() { return m_pbMappedView; };
private:
BOOL FIsValidSMBA(IN SHMEMHANDLE hSMBA);
DWORD IndexFromOffset(IN DWORD cbOffset);
//
// This points to the FreeList for this SMS.
//
FreeList * m_pfl;
//
// The usual byprodcuts of creating and mapping a file view.
//
HANDLE m_hFileMapping;
BYTE * m_pbMappedView;
//
// These are kept around for convenience after Initialization
//
DWORD m_dwSegmentId;
DWORD m_cbBlockSize;
DWORD m_fBlkSz;
//
// This lets us find out way back to the block heap that owns us.
//
CSharedMemoryBlockHeap * m_psmbh;
//
// We want anyone copying this around.
//
CSharedMemorySegment& operator=(const CSharedMemorySegment&);
CSharedMemorySegment(const CSharedMemorySegment&);
};
/*$--CSharedMemorySegment::IndexFromOffset==================================*\
Given a block offset, calculate the index for the bit vector that represents
this block.
\*==========================================================================*/
inline
DWORD
CSharedMemorySegment::IndexFromOffset(IN DWORD cbOffset)
{
return cbOffset >> m_fBlkSz;
}
/*$--CSharedMemorySegment::PvFromSMBA=======================================*\
This calculates the virtual address for a block given its SMBA handle.
A side effect of this call is that the block is marked as owned.
\*==========================================================================*/
inline
PVOID
CSharedMemorySegment::PvFromSMBA(IN SHMEMHANDLE hSMBA)
{
Assert(FIsValidSMBA(hSMBA));
return (!hSMBA ? NULL : (PVOID)(SegmentOffsetFromSMBA(hSMBA) + m_pbMappedView));
}
/*$--FreeListsPerSegment====================================================*\
This simply calculates the number of FreeLists a segment can host.
FreeLists are stored in the first block of memory in a segment.
In order to save space, the several FreeLists are stored in one block.
The means that subsequent SMSs can save the overhead of putting their FreeList
in the same SMS that it refers to.
See CSharedMemoryBlockHeap::FreeListFromSegmentId() to understand better how
this works.
\*==========================================================================*/
inline
DWORD
FreeListsPerSegment(IN DWORD cbBlockSize)
{
return cbBlockSize / sizeof(FreeList);
}
#endif // ___SMSEG_H__