Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reference counting issue of MapViewOfFile on Windows

It seems that MapViewOfFile increases the reference count of the file mapping kernel object.

Quoted from the MSDN description of MapViewOfFile:

Mapped views of a file mapping object maintain internal references to the object, and a file mapping object does not close until all references to it are released. Therefore, to fully close a file mapping object, an application must unmap all mapped views of the file mapping object by calling UnmapViewOfFile and close the file mapping object handle by calling CloseHandle. These functions can be called in any order.

Also, from Windows via C/C++, 5th Edition:

The preceding code shows the "expected" method for manipulating memory-mapped files. However, what it does not show is that the system increments the usage counts of the file object and the file-mapping object when you call MapViewOfFile...

Despite these, my actual test suggests the opposite. I'm using Visual Studio 2015 on Windows 10 64-bit. The test program is as follows:

#include <windows.h>

int main() {
  HANDLE h = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 128, "test");
  void* p_memory = MapViewOfFile(h, FILE_MAP_WRITE, 0, 0, 0);
  CloseHandle(h);
  h = OpenFileMappingA(FILE_MAP_WRITE, FALSE, "test");
  DWORD dw = GetLastError(); // ERROR_FILE_NOT_FOUND
}

The OpenFileMapping call failed with the last error ERROR_FILE_NOT_FOUND. When I remove the CloseHandle call, everything would be fine. This implies that the CloseHandle call eliminates the last reference count of the file mapping kernel object and destroys it. This in turn implies that MapViewOfFile does not actually increases the reference count of the object.

I want to make sure what is going on, and what is the exact semantics of MapViewOfFile with respect to reference counting of the file mapping kernel object.

like image 350
Lingxi Avatar asked Oct 31 '15 08:10

Lingxi


1 Answers

You can make it more convincing by using a file as the backing store instead of the paging file:

int main() {
    const char* path = "mmf.bin";
    DeleteFile(path);
    HANDLE hFile = CreateFile(path, GENERIC_READ | GENERIC_WRITE, 
        FILE_FLAG_DELETE_ON_CLOSE,
        NULL, CREATE_NEW, 0, NULL);
    HANDLE h = CreateFileMappingA(hFile, NULL, PAGE_READWRITE, 0, 128, "test");
    int* p_memory = (int*)MapViewOfFile(h, FILE_MAP_WRITE, 0, 0, 128);
    CloseHandle(h);
    DWORD attr = GetFileAttributes(path);
    if (attr != INVALID_FILE_ATTRIBUTES) puts("File still exists");
    else puts("File is gone");
}

Output: File is gone

So "the system increments the usage counts of the file object" is most definitely not correct. And I think you disproved that it increments the usage count on the file-mapping object. Not sure what to make of this, Richter doesn't get it wrong very often. Nothing in the errata for the book either. It might have worked this way in an earlier version of Windows, not sure since I never got this wrong on purpose. We'll have to stick with what the SDK documentation actually says:

A shared file mapping object will not be destroyed until all processes that use it close their handles to it by using the CloseHandle function.

like image 72
Hans Passant Avatar answered Oct 28 '22 01:10

Hans Passant