Index: base/test_file_util_win.cc |
=================================================================== |
--- base/test_file_util_win.cc (revision 6152) |
+++ base/test_file_util_win.cc (working copy) |
@@ -13,31 +13,88 @@ |
namespace file_util { |
+// We could use GetSystemInfo to get the page size, but this serves |
+// our purpose fine since 4K is the page size on x86 as well as x64. |
+static const ptrdiff_t kPageSize = 4096; |
+ |
bool EvictFileFromSystemCache(const wchar_t* file) { |
// Request exclusive access to the file and overwrite it with no buffering. |
- ScopedHandle hfile( |
+ ScopedHandle file_handle( |
CreateFile(file, GENERIC_READ | GENERIC_WRITE, 0, NULL, |
- OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, |
- NULL)); |
- if (!hfile) |
+ OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL)); |
+ if (!file_handle) |
return false; |
+ // Get some attributes to restore later. |
+ BY_HANDLE_FILE_INFORMATION bhi = {0}; |
+ CHECK(::GetFileInformationByHandle(file_handle, &bhi)); |
+ |
// Execute in chunks. It could be optimized. We want to do few of these since |
- // these opterations will be slow without the cache. |
- char buffer[4096]; |
+ // these operations will be slow without the cache. |
+ |
+ // Non-buffered reads and writes need to be sector aligned and since sector |
+ // sizes typically range from 512-4096 bytes, we just use the page size. |
+ // The buffer size is twice the size of a page (minus one) since we need to |
+ // get an aligned pointer into the buffer that we can use. |
+ char buffer[2 * kPageSize - 1]; |
+ // Get an aligned pointer into buffer. |
+ char* read_write = reinterpret_cast<char*>( |
+ reinterpret_cast<ptrdiff_t>(buffer + kPageSize - 1) & ~(kPageSize - 1)); |
+ DCHECK((reinterpret_cast<int>(read_write) % kPageSize) == 0); |
+ |
+ // If the file size isn't a multiple of kPageSize, we'll need special |
+ // processing. |
+ bool file_is_page_aligned = true; |
int total_bytes = 0; |
- DWORD bytes_read; |
+ DWORD bytes_read, bytes_written; |
for (;;) { |
bytes_read = 0; |
- ReadFile(hfile, buffer, sizeof(buffer), &bytes_read, NULL); |
+ ReadFile(file_handle, read_write, kPageSize, &bytes_read, NULL); |
if (bytes_read == 0) |
break; |
- SetFilePointer(hfile, total_bytes, 0, FILE_BEGIN); |
- if (!WriteFile(hfile, buffer, bytes_read, &bytes_read, NULL)) |
+ if (bytes_read < kPageSize) { |
+ // Zero out the remaining part of the buffer. |
+ // WriteFile will fail if we provide a buffer size that isn't a |
+ // sector multiple, so we'll have to write the entire buffer with |
+ // padded zeros and then use SetEndOfFile to truncate the file. |
+ ZeroMemory(read_write + bytes_read, kPageSize - bytes_read); |
+ file_is_page_aligned = false; |
+ } |
+ |
+ // Move back to the position we just read from. |
+ // Note that SetFilePointer will also fail if total_bytes isn't sector |
+ // aligned, but that shouldn't happen here. |
+ DCHECK((total_bytes % kPageSize) == 0); |
+ SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN); |
+ if (!WriteFile(file_handle, read_write, kPageSize, &bytes_written, NULL) || |
+ bytes_written != kPageSize) { |
+ DCHECK(false); |
return false; |
+ } |
+ |
total_bytes += bytes_read; |
+ |
+ // If this is false, then we just processed the last portion of the file. |
+ if (!file_is_page_aligned) |
+ break; |
} |
+ |
+ if (!file_is_page_aligned) { |
+ // The size of the file isn't a multiple of the page size, so we'll have |
+ // to open the file again, this time without the FILE_FLAG_NO_BUFFERING |
+ // flag and use SetEndOfFile to mark EOF. |
+ file_handle.Set(CreateFile(file, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, |
+ 0, NULL)); |
+ CHECK(SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN) != |
+ INVALID_SET_FILE_POINTER); |
+ CHECK(::SetEndOfFile(file_handle)); |
+ } |
+ |
+ // Restore the file attributes. |
+ CHECK(::SetFileTime(file_handle, &bhi.ftCreationTime, &bhi.ftLastAccessTime, |
+ &bhi.ftLastWriteTime)); |
+ |
return true; |
} |