// Author: Sanjay Ghemawat
#include "config.h"
+#include "TCSystemAlloc.h"
+
+#include <algorithm>
+#include <fcntl.h>
+#include "Assertions.h"
+#include "TCSpinLock.h"
+#include "UnusedParam.h"
+#include "VMTags.h"
+
#if HAVE(STDINT_H)
#include <stdint.h>
#elif HAVE(INTTYPES_H)
#else
#include <sys/types.h>
#endif
-#if PLATFORM(WIN_OS)
+
+#if OS(WINDOWS)
#include "windows.h"
#else
#include <errno.h>
#include <unistd.h>
#include <sys/mman.h>
#endif
-#include <fcntl.h>
-#include "Assertions.h"
-#include "TCSystemAlloc.h"
-#include "TCSpinLock.h"
-#include "UnusedParam.h"
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
-#if PLATFORM(DARWIN) && defined(VM_MEMORY_TCMALLOC)
-static const int mmapFileDescriptor = VM_MAKE_TAG(VM_MEMORY_TCMALLOC);
-#else
-static const int mmapFileDescriptor = -1;
-#endif
+using namespace std;
// Structure for discovering alignment
union MemoryAligner {
extra = alignment - pagesize;
}
void* result = mmap(NULL, size + extra,
- PROT_READ|PROT_WRITE,
+ PROT_READ | PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS,
- mmapFileDescriptor, 0);
+ VM_TAG_FOR_TCMALLOC_MEMORY, 0);
if (result == reinterpret_cast<void*>(MAP_FAILED)) {
mmap_failure = true;
return NULL;
devmem_failure = true;
return NULL;
}
- void *result = mmap(0, size + extra, PROT_WRITE|PROT_READ,
+ void *result = mmap(0, size + extra, PROT_READ | PROT_WRITE,
MAP_SHARED, physmem_fd, physmem_base);
if (result == reinterpret_cast<void*>(MAP_FAILED)) {
devmem_failure = true;
return NULL;
}
+#if HAVE(MADV_FREE_REUSE)
+
+void TCMalloc_SystemRelease(void* start, size_t length)
+{
+ while (madvise(start, length, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN) { }
+}
+
+#elif HAVE(MADV_FREE) || HAVE(MADV_DONTNEED)
+
void TCMalloc_SystemRelease(void* start, size_t length)
{
- UNUSED_PARAM(start);
- UNUSED_PARAM(length);
-#if HAVE(MADV_DONTNEED)
+ // MADV_FREE clears the modified bit on pages, which allows
+ // them to be discarded immediately.
+#if HAVE(MADV_FREE)
+ const int advice = MADV_FREE;
+#else
+ const int advice = MADV_DONTNEED;
+#endif
if (FLAGS_malloc_devmem_start) {
// It's not safe to use MADV_DONTNEED if we've been mapping
// /dev/mem for heap memory
if (new_end > new_start) {
// Note -- ignoring most return codes, because if this fails it
// doesn't matter...
- // The msync call with MS_KILLPAGES on Leopard is equivalent to an
- // madvise call with MADV_FREE on SnowLeopard
- while (msync(reinterpret_cast<char*>(new_start), new_end - new_start,
- MS_KILLPAGES) == -1 &&
+ while (madvise(reinterpret_cast<char*>(new_start), new_end - new_start,
+ advice) == -1 &&
errno == EAGAIN) {
// NOP
}
- return;
}
-#endif
+}
-#if HAVE(MMAP)
- void *newAddress = mmap(start, length, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, mmapFileDescriptor, 0);
- UNUSED_PARAM(newAddress);
+#elif HAVE(MMAP)
+
+void TCMalloc_SystemRelease(void* start, size_t length)
+{
+ void* newAddress = mmap(start, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
// If the mmap failed then that's ok, we just won't return the memory to the system.
- ASSERT(newAddress == start || newAddress == reinterpret_cast<void*>(MAP_FAILED));
- return;
+ ASSERT_UNUSED(newAddress, newAddress == start || newAddress == reinterpret_cast<void*>(MAP_FAILED));
+}
+
+#elif HAVE(VIRTUALALLOC)
+
+void TCMalloc_SystemRelease(void* start, size_t length)
+{
+ if (VirtualFree(start, length, MEM_DECOMMIT))
+ return;
+
+ // The decommit may fail if the memory region consists of allocations
+ // from more than one call to VirtualAlloc. In this case, fall back to
+ // using VirtualQuery to retrieve the allocation boundaries and decommit
+ // them each individually.
+
+ char* ptr = static_cast<char*>(start);
+ char* end = ptr + length;
+ MEMORY_BASIC_INFORMATION info;
+ while (ptr < end) {
+ size_t resultSize = VirtualQuery(ptr, &info, sizeof(info));
+ ASSERT_UNUSED(resultSize, resultSize == sizeof(info));
+
+ size_t decommitSize = min<size_t>(info.RegionSize, end - ptr);
+ BOOL success = VirtualFree(ptr, decommitSize, MEM_DECOMMIT);
+ ASSERT_UNUSED(success, success);
+ ptr += decommitSize;
+ }
+}
+
+#else
+
+// Platforms that don't support returning memory use an empty inline version of TCMalloc_SystemRelease
+// declared in TCSystemAlloc.h
+
#endif
+
+#if HAVE(MADV_FREE_REUSE)
+
+void TCMalloc_SystemCommit(void* start, size_t length)
+{
+ while (madvise(start, length, MADV_FREE_REUSE) == -1 && errno == EAGAIN) { }
}
+
+#elif HAVE(VIRTUALALLOC)
+
+void TCMalloc_SystemCommit(void* start, size_t length)
+{
+ if (VirtualAlloc(start, length, MEM_COMMIT, PAGE_READWRITE) == start)
+ return;
+
+ // The commit may fail if the memory region consists of allocations
+ // from more than one call to VirtualAlloc. In this case, fall back to
+ // using VirtualQuery to retrieve the allocation boundaries and commit them
+ // each individually.
+
+ char* ptr = static_cast<char*>(start);
+ char* end = ptr + length;
+ MEMORY_BASIC_INFORMATION info;
+ while (ptr < end) {
+ size_t resultSize = VirtualQuery(ptr, &info, sizeof(info));
+ ASSERT_UNUSED(resultSize, resultSize == sizeof(info));
+
+ size_t commitSize = min<size_t>(info.RegionSize, end - ptr);
+ void* newAddress = VirtualAlloc(ptr, commitSize, MEM_COMMIT, PAGE_READWRITE);
+ ASSERT_UNUSED(newAddress, newAddress == ptr);
+ ptr += commitSize;
+ }
+}
+
+#else
+
+// Platforms that don't need to explicitly commit memory use an empty inline version of TCMalloc_SystemCommit
+// declared in TCSystemAlloc.h
+
+#endif