Challenge: Giving file system developer ulcer
I’m trying to reason about the behavior of this code, and I can’t decide if this is a stroke of genius or if I’m suffering from a stroke. Take a look at the code, and then I’ll discuss what I’m trying to do below:HANDLE hFile = CreateFileA("R:/original_file.bin", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { printf("Error creating file: %d\n", GetLastError()); exit(__LINE__); } HANDLE hMapFile = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL); if (hMapFile == NULL) { fprintf(stderr, "Could not create file mapping object: %x\n", GetLastError()); exit(__LINE__); } char* lpMapAddress = MapViewOfFile(hMapFile, FILE_MAP_WRITE, 0, 0, 0); if (lpMapAddress == NULL) { fprintf(stderr, "Could not map view of file: %x\n", GetLastError()); exit(__LINE__); } for (size_t i = 2 * MB; i
I’m trying to reason about the behavior of this code, and I can’t decide if this is a stroke of genius or if I’m suffering from a stroke. Take a look at the code, and then I’ll discuss what I’m trying to do below:
HANDLE hFile = CreateFileA("R:/original_file.bin",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("Error creating file: %d\n", GetLastError());
exit(__LINE__);
}
HANDLE hMapFile = CreateFileMapping(hFile, NULL,
PAGE_READWRITE, 0, 0, NULL);
if (hMapFile == NULL) {
fprintf(stderr, "Could not create file mapping object: %x\n", GetLastError());
exit(__LINE__);
}
char* lpMapAddress = MapViewOfFile(hMapFile, FILE_MAP_WRITE, 0, 0, 0);
if (lpMapAddress == NULL) {
fprintf(stderr, "Could not map view of file: %x\n", GetLastError());
exit(__LINE__);
}
for (size_t i = 2 * MB; i < 4 * MB; i++)
{
lpMapAddress[i]++;
}
HANDLE hDirect = CreateFileA("R:/original_file.bin",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
SetFilePointerEx(hDirect, (LARGE_INTEGER) { 6 * MB }, & fileSize, FILE_BEGIN);
for (i = 6 ; i < 10 ; i++) {
if (!WriteFile(hDirect, lpMapAddress + i * MB, MB, &bytesWritten, NULL)) {
fprintf(stderr, "WriteFile direct failed on iteration %d: %x\n", i, GetLastError());
exit(__LINE__);
}
}
The idea is pretty simple, I’m opening the same file twice. Once in buffered mode and mapping that memory for both reads & writes. The problem is that to flush the data to disk, I have to either wait for the OS, or call FlushViewOfFile()
and FlushFileBuffers()
to actually flush it to disk explicitly.
The problem with this approach is that FlushFileBuffers()
has undesirable side effects. So I’m opening the file again, this time for unbuffered I/O. I’m writing to the memory map and then using the same mapping to write to the file itself. On Windows, that goes through a separate path (and may lose coherence with the memory map).
The idea here is that since I’m writing from the same location, I can’t lose coherence. I either get the value from the file or from the memory map, and they are both the same. At least, that is what I hope will happen.
For the purpose of discussion, I can ensure that there is no one else writing to this file while I’m abusing the system in this manner. What do you think Windows will do in this case?
I believe that when I’m writing using unbuffered I/O in this manner, I’m forcing the OS to drop the mapping and refresh from the disk. That is likely the reason why it may lose coherence, because there may be already reads that aren’t served from main memory, or something like that.
This isn’t an approach that I would actually take for production usage, but it is a damn interesting thing to speculate on. If you have any idea what will actually happen, I would love to have your input.