home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Troubleshooting Netware Systems
/
CSTRIAL0196.BIN
/
attach
/
msj
/
v10n10
/
gmmfsrc.exe
/
GMMF.C
next >
Wrap
C/C++ Source or Header
|
1995-10-01
|
14KB
|
382 lines
/*****************************************************************************
Module name: GMMF.c
Written by: Jeffrey Richter
Purpose: Implementation of growable memory-mapped files.
*****************************************************************************/
#define STRICT
#include <windows.h>
#include <windowsx.h>
#pragma warning(disable: 4001) /* single line comment */
#include "GMMF.h"
//////////////////////////////////////////////////////////////////////////////
// Internal functions implemented at the end of this file.
// Clear the MMF and the reserved address space immediately following it.
static void WINAPI GMMF_ClearRegion (PGMMF pgmmf);
// Construct a region containing a MMF and a reserved address space region
// immediately following it.
static void WINAPI GMMF_ConstructRegion (PGMMF pgmmf, DWORD cbDiskFileNow);
// This function adjusts the contents of the GMMF's memory region.
// It begins by clearing the contents of the region and then constructs the
// contents with the properly adjusted MMF and reserved region.
static void WINAPI GMMF_AdjustRegion (PGMMF pgmmf, DWORD cbDiskFileNow);
//////////////////////////////////////////////////////////////////////////////
// This algorithm require knowledge of the system information. In particular,
// the allocation-granularity (and possibly the page size) is used. Since
// this information never changes, it can be obtained once (in GMMF_Create)
//.and saved in this global variable for later reference.
static SYSTEM_INFO g_sinf;
#ifndef GMMF_WINNT_ONLY
// There are some differences between Windows NT and Windows 95. This global
// variable tells us which OS platofmr the code is executing on.
static OSVERSIONINFO g_osvi;
#endif
//////////////////////////////////////////////////////////////////////////////
// This algorithm must reserve address space immediately following a MMF.
// Normally, we would use VirtualAlloc to do this but Windows 95 does not
// allow VirtualAlloc to reserve address space between 0x80000000 and
// 0xBFFFFFFF. However, we can create a MMF with the SEC_RESERVE flag to
// reserve address space in the this memory range. The following macros
// abstract the reserving/releasing of address space for the two OS platforms.
#ifdef GMMF_WINNT_ONLY
// If Windows NT only, use VirtualAlloc/VirtualFree functions.
#define GMMF_ResAddrSpace(pgmmf, pvAddress, cbSize) \
VirtualAlloc(pvAddress, cbSize, MEM_RESERVE, PAGE_NOACCESS)
#define GMMF_FreeAddrSpace(pgmmf, pvAddress) \
VirtualFree(pvAddress, 0, MEM_RELEASE)
#else
// If Windows NT or Windows 95, use MMFs with SEC_RESERVE.
// This define is passed to CreateFileMapping in order to create
// a file mapping that is backed by the system's paging file(s).
#define HFILE_PAGEFILE ((HANDLE) 0xffffffff)
#define GMMF_ResAddrSpace(pgmmf, pvAddress, cbSize) \
(pgmmf->hFileMapRes = CreateFileMapping(HFILE_PAGEFILE, NULL, \
PAGE_READONLY | SEC_RESERVE, 0, cbSize, NULL), \
(pgmmf->hFileMapRes == NULL) ? NULL : \
MapViewOfFileEx(pgmmf->hFileMapRes, FILE_MAP_READ, 0, 0, 0, pvAddress))
#define GMMF_FreeAddrSpace(pgmmf, pvAddress) \
(UnmapViewOfFile(pvAddress) && CloseHandle(pgmmf->hFileMapRes))
#endif
//////////////////////////////////////////////////////////////////////////////
// Creates a GMMF and places it in the process's address space. Note that
// the cbFileSizeMax and cbFileGrowInc values are rounded up to an even
// allocation-granularity boundary. The return value is the address of the
// GMMF or NULL if the GMMF could not be created.
PVOID WINAPI GMMF_Create (PGMMF pgmmf, HANDLE hFile, DWORD cbFileSizeMax,
DWORD cbFileGrowInc, DWORD cbOverrunBuf) {
DWORD cbMaxRgnSize;
if (g_sinf.dwAllocationGranularity == 0) {
// See the comment assocaited with the g_sinf and g_osvi
// variables above.
GetSystemInfo(&g_sinf);
#ifndef GMMF_WINNT_ONLY
// Sometimes we need to know if we're on Windows 95 or Windows NT
g_osvi.dwOSVersionInfoSize = sizeof(g_osvi);
GetVersionEx(&g_osvi);
#endif
}
// Clear all the members of the pgmmf structure
ZeroMemory(pgmmf, sizeof(*pgmmf));
// Initialize the members with the values passed to this function
// NOTE: The maximum file size and the file grow increment are rounded up
// to the next allocation granularity boundary.
pgmmf->cbFileSizeMax = RoundUp(cbFileSizeMax,
g_sinf.dwAllocationGranularity);
pgmmf->cbFileGrowInc = RoundUp(cbFileGrowInc,
g_sinf.dwAllocationGranularity);
pgmmf->cbOverrunBuf = cbOverrunBuf;
pgmmf->hFile = hFile;
// Reserve an address space region that is big enough to hold the GMMF
// assuming that it grew to its maximum size and the overrun buffer area.
cbMaxRgnSize = pgmmf->cbFileSizeMax + cbOverrunBuf;
#ifdef GMMF_WINNT_ONLY
pgmmf->pbFile = GMMF_ResAddrSpace(pgmmf, NULL, cbMaxRgnSize);
#else
if (g_osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) {
// Windows 95 maps MMFs using page granularity rather than allocation
// granularity. We must force allocation-granularity so we will reserve
// (AllocGran minus PageSize) more space than we have to and round the
// memory address up to an allocation-granularity boundary. Finally,
// we reserve the exact amount we need at the AllocGran memory address.
PBYTE pbTemp = GMMF_ResAddrSpace(pgmmf, NULL, cbMaxRgnSize +
g_sinf.dwAllocationGranularity - g_sinf.dwPageSize);
pgmmf->pbFile = (PBYTE)
RoundUp((DWORD) pbTemp, g_sinf.dwAllocationGranularity);
// Try to make the next two statements execute without being pre-empted.
Sleep(0);
GMMF_FreeAddrSpace(pgmmf, pbTemp);
pgmmf->pbFile = GMMF_ResAddrSpace(pgmmf, pgmmf->pbFile, cbMaxRgnSize);
} else {
// Windows NT guarantess allocation granularity so we don't
// have to force it.
pgmmf->pbFile = GMMF_ResAddrSpace(pgmmf, NULL, cbMaxRgnSize);
}
#endif
// Adjust the GMMF region so that it contains a MMF backed by the disk
// file's storage and a reserved region (up to the maximum size of the
// GMMF specified) immediately following the MMF.
GMMF_AdjustRegion(pgmmf, GetFileSize(hFile, NULL));
// Return the base address of the GMMF
return(pgmmf->pbFile);
}
//////////////////////////////////////////////////////////////////////////////
// SEH Exception filter used to grow the GMMF.
long WINAPI GMMF_ExcFilter (PEXCEPTION_POINTERS pexp, PGMMF pgmmf) {
// Assume that we don't know how to handle the exception
DWORD lDisposition = EXCEPTION_CONTINUE_SEARCH;
PEXCEPTION_RECORD pexr = pexp->ExceptionRecord;
PBYTE pbAccAddr;
// We only handle access violations
if (pexr->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
return(lDisposition);
// Get the address of the attempted memory access
pbAccAddr = (PBYTE) pexr->ExceptionInformation[1];
// Raise an exception if the attempted access is in the overrun buffer.
if (InRange(pgmmf->pbFile + pgmmf->cbFileSizeMax, pbAccAddr,
pgmmf->pbFile + pgmmf->cbFileSizeMax + pgmmf->cbOverrunBuf - 1)) {
RaiseException(EXCEPTION_GMMF_WRITEPASTMAX,
EXCEPTION_NONCONTINUABLE, 0, NULL);
}
// We only handle access violations within the range of the GMMF
if (!InRange(pgmmf->pbFile, pbAccAddr,
pgmmf->pbFile + pgmmf->cbFileSizeMax - 1))
return(lDisposition);
// Grow the MMF so that it is large enough to contain the attempted
// memory access; then, retry the memory access.
GMMF_AdjustRegion(pgmmf, pbAccAddr - pgmmf->pbFile + 1);
return(EXCEPTION_CONTINUE_EXECUTION);
}
//////////////////////////////////////////////////////////////////////////////
// Closes a GMMF and truncates the file to the specified size.
void WINAPI GMMF_Close (PGMMF pgmmf, DWORD cbDiskFile) {
// Unmap the view of the file, close the file-mapping object and free the
// reserved address space region
GMMF_ClearRegion(pgmmf);
// Shrink the file to its required size
SetFilePointer(pgmmf->hFile, cbDiskFile, NULL, FILE_BEGIN);
SetEndOfFile(pgmmf->hFile);
// Clear all the members of the pgmmf structure for safety
ZeroMemory(pgmmf, sizeof(*pgmmf));
}
//////////////////////////////////////////////////////////////////////////////
// Clear the MMF and the reserved address space immediately following it.
static void WINAPI GMMF_ClearRegion (PGMMF pgmmf) {
MEMORY_BASIC_INFORMATION mbi;
// Get the state of memory at the base of thre GMMF region.
VirtualQuery(pgmmf->pbFile, &mbi, sizeof(mbi));
// The region will look like one of the following:
// 1. One reserved region
// 2. One committed region (file) and one reserved region
// 3. One committed region (file)
// I want the following instructions to execute without being pre-empted.
// I cannot guarantee this but I can help the situation by forcing the
// system to reschedule this thread now so that the following lines start
// out with a full time-slice. If the following lines take more than a
// time-slice to execute, we can only hope for the best.
Sleep(0);
if (mbi.State == MEM_RESERVE) {
// One reserved region.
GMMF_FreeAddrSpace(pgmmf, mbi.AllocationBase);
} else {
// A committed region
UnmapViewOfFile(pgmmf->pbFile);
if (mbi.RegionSize < pgmmf->cbFileSizeMax + pgmmf->cbOverrunBuf) {
// A reserved region after the committed region
GMMF_FreeAddrSpace(pgmmf,
(PBYTE) mbi.AllocationBase + mbi.RegionSize);
}
}
// Close the small disk file's file-mapping object
if (pgmmf->hFileMap != NULL) {
CloseHandle(pgmmf->hFileMap);
pgmmf->hFileMap = NULL;
}
}
//////////////////////////////////////////////////////////////////////////////
// Construct a region containing a MMF and a reserved address space region
// immediately following it.
static void WINAPI GMMF_ConstructRegion (PGMMF pgmmf, DWORD cbDiskFileNow) {
// Get the new size of the file (a multiple of the specified increment).
DWORD cbDiskFileNew = RoundUp(cbDiskFileNow, pgmmf->cbFileGrowInc);
PBYTE pbTemp;
if (cbDiskFileNew > 0) {
// Grow the MMF by creating a new file-mapping object.
pgmmf->hFileMap = CreateFileMapping(pgmmf->hFile, NULL,
PAGE_READWRITE, 0, cbDiskFileNew, NULL);
if (pgmmf->hFileMap == NULL) {
// File-mapping could not be created, the disk is probably full.
RaiseException(EXCEPTION_GMMF_DISKFULL,
EXCEPTION_NONCONTINUABLE, 0, NULL);
}
// Map the new MMF at the same location as the previously mapped view.
pbTemp = MapViewOfFileEx(pgmmf->hFileMap, FILE_MAP_ALL_ACCESS,
0, 0, 0, pgmmf->pbFile);
// Check to see if our region has been corrupted by another thread.
if (pbTemp != pgmmf->pbFile)
RaiseException(EXCEPTION_GMMF_CORRUPTEDRGN,
EXCEPTION_NONCONTINUABLE, 0, NULL);
}
if (cbDiskFileNew < pgmmf->cbFileSizeMax + pgmmf->cbOverrunBuf) {
// Reserve space after the GMMF for growth detection.
// The space should be reserved after the MMF and the size should be the
// maximum size of the file plus the overrun buffer size minus the
// current size of the MMF.
pbTemp = GMMF_ResAddrSpace(pgmmf, pgmmf->pbFile + cbDiskFileNew,
pgmmf->cbFileSizeMax + pgmmf->cbOverrunBuf - cbDiskFileNew);
// Check to see if our region has been corrupted by another thread.
if (pbTemp != (pgmmf->pbFile + cbDiskFileNew)) {
RaiseException(EXCEPTION_GMMF_CORRUPTEDRGN,
EXCEPTION_NONCONTINUABLE, 0, NULL);
}
}
}
//////////////////////////////////////////////////////////////////////////////
// This function adjusts the contents of the GMMF's memory region.
// It begins by clearing the contents of the region and then constructs the
// contents with the properly adjusted MMF and reserved region.
static void WINAPI GMMF_AdjustRegion (PGMMF pgmmf, DWORD cbDiskFileNow) {
int nThreadPriority;
#ifndef GMMF_WINNT_ONLY
// On Windows 95, we temporarily boost the process's prioirty class to
// realtime and restore it back to the original prioirity class later.
int nPriorityClass;
#endif
__try {
// Boost our thread's priority so that another thread is
// UNLIKELY to steal our address space while we're changing it.
nThreadPriority = GetThreadPriority(GetCurrentThread());
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
#ifndef GMMF_WINNT_ONLY
if (g_osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) {
// If we're running on Windows 95, boost our priority class
// too because MMFs are in a memory region shared by other
// Win32 processes.
nPriorityClass = GetPriorityClass(GetCurrentProcess());
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
}
#endif
// Step 1: Clear the contents of the region
GMMF_ClearRegion(pgmmf);
// Step 2: Restore the contents with the properly adjusted lengths
// NOTE: This function may raise exceptions that should be handled
// by the caller.
GMMF_ConstructRegion(pgmmf, cbDiskFileNow);
}
__finally {
// Make sure that we always restore our priority class and thread
// priority so that we do not continue to adversely affect other
// threads in the system.
#ifndef GMMF_WINNT_ONLY
if (g_osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) {
SetPriorityClass(GetCurrentProcess(), nPriorityClass);
}
#endif
SetThreadPriority(GetCurrentThread(), nThreadPriority);
}
}
//////////////////////////////// End of File /////////////////////////////////