home *** CD-ROM | disk | FTP | other *** search
/ Troubleshooting Netware Systems / CSTRIAL0196.BIN / attach / msj / v10n10 / gmmfsrc.exe / GMMF.C next >
C/C++ Source or Header  |  1995-10-01  |  14KB  |  382 lines

  1. /*****************************************************************************
  2. Module name: GMMF.c
  3. Written by: Jeffrey Richter
  4. Purpose: Implementation of growable memory-mapped files.
  5. *****************************************************************************/
  6.  
  7.  
  8. #define STRICT
  9. #include <windows.h>
  10. #include <windowsx.h>
  11. #pragma warning(disable: 4001)   /* single line comment */
  12.  
  13. #include "GMMF.h"
  14.  
  15.  
  16. //////////////////////////////////////////////////////////////////////////////
  17.  
  18.  
  19. // Internal functions implemented at the end of this file.
  20.  
  21.  
  22. // Clear the MMF and the reserved address space immediately following it.
  23. static void WINAPI GMMF_ClearRegion (PGMMF pgmmf);
  24.  
  25.  
  26. // Construct a region containing a MMF and a reserved address space region 
  27. // immediately following it.
  28. static void WINAPI GMMF_ConstructRegion (PGMMF pgmmf, DWORD cbDiskFileNow);
  29.  
  30.  
  31. // This function adjusts the contents of the GMMF's memory region.
  32. // It begins by clearing the contents of the region and then constructs the 
  33. // contents with the properly adjusted MMF and reserved region.
  34. static void WINAPI GMMF_AdjustRegion (PGMMF pgmmf, DWORD cbDiskFileNow);
  35.  
  36.  
  37. //////////////////////////////////////////////////////////////////////////////
  38.  
  39.  
  40. // This algorithm require knowledge of the system information.  In particular,
  41. // the allocation-granularity (and possibly the page size) is used.  Since 
  42. // this information never changes, it can be obtained once (in GMMF_Create) 
  43. //.and saved in this global variable for later reference.
  44. static SYSTEM_INFO   g_sinf;
  45.  
  46. #ifndef GMMF_WINNT_ONLY
  47. // There are some differences between Windows NT and Windows 95. This global
  48. // variable tells us which OS platofmr the code is executing on.
  49. static OSVERSIONINFO g_osvi;
  50. #endif
  51.  
  52.  
  53. //////////////////////////////////////////////////////////////////////////////
  54.  
  55.  
  56. // This algorithm must reserve address space immediately following a MMF.  
  57. // Normally, we would use VirtualAlloc to do this but Windows 95 does not 
  58. // allow VirtualAlloc to reserve address space between 0x80000000 and 
  59. // 0xBFFFFFFF.  However, we can create a MMF with the SEC_RESERVE flag to
  60. // reserve address space in the this memory range.  The following macros
  61. // abstract the reserving/releasing of address space for the two OS platforms.
  62.  
  63. #ifdef GMMF_WINNT_ONLY
  64. // If Windows NT only, use VirtualAlloc/VirtualFree functions.
  65.  
  66. #define GMMF_ResAddrSpace(pgmmf, pvAddress, cbSize)  \
  67.    VirtualAlloc(pvAddress, cbSize, MEM_RESERVE, PAGE_NOACCESS)
  68.  
  69. #define GMMF_FreeAddrSpace(pgmmf, pvAddress)  \
  70.    VirtualFree(pvAddress, 0, MEM_RELEASE)
  71.  
  72. #else
  73.  
  74. // If Windows NT or Windows 95, use MMFs with SEC_RESERVE.
  75.  
  76. // This define is passed to CreateFileMapping in order to create
  77. // a file mapping that is backed by the system's paging file(s).
  78. #define HFILE_PAGEFILE  ((HANDLE) 0xffffffff)
  79.  
  80. #define GMMF_ResAddrSpace(pgmmf, pvAddress, cbSize)  \
  81.    (pgmmf->hFileMapRes = CreateFileMapping(HFILE_PAGEFILE, NULL,             \
  82.       PAGE_READONLY | SEC_RESERVE, 0, cbSize, NULL),                         \
  83.    (pgmmf->hFileMapRes == NULL) ? NULL :                                     \
  84.       MapViewOfFileEx(pgmmf->hFileMapRes, FILE_MAP_READ, 0, 0, 0, pvAddress))
  85.  
  86. #define GMMF_FreeAddrSpace(pgmmf, pvAddress)  \
  87.    (UnmapViewOfFile(pvAddress) && CloseHandle(pgmmf->hFileMapRes))
  88.  
  89. #endif
  90.  
  91.  
  92. //////////////////////////////////////////////////////////////////////////////
  93.  
  94.  
  95. // Creates a GMMF and places it in the process's address space.  Note that 
  96. // the cbFileSizeMax and cbFileGrowInc values are rounded up to an even 
  97. // allocation-granularity boundary.  The return value is the address of the
  98. // GMMF or NULL if the GMMF could not be created.
  99. PVOID WINAPI GMMF_Create (PGMMF pgmmf, HANDLE hFile, DWORD cbFileSizeMax, 
  100.    DWORD cbFileGrowInc, DWORD cbOverrunBuf) {
  101.  
  102.    DWORD cbMaxRgnSize;
  103.  
  104.    if (g_sinf.dwAllocationGranularity == 0) {
  105.  
  106.       // See the comment assocaited with the g_sinf and g_osvi 
  107.       // variables above.
  108.       GetSystemInfo(&g_sinf);
  109.  
  110. #ifndef GMMF_WINNT_ONLY
  111.       // Sometimes we need to know if we're on Windows 95 or Windows NT
  112.       g_osvi.dwOSVersionInfoSize = sizeof(g_osvi);
  113.       GetVersionEx(&g_osvi);
  114. #endif
  115.    }
  116.  
  117.    // Clear all the members of the pgmmf structure
  118.    ZeroMemory(pgmmf, sizeof(*pgmmf));
  119.  
  120.    // Initialize the members with the values passed to this function
  121.    // NOTE: The maximum file size and the file grow increment are rounded up 
  122.    // to the next allocation granularity boundary.
  123.    pgmmf->cbFileSizeMax = RoundUp(cbFileSizeMax, 
  124.       g_sinf.dwAllocationGranularity);
  125.    pgmmf->cbFileGrowInc = RoundUp(cbFileGrowInc, 
  126.       g_sinf.dwAllocationGranularity);
  127.    pgmmf->cbOverrunBuf  = cbOverrunBuf;
  128.    pgmmf->hFile = hFile;
  129.  
  130.    // Reserve an address space region that is big enough to hold the GMMF
  131.    // assuming that it grew to its maximum size and the overrun buffer area.
  132.    cbMaxRgnSize = pgmmf->cbFileSizeMax + cbOverrunBuf;
  133. #ifdef GMMF_WINNT_ONLY
  134.    pgmmf->pbFile = GMMF_ResAddrSpace(pgmmf, NULL, cbMaxRgnSize);
  135. #else
  136.    if (g_osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) {
  137.  
  138.       // Windows 95 maps MMFs using page granularity rather than allocation
  139.       // granularity.  We must force allocation-granularity so we will reserve
  140.       // (AllocGran minus PageSize) more space than we have to and round the
  141.       // memory address up to an allocation-granularity boundary.  Finally,
  142.       // we reserve the exact amount we need at the AllocGran memory address.
  143.       PBYTE pbTemp = GMMF_ResAddrSpace(pgmmf, NULL, cbMaxRgnSize +
  144.          g_sinf.dwAllocationGranularity - g_sinf.dwPageSize);
  145.       pgmmf->pbFile = (PBYTE) 
  146.          RoundUp((DWORD) pbTemp, g_sinf.dwAllocationGranularity);
  147.  
  148.       // Try to make the next two statements execute without being pre-empted.
  149.       Sleep(0);
  150.       GMMF_FreeAddrSpace(pgmmf, pbTemp);
  151.       pgmmf->pbFile = GMMF_ResAddrSpace(pgmmf, pgmmf->pbFile, cbMaxRgnSize);
  152.    } else {
  153.  
  154.       // Windows NT guarantess allocation granularity so we don't 
  155.       // have to force it.
  156.       pgmmf->pbFile = GMMF_ResAddrSpace(pgmmf, NULL, cbMaxRgnSize);
  157.    }
  158. #endif
  159.  
  160.    // Adjust the GMMF region so that it contains a MMF backed by the disk 
  161.    // file's storage and a reserved region (up to the maximum size of the 
  162.    // GMMF specified) immediately following the MMF.
  163.    GMMF_AdjustRegion(pgmmf, GetFileSize(hFile, NULL));
  164.  
  165.    // Return the base address of the GMMF
  166.    return(pgmmf->pbFile);
  167. }
  168.  
  169.  
  170. //////////////////////////////////////////////////////////////////////////////
  171.  
  172.  
  173. // SEH Exception filter used to grow the GMMF.
  174. long WINAPI GMMF_ExcFilter (PEXCEPTION_POINTERS pexp, PGMMF pgmmf) {
  175.  
  176.    // Assume that we don't know how to handle the exception
  177.    DWORD lDisposition = EXCEPTION_CONTINUE_SEARCH;
  178.  
  179.    PEXCEPTION_RECORD pexr = pexp->ExceptionRecord;
  180.    PBYTE pbAccAddr;
  181.  
  182.    // We only handle access violations
  183.    if (pexr->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) 
  184.       return(lDisposition);
  185.  
  186.    // Get the address of the attempted memory access
  187.    pbAccAddr = (PBYTE) pexr->ExceptionInformation[1];
  188.  
  189.    // Raise an exception if the attempted access is in the overrun buffer.
  190.    if (InRange(pgmmf->pbFile + pgmmf->cbFileSizeMax, pbAccAddr, 
  191.          pgmmf->pbFile + pgmmf->cbFileSizeMax + pgmmf->cbOverrunBuf - 1)) {
  192.       RaiseException(EXCEPTION_GMMF_WRITEPASTMAX, 
  193.          EXCEPTION_NONCONTINUABLE, 0, NULL);
  194.    }
  195.  
  196.    // We only handle access violations within the range of the GMMF
  197.    if (!InRange(pgmmf->pbFile, pbAccAddr, 
  198.          pgmmf->pbFile + pgmmf->cbFileSizeMax - 1))
  199.       return(lDisposition);
  200.  
  201.    // Grow the MMF so that it is large enough to contain the attempted
  202.    // memory access; then, retry the memory access.
  203.    GMMF_AdjustRegion(pgmmf, pbAccAddr - pgmmf->pbFile + 1);
  204.    return(EXCEPTION_CONTINUE_EXECUTION);
  205. }
  206.  
  207.  
  208. //////////////////////////////////////////////////////////////////////////////
  209.  
  210.  
  211. // Closes a GMMF and truncates the file to the specified size.
  212. void WINAPI GMMF_Close (PGMMF pgmmf, DWORD cbDiskFile) {
  213.  
  214.    // Unmap the view of the file, close the file-mapping object and free the
  215.    // reserved address space region
  216.    GMMF_ClearRegion(pgmmf);
  217.  
  218.    // Shrink the file to its required size
  219.    SetFilePointer(pgmmf->hFile, cbDiskFile, NULL, FILE_BEGIN);
  220.    SetEndOfFile(pgmmf->hFile);
  221.  
  222.    // Clear all the members of the pgmmf structure for safety
  223.    ZeroMemory(pgmmf, sizeof(*pgmmf));
  224. }
  225.  
  226.  
  227. //////////////////////////////////////////////////////////////////////////////
  228.  
  229.  
  230. // Clear the MMF and the reserved address space immediately following it.
  231. static void WINAPI GMMF_ClearRegion (PGMMF pgmmf) {
  232.  
  233.    MEMORY_BASIC_INFORMATION mbi;
  234.  
  235.    // Get the state of memory at the base of thre GMMF region.
  236.    VirtualQuery(pgmmf->pbFile, &mbi, sizeof(mbi));
  237.  
  238.    // The region will look like one of the following:
  239.    // 1. One reserved region
  240.    // 2. One committed region (file) and one reserved region
  241.    // 3. One committed region (file)
  242.  
  243.    // I want the following instructions to execute without being pre-empted.  
  244.    // I cannot guarantee this but I can help the situation by forcing the 
  245.    // system to reschedule this thread now so that the following lines start 
  246.    // out with a full time-slice.  If the following lines take more than a 
  247.    // time-slice to execute, we can only hope for the best.
  248.    Sleep(0);
  249.  
  250.    if (mbi.State == MEM_RESERVE) {
  251.  
  252.       // One reserved region.
  253.       GMMF_FreeAddrSpace(pgmmf, mbi.AllocationBase);
  254.    } else {
  255.  
  256.       // A committed region
  257.       UnmapViewOfFile(pgmmf->pbFile);
  258.       if (mbi.RegionSize < pgmmf->cbFileSizeMax + pgmmf->cbOverrunBuf) {
  259.  
  260.          // A reserved region after the committed region
  261.          GMMF_FreeAddrSpace(pgmmf, 
  262.             (PBYTE) mbi.AllocationBase + mbi.RegionSize);
  263.       }
  264.    }
  265.  
  266.    // Close the small disk file's file-mapping object
  267.    if (pgmmf->hFileMap != NULL) {
  268.  
  269.       CloseHandle(pgmmf->hFileMap);
  270.       pgmmf->hFileMap = NULL;
  271.    }
  272. }
  273.  
  274.  
  275. //////////////////////////////////////////////////////////////////////////////
  276.  
  277.  
  278. // Construct a region containing a MMF and a reserved address space region 
  279. // immediately following it.
  280. static void WINAPI GMMF_ConstructRegion (PGMMF pgmmf, DWORD cbDiskFileNow) {
  281.  
  282.    // Get the new size of the file (a multiple of the specified increment).
  283.    DWORD cbDiskFileNew = RoundUp(cbDiskFileNow, pgmmf->cbFileGrowInc);
  284.    PBYTE pbTemp;
  285.  
  286.    if (cbDiskFileNew > 0) {
  287.  
  288.       // Grow the MMF by creating a new file-mapping object.
  289.       pgmmf->hFileMap = CreateFileMapping(pgmmf->hFile, NULL, 
  290.          PAGE_READWRITE, 0, cbDiskFileNew, NULL);
  291.       if (pgmmf->hFileMap == NULL) {
  292.          // File-mapping could not be created, the disk is probably full.
  293.          RaiseException(EXCEPTION_GMMF_DISKFULL, 
  294.             EXCEPTION_NONCONTINUABLE, 0, NULL);
  295.       }
  296.  
  297.       // Map the new MMF at the same location as the previously mapped view.
  298.       pbTemp = MapViewOfFileEx(pgmmf->hFileMap, FILE_MAP_ALL_ACCESS, 
  299.          0, 0, 0, pgmmf->pbFile);
  300.  
  301.       // Check to see if our region has been corrupted by another thread.
  302.       if (pbTemp != pgmmf->pbFile)
  303.          RaiseException(EXCEPTION_GMMF_CORRUPTEDRGN, 
  304.             EXCEPTION_NONCONTINUABLE, 0, NULL);
  305.    }
  306.  
  307.    if (cbDiskFileNew < pgmmf->cbFileSizeMax + pgmmf->cbOverrunBuf) {
  308.  
  309.       // Reserve space after the GMMF for growth detection.
  310.  
  311.       // The space should be reserved after the MMF and the size should be the 
  312.       // maximum size of the file plus the overrun buffer size minus the 
  313.       // current size of the MMF.
  314.       pbTemp = GMMF_ResAddrSpace(pgmmf, pgmmf->pbFile + cbDiskFileNew, 
  315.          pgmmf->cbFileSizeMax + pgmmf->cbOverrunBuf - cbDiskFileNew);
  316.  
  317.       // Check to see if our region has been corrupted by another thread.
  318.       if (pbTemp != (pgmmf->pbFile + cbDiskFileNew)) {
  319.          RaiseException(EXCEPTION_GMMF_CORRUPTEDRGN, 
  320.             EXCEPTION_NONCONTINUABLE, 0, NULL);
  321.       }
  322.    }
  323. }
  324.  
  325.  
  326. //////////////////////////////////////////////////////////////////////////////
  327.  
  328.  
  329. // This function adjusts the contents of the GMMF's memory region.
  330. // It begins by clearing the contents of the region and then constructs the 
  331. // contents with the properly adjusted MMF and reserved region.
  332. static void WINAPI GMMF_AdjustRegion (PGMMF pgmmf, DWORD cbDiskFileNow) {
  333.  
  334.    int nThreadPriority;
  335. #ifndef GMMF_WINNT_ONLY
  336.    // On Windows 95, we temporarily boost the process's prioirty class to
  337.    // realtime and restore it back to the original prioirity class later.
  338.    int nPriorityClass;
  339. #endif
  340.  
  341.    __try {
  342.       
  343.       // Boost our thread's priority so that another thread is 
  344.       // UNLIKELY to steal our address space while we're changing it.
  345.       nThreadPriority = GetThreadPriority(GetCurrentThread());
  346.       SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
  347.  
  348. #ifndef GMMF_WINNT_ONLY
  349.       if (g_osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) {
  350.          // If we're running on Windows 95, boost our priority class 
  351.          // too because MMFs are in a memory region shared by other 
  352.          // Win32 processes.
  353.          nPriorityClass = GetPriorityClass(GetCurrentProcess());
  354.          SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
  355.       }
  356. #endif
  357.  
  358.       // Step 1: Clear the contents of the region
  359.       GMMF_ClearRegion(pgmmf);
  360.  
  361.       // Step 2: Restore the contents with the properly adjusted lengths
  362.       // NOTE: This function may raise exceptions that should be handled 
  363.       // by the caller.
  364.       GMMF_ConstructRegion(pgmmf, cbDiskFileNow);
  365.    }
  366.    __finally {
  367.  
  368.       // Make sure that we always restore our priority class and thread 
  369.       // priority so that we do not continue to adversely affect other 
  370.       // threads in the system.
  371. #ifndef GMMF_WINNT_ONLY
  372.       if (g_osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) {
  373.          SetPriorityClass(GetCurrentProcess(), nPriorityClass);
  374.       }
  375. #endif
  376.       SetThreadPriority(GetCurrentThread(), nThreadPriority);
  377.    }
  378. }
  379.  
  380.  
  381. //////////////////////////////// End of File /////////////////////////////////
  382.