📄 mmapmgr.cpp
字号:
/* ***** BEGIN LICENSE BLOCK *****
* Version: RCSL 1.0/RPSL 1.0
*
* Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved.
*
* The contents of this file, and the files included with this file, are
* subject to the current version of the RealNetworks Public Source License
* Version 1.0 (the "RPSL") available at
* http://www.helixcommunity.org/content/rpsl unless you have licensed
* the file under the RealNetworks Community Source License Version 1.0
* (the "RCSL") available at http://www.helixcommunity.org/content/rcsl,
* in which case the RCSL will apply. You may also obtain the license terms
* directly from RealNetworks. You may not use this file except in
* compliance with the RPSL or, if you have a valid RCSL with RealNetworks
* applicable to this file, the RCSL. Please see the applicable RPSL or
* RCSL for the rights, obligations and limitations governing use of the
* contents of the file.
*
* This file is part of the Helix DNA Technology. RealNetworks is the
* developer of the Original Code and owns the copyrights in the portions
* it created.
*
* This file, and the files included with this file, is distributed and made
* available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
*
* Technology Compatibility Kit Test Suite(s) Location:
* http://www.helixcommunity.org/content/tck
*
* Contributor(s):
*
* ***** END LICENSE BLOCK ***** */
// no memory map support on VXWORKS or Symbian
#if !defined(_VXWORKS) && !defined(_SYMBIAN)
/*
* MMapMgr needs atomic ref increments.
*/
#define _WIN32_USE_INTERLOCKED_INCREMENT
#include "hxcom.h"
#include "hxtypes.h"
#ifndef _MACINTOSH
#include <stdio.h>
#include <stdlib.h>
#ifdef _UNIX
#include <unistd.h>
#include <fcntl.h>
#endif
#include "hlxclib/sys/types.h"
#include "hlxclib/sys/stat.h"
#include "hlxclib/io.h"
#if defined(_UNIX) && !defined(_BEOS)
#include <sys/mman.h>
#endif
#include "hxbuffer.h"
#include "hxstrutl.h"
#include "hxmap.h"
#include "hxengin.h"
#include "mmapmgr.h"
#include "hxspriv.h"
#include "debug.h"
#include "mmapdatf.h"
#include "hxassert.h"
#include "hxmon.h"
#ifdef _MMM_NEED_MUTEX
#include "hxcomm.h"
#endif
//#define MMAPDEBUG
//#define MMAPMOREDEBUG
#ifdef MMAPDEBUG
#define MMAPDPRINTF(x) printf x
#else
#define MMAPDPRINTF(x)
#endif
/* Must be a multiple of the page size */
#define MMAP_INITIAL_CHUNK_SIZE \
(256 * 1024)
#define MMAP_EXTRA_SLOP_SIZE \
65536
#define MAX_ADDRESS_SPACE_USED \
1 * 1024 * 1000 * 1000 // 1 Gig
#define MS_BETWEEN_HANDLE_REAP_BUCKETS \
3000
#define NUMBER_OF_REAP_BUCKETS_TO_EMPTY_ON_ADDRESS_SPACE_EXHUSTED \
2
#ifndef MAP_FAIL
#define MAP_FAIL ((void *)-1)
#endif
/*
* On Linux/NT we have 1 address space for all procs. On other unicies we
* have 'n' processes and 'n' address spaces. This variables tracks address
* usage correctly on all 3 types of OSes.
*/
UINT32 g_ulAddressSpaceUsed = 0;
/*
* On the server we wish to reap the memory after a little bit of time
* because it is very likely that we will want to use it again in a
* short amount of time. On the client we do not wish to do this, becuase we
* can not do good things like delete the file if it is mem-mapped.
*/
static BOOL z_bDeterminedIfWeAreWithinServer = FALSE;
static BOOL z_bWithinServer = FALSE;
/*
* On NT we only have one MemoryMapManager per server.
* On other unix platforms, we have on MemoryMapManager
* per process.
*
* XXXSMP The Linux code is Sub-Optimal!! Since we have 1 address space,
* we should take advantage of the fact that pages that we are trying to
* map in one thread may already be mapped by another thread. With 'n'
* MMM's, we can't take advantage of this optimization.
*/
MemoryMapManager::MemoryMapManager(IUnknown* pContext,
BOOL bDisableMemoryMappedIO, UINT32 ulChunkSize)
: m_pMMMCallback(NULL)
{
m_pDevINodeToFileInfoMap = new CHXMapStringToOb;
m_pDevINodeToFileInfoMap->InitHashTable(517);
m_ulActiveReapList = 0;
HX_VERIFY (HXR_OK ==
pContext->QueryInterface(IID_IHXScheduler, (void **)&m_pScheduler));
if (!z_bDeterminedIfWeAreWithinServer)
{
z_bDeterminedIfWeAreWithinServer = TRUE;
IHXRegistry* pHXReg = NULL;
pContext->QueryInterface(IID_IHXRegistry, (void **)&pHXReg);
if (pHXReg)
{
HXPropType type = pHXReg->GetTypeByName("server.version");
if (type!= PT_UNKNOWN)
{
z_bWithinServer = TRUE;
}
HX_RELEASE(pHXReg);
}
}
if (ulChunkSize)
{
m_ulChunkSize = ulChunkSize;
}
else
{
m_ulChunkSize = MMAP_INITIAL_CHUNK_SIZE;
}
m_lRefCount = 0;
if (z_bWithinServer)
{
m_pMMMCallback = new MMMCallback(this);
if (m_pMMMCallback)
{
m_pMMMCallback->AddRef();
m_pMMMCallback->m_hPendingHandle = m_pScheduler->RelativeEnter(m_pMMMCallback, MS_BETWEEN_HANDLE_REAP_BUCKETS);
}
}
#ifdef _MMM_NEED_MUTEX
m_pMutex = NULL;
#ifdef _DEBUG
m_bHaveMutex = FALSE;
#endif
IHXCommonClassFactory* pCCF;
if (HXR_OK == pContext->QueryInterface(IID_IHXCommonClassFactory,
(void**)&pCCF))
{
pCCF->CreateInstance(CLSID_IHXMutex, (void**)&m_pMutex);
pCCF->Release();
}
#endif
m_pFastAlloc = NULL;
pContext->QueryInterface(IID_IHXFastAlloc, (void **)&m_pFastAlloc);
m_bDisableMemoryMappedIO = bDisableMemoryMappedIO;
}
MemoryMapManager::~MemoryMapManager()
{
/* This had better be empty by now!! */
HX_DELETE(m_pDevINodeToFileInfoMap);
if (m_pMMMCallback && m_pMMMCallback->m_hPendingHandle)
{
m_pScheduler->Remove(m_pMMMCallback->m_hPendingHandle);
}
HX_RELEASE(m_pMMMCallback);
HX_RELEASE(m_pScheduler);
HX_RELEASE(m_pFastAlloc);
#ifdef _MMM_NEED_MUTEX
HX_RELEASE(m_pMutex);
#endif
}
STDMETHODIMP
MemoryMapManager::QueryInterface(REFIID riid, void** ppvObj)
{
if (IsEqualIID(riid, IID_IUnknown))
{
AddRef();
*ppvObj = (IUnknown*)(IUnknown*)this;
return HXR_OK;
}
*ppvObj = NULL;
return HXR_NOINTERFACE;
}
/*
* These are the only AddRef/Release pairs
* that need to be thread safe. All the
* other don't need to be. Since _WIN32_USE_INTERLOCKED_INCREMENT
* is defined at the top of this file, every instance will be actual
* so for the ones that we don't want to use it we just do ++/--
*/
STDMETHODIMP_(ULONG32)
MemoryMapManager::AddRef()
{
return InterlockedIncrement(&m_lRefCount);
}
STDMETHODIMP_(ULONG32)
MemoryMapManager::Release()
{
if (InterlockedDecrement(&m_lRefCount) > 0)
{
return m_lRefCount;
}
delete this;
#ifdef _WIN32
MemoryMapDataFile::m_zpMMM = NULL;
#endif
return 0;
}
void
MemoryMapManager::ProcessIdle()
{
LockMutex();
m_ulActiveReapList = (m_ulActiveReapList + 1) % NUMBER_OF_REAP_BUCKETS;
EmptyReapBuckets();
/*
* Note: This callback is always called on Streamer #1 on NT, so the first
* streamer to be spawned will reap dead pages for all other processes.
*/
m_pMMMCallback->m_hPendingHandle =m_pScheduler->RelativeEnter(
m_pMMMCallback, MS_BETWEEN_HANDLE_REAP_BUCKETS);
UnlockMutex();
return;
}
/*
* This context is from the correct process to charge the descriptor usage
* against.
*/
void*
MemoryMapManager::GetMMHandle(FILE_IDENTIFIER Descriptor)
{
char pLookup[128]; /* Flawfinder: ignore */
UINT32 ulSize = 0;
#ifdef _UNIX
struct stat s;
if (fstat(Descriptor, &s) == 0)
{
if (!s.st_dev || !s.st_ino)
{
return 0;
}
#ifdef _RED_HAT_5_X_
/* Why make devices 64 bit??? */
sprintf (pLookup, "%lld,%ld", s.st_dev, s.st_ino); /* Flawfinder: ignore */
#else
sprintf (pLookup, "%d,%ld", s.st_dev, s.st_ino); /* Flawfinder: ignore */
#endif
ASSERT(s.st_dev);
ASSERT(s.st_ino);
ulSize = (UINT32)s.st_size;
MMAPDPRINTF(("%p: GetHandle %d %d %ld ", this,
Descriptor, s.st_dev, s.st_ino));
}
#elif defined(_WINDOWS)
BY_HANDLE_FILE_INFORMATION FileInformation;
if (GetFileInformationByHandle(Descriptor, &FileInformation))
{
sprintf (pLookup, "%x,%x,%x", FileInformation.nFileIndexLow, /* Flawfinder: ignore */
FileInformation.nFileIndexHigh, FileInformation.dwVolumeSerialNumber);
ulSize = FileInformation.nFileSizeLow;
MMAPDPRINTF(("%p: '%s'\n", Descriptor, pLookup));
}
#endif
if (ulSize == 0)
{
return 0;
}
void* pVoid = NULL;
LockMutex();
m_pDevINodeToFileInfoMap->Lookup(pLookup, pVoid);
UnlockMutex();
return pVoid;
}
void*
MemoryMapManager::OpenMap(FILE_IDENTIFIER Descriptor, IUnknown* pContext)
{
char pLookup[128]; /* Flawfinder: ignore */
UINT32 ulSize = 0;
if (m_bDisableMemoryMappedIO)
{
return NULL;
}
#ifdef _UNIX
struct stat s;
if (fstat(Descriptor, &s) == 0)
{
if (!s.st_dev || !s.st_ino)
{
return 0;
}
#ifdef _RED_HAT_5_X_
/* Why make devices 64 bit??? */
sprintf (pLookup, "%lld,%ld", s.st_dev, s.st_ino); /* Flawfinder: ignore */
#else
sprintf (pLookup, "%d,%ld", s.st_dev, s.st_ino); /* Flawfinder: ignore */
#endif
ASSERT(s.st_dev);
ASSERT(s.st_ino);
ulSize = (UINT32)s.st_size;
MMAPDPRINTF(("%p: OpenMap %d %d %ld ", this,
Descriptor, s.st_dev, s.st_ino));
}
#elif defined(_WINDOWS)
BY_HANDLE_FILE_INFORMATION FileInformation;
if (GetFileInformationByHandle(Descriptor, &FileInformation))
{
sprintf (pLookup, "%x,%x,%x", FileInformation.nFileIndexLow, /* Flawfinder: ignore */
FileInformation.nFileIndexHigh, FileInformation.dwVolumeSerialNumber);
ulSize = FileInformation.nFileSizeLow;
MMAPDPRINTF(("%p: '%s'\n", Descriptor, pLookup));
}
#endif
if (ulSize == 0)
{
return 0;
}
void* pVoid = NULL;
struct _FileInfo* pInfo = NULL;
LockMutex();
m_pDevINodeToFileInfoMap->Lookup(pLookup, pVoid);
if (pVoid)
{
MMAPDPRINTF(("(OpenMap Found Already)\n"));
pInfo = (struct _FileInfo*) pVoid;
HX_ASSERT (pInfo->Descriptor != 0);
pInfo->ulRefCount++;
pInfo->ulUseCount++;
/* In case the file size has changed */
pInfo->ulSize = ulSize;
UnlockMutex();
return pVoid;
}
else
{
MMAPDPRINTF(("(OpenMap New)\n"));
#ifdef _UNIX
m_pDevINodeToFileInfoMap->SetAt(pLookup, pInfo = new struct _FileInfo);
pInfo->Descriptor = dup(Descriptor);
#else
HANDLE hTest = CreateFileMapping(Descriptor, NULL, PAGE_READWRITE, 0, 0, NULL);
if (!hTest)
{
/*
* Try to open the mapping for read only. This is supposed to handle
* readonly files. If the file was opened for write then this
* will fail anyway, so we can't really do the wrong thing here.
*/
hTest = CreateFileMapping(Descriptor, NULL, PAGE_READONLY, 0, 0, NULL);
if (!hTest)
{
UnlockMutex();
return NULL;
}
}
m_pDevINodeToFileInfoMap->SetAt(pLookup, pInfo = new struct _FileInfo);
pInfo->Descriptor = hTest;
#endif
SafeStrCpy(pInfo->pKey, pLookup, FILEINFO_KEY_SIZE);
pInfo->ulSize = ulSize;
pInfo->pMgr = this;
pInfo->pMgr->AddRef();
pInfo->ulRefCount = 1;
pInfo->ulUseCount = 1;
#ifdef _WIN32
pInfo->m_pPTEList = NULL;
#endif
memset(pInfo->pPageTable, 0, sizeof (pInfo->pPageTable));
if (HXR_OK == pContext->QueryInterface(IID_IHXDescriptorRegistration,
(void **)&pInfo->pDescReg))
{
pInfo->pDescReg->RegisterDescriptors(1);
}
else
{
pInfo->pDescReg = NULL;
}
UnlockMutex();
return (void *)pInfo;
}
}
#ifdef _WIN32
void
MemoryMapManager::AttemptCloseMapNow(void* pHandle)
{
LockMutex();
struct _FileInfo* pInfo = (struct _FileInfo*)pHandle;
_PageTableEntry* pPTE;
_PageTableEntry* pNextPTE;
pNextPTE = pInfo->m_pPTEList;
while (pNextPTE)
{
pPTE = pNextPTE;
pNextPTE = pPTE->m_pNextPTE;
pPTE->bReapMe = TRUE;
CheckAndReapPageTableEntry(pPTE);
}
UnlockMutex();
}
#endif
void
MemoryMapManager::CloseMap(void* pHandle)
{
LockMutex();
MMAPDPRINTF(("CloseMap\n"));
struct _FileInfo* pInfo = (struct _FileInfo*)pHandle;
pInfo->ulRefCount--;
pInfo->ulUseCount--;
if (pInfo->ulRefCount == 0)
{
DestroyFileInfo(pHandle);
}
#if 0
/*
* This code is really slow and not needed. These pages will be
* reaped shortly by the timed reaper.
*/
if (pInfo->ulUseCount == 0)
{
for (int i = 0; i < NUM_PTES; i++)
{
if (pInfo->pPageTable[i] == NULL)
{
continue;
}
struct _PageTableEntry* pIE = 0;
for (int j = 0; j < NUM_PTES; j++)
{
pIE = (struct _PageTableEntry*)
&(pInfo->pPageTable[i]->pEntry[j]);
if (pIE->bActive == FALSE)
{
continue;
}
pIE->bReapMe = TRUE;
CheckAndReapPageTableEntry(pIE);
if (pInfo->pPageTable[i] == NULL)
{
break;
}
}
}
}
#endif
MMAPDPRINTF(("Done CloseMap\n"));
UnlockMutex();
}
/*
* On _WIN32 whoever calls this MUST have the mUTex!!!
*/
void MemoryMapManager::EmptyReapBuckets()
{
#if defined _MMM_NEED_MUTEX && defined _DEBUG
HX_ASSERT(m_bHaveMutex);
#endif
UINT32 ulListToReap = (m_ulActiveReapList + 1) % NUMBER_OF_REAP_BUCKETS;
if (!ReapBuckets[ulListToReap].IsEmpty())
{
CHXSimpleList* pList = &ReapBuckets[ulListToReap];
struct _PageTableEntry* pIE = 0;
LISTPOSITION l = pList->GetHeadPosition();
LISTPOSITION temp;
while (l)
{
pIE = (struct _PageTableEntry*)pList->GetAt(l);
pIE->bReapMe = TRUE;
temp = l;
pList->GetNext(l);
if (CheckAndReapPageTableEntry(pIE) == FALSE)
{
pIE->bDeadPage = TRUE;
pList->RemoveAt(temp);
}
else
{
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -