Bug 1299803 - Part 2: Clean up AllocateMappedContent and make it consistent across all OSes. r=sfink
authorEmanuel Hoogeveen <emanuel.hoogeveen@gmail.com>
Fri, 02 Sep 2016 08:09:00 -0400
changeset 313155 13af8367325a5de11a3b5e6c4694a1fd5f67a6a9
parent 313154 844899534edbfdf848bdded4e61197f24d8813c8
child 313156 5892d029f637358c6c337c6a0e55d11faa86c126
push id30673
push usercbook@mozilla.com
push dateThu, 08 Sep 2016 10:01:33 +0000
treeherdermozilla-central@938ce16be25f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1299803
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1299803 - Part 2: Clean up AllocateMappedContent and make it consistent across all OSes. r=sfink
js/src/gc/Memory.cpp
--- a/js/src/gc/Memory.cpp
+++ b/js/src/gc/Memory.cpp
@@ -275,67 +275,71 @@ size_t
 GetPageFaultCount()
 {
     PROCESS_MEMORY_COUNTERS pmc;
     if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
         return 0;
     return pmc.PageFaultCount;
 }
 
-// On Windows the minimum size for a mapping is the allocation granularity
-// (64KiB in practice), so mapping very small buffers is potentially wasteful.
 void*
 AllocateMappedContent(int fd, size_t offset, size_t length, size_t alignment)
 {
-    // The allocation granularity must be a whole multiple of the alignment and
-    // the caller must request an aligned offset to satisfy Windows' and the
-    // caller's alignment requirements.
+    MOZ_ASSERT(length && alignment);
+
+    // The allocation granularity and the requested offset
+    // must both be divisible by the requested alignment.
     if (allocGranularity % alignment != 0 || offset % alignment != 0)
         return nullptr;
 
-    // Make sure file exists and do sanity check for offset and size.
     HANDLE hFile = reinterpret_cast<HANDLE>(intptr_t(fd));
-    MOZ_ASSERT(hFile != INVALID_HANDLE_VALUE);
 
-    uint32_t fSizeHgh;
-    uint32_t fSizeLow = GetFileSize(hFile, LPDWORD(&fSizeHgh));
-    if (fSizeLow == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
-        return nullptr;
-
-    uint64_t fSize = (uint64_t(fSizeHgh) << 32) + fSizeLow;
-    if (offset >= size_t(fSize) || length == 0 || length > fSize - offset)
-        return nullptr;
-
-    uint64_t mapSize = length + offset;
-    HANDLE hMap = CreateFileMapping(hFile, nullptr, PAGE_READONLY, mapSize >> 32, mapSize, nullptr);
+    // This call will fail if the file does not exist, which is what we want.
+    HANDLE hMap = CreateFileMapping(hFile, nullptr, PAGE_READONLY, 0, 0, nullptr);
     if (!hMap)
         return nullptr;
 
-    // MapViewOfFile requires the offset to be a whole multiple of the
-    // allocation granularity.
-    size_t alignOffset = offset - (offset % allocGranularity);
-    size_t alignLength = length + (offset % allocGranularity);
-    void* map = MapViewOfFile(hMap, FILE_MAP_COPY, 0, alignOffset, alignLength);
+    size_t alignedOffset = offset - (offset % allocGranularity);
+    size_t alignedLength = length + (offset % allocGranularity);
+
+    DWORD offsetH = uint32_t(uint64_t(alignedOffset) >> 32);
+    DWORD offsetL = uint32_t(alignedOffset);
+
+    // If the offset or length are out of bounds, this call will fail.
+    uint8_t* map = static_cast<uint8_t*>(MapViewOfFile(hMap, FILE_MAP_COPY, offsetH,
+                                                       offsetL, alignedLength));
+
+    // This just decreases the file mapping object's internal reference count;
+    // it won't actually be destroyed until we unmap the associated view.
     CloseHandle(hMap);
+
     if (!map)
         return nullptr;
 
-    return reinterpret_cast<void*>(uintptr_t(map) + (offset - alignOffset));
+#ifdef DEBUG
+    // Zero out data before and after the desired mapping to catch errors early.
+    if (offset != alignedOffset)
+        memset(map, 0, offset - alignedOffset);
+    if (alignedLength % pageSize)
+        memset(map + alignedLength, 0, pageSize - (alignedLength % pageSize));
+#endif
+
+    return map + (offset - alignedOffset);
 }
 
 void
 DeallocateMappedContent(void* p, size_t /*length*/)
 {
     if (!p)
         return;
 
     // Calculate the address originally returned by MapViewOfFile.
-    // This is required because AllocateMappedContent returns a pointer that
-    // might be offset into the view, necessitated by the requirement that the
-    // beginning of a view must be aligned with the allocation granularity.
+    // This is needed because AllocateMappedContent returns a pointer
+    // that might be offset from the view, as the beginning of a
+    // view must be aligned with the allocation granularity.
     uintptr_t map = uintptr_t(p) - (uintptr_t(p) % allocGranularity);
     MOZ_ALWAYS_TRUE(UnmapViewOfFile(reinterpret_cast<void*>(map)));
 }
 
 #  else // Various APIs are unavailable.
 
 void*
 MapAlignedPages(size_t size, size_t alignment)
@@ -771,71 +775,60 @@ GetPageFaultCount()
     if (err)
         return 0;
     return usage.ru_majflt;
 }
 
 void*
 AllocateMappedContent(int fd, size_t offset, size_t length, size_t alignment)
 {
-#define NEED_PAGE_ALIGNED 0
-    size_t pa_start; // Page aligned starting
-    size_t pa_end; // Page aligned ending
-    size_t pa_size; // Total page aligned size
-    struct stat st;
-    uint8_t* buf;
+    MOZ_ASSERT(length && alignment);
 
-    // Make sure file exists and do sanity check for offset and size.
-    if (fstat(fd, &st) < 0 || offset >= (size_t) st.st_size ||
-        length == 0 || length > (size_t) st.st_size - offset)
+    // The allocation granularity and the requested offset
+    // must both be divisible by the requested alignment.
+    if (allocGranularity % alignment != 0 || offset % alignment != 0)
         return nullptr;
 
-    // Check for minimal alignment requirement.
-#if NEED_PAGE_ALIGNED
-    alignment = std::max(alignment, pageSize);
-#endif
-    if (offset & (alignment - 1))
+    // Sanity check the offset and size, as mmap does not do this for us.
+    struct stat st;
+    if (fstat(fd, &st) || offset >= uint64_t(st.st_size) || length > uint64_t(st.st_size) - offset)
         return nullptr;
 
-    // Page aligned starting of the offset.
-    pa_start = offset & ~(pageSize - 1);
-    // Calculate page aligned ending by adding one page to the page aligned
-    // starting of data end position(offset + length - 1).
-    pa_end = ((offset + length - 1) & ~(pageSize - 1)) + pageSize;
-    pa_size = pa_end - pa_start;
+    size_t alignedOffset = offset - (offset % allocGranularity);
+    size_t alignedLength = length + (offset % allocGranularity);
 
-    // Ask for a continuous memory location.
-    buf = (uint8_t*) MapMemory(pa_size);
-    if (!buf)
+    uint8_t* map = static_cast<uint8_t*>(MapMemory(alignedLength, PROT_READ | PROT_WRITE,
+                                                   MAP_PRIVATE, fd, alignedOffset));
+    if (!map)
         return nullptr;
 
-    buf = (uint8_t*) MapMemoryAt(buf, pa_size, PROT_READ | PROT_WRITE,
-                                  MAP_PRIVATE | MAP_FIXED, fd, pa_start);
-    if (!buf)
-        return nullptr;
+#ifdef DEBUG
+    // Zero out data before and after the desired mapping to catch errors early.
+    if (offset != alignedOffset)
+        memset(map, 0, offset - alignedOffset);
+    if (alignedLength % pageSize)
+        memset(map + alignedLength, 0, pageSize - (alignedLength % pageSize));
+#endif
 
-    // Reset the data before target file, which we don't need to see.
-    memset(buf, 0, offset - pa_start);
-
-    // Reset the data after target file, which we don't need to see.
-    memset(buf + (offset - pa_start) + length, 0, pa_end - (offset + length));
-
-    return buf + (offset - pa_start);
+    return map + (offset - alignedOffset);
 }
 
 void
 DeallocateMappedContent(void* p, size_t length)
 {
-    void* pa_start; // Page aligned starting
-    size_t total_size; // Total allocated size
+    if (!p)
+        return;
 
-    pa_start = (void*)(uintptr_t(p) & ~(pageSize - 1));
-    total_size = ((uintptr_t(p) + length) & ~(pageSize - 1)) + pageSize - uintptr_t(pa_start);
-    if (munmap(pa_start, total_size))
-        MOZ_ASSERT(errno == ENOMEM);
+    // Calculate the address originally returned by mmap.
+    // This is needed because AllocateMappedContent returns a pointer
+    // that might be offset from the mapping, as the beginning of a
+    // mapping must be aligned with the allocation granularity.
+    uintptr_t map = uintptr_t(p) - (uintptr_t(p) % allocGranularity);
+    size_t alignedLength = length + (uintptr_t(p) % allocGranularity);
+    UnmapPages(reinterpret_cast<void*>(map), alignedLength);
 }
 
 #else
 #error "Memory mapping functions are not defined for your OS."
 #endif
 
 void
 ProtectPages(void* p, size_t size)