Home | History | Annotate | Download | only in wtf
      1 // Copyright (c) 2005, 2007, Google Inc.
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are
      6 // met:
      7 //
      8 //     * Redistributions of source code must retain the above copyright
      9 // notice, this list of conditions and the following disclaimer.
     10 //     * Redistributions in binary form must reproduce the above
     11 // copyright notice, this list of conditions and the following disclaimer
     12 // in the documentation and/or other materials provided with the
     13 // distribution.
     14 //     * Neither the name of Google Inc. nor the names of its
     15 // contributors may be used to endorse or promote products derived from
     16 // this software without specific prior written permission.
     17 //
     18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 
     30 // ---
     31 // Author: Sanjay Ghemawat
     32 
     33 #include "config.h"
     34 #if !USE(SYSTEM_MALLOC)
     35 #include "TCSystemAlloc.h"
     36 
     37 #include "Assertions.h"
     38 #include "CheckedArithmetic.h"
     39 #include "TCSpinLock.h"
     40 #include "UnusedParam.h"
     41 #include "VMTags.h"
     42 #include <algorithm>
     43 #include <stdint.h>
     44 
     45 #if OS(WINDOWS)
     46 #include "windows.h"
     47 #else
     48 #include <errno.h>
     49 #include <unistd.h>
     50 #include <sys/mman.h>
     51 #endif
     52 
     53 #ifndef MAP_ANONYMOUS
     54 #define MAP_ANONYMOUS MAP_ANON
     55 #endif
     56 
     57 using namespace std;
     58 
     59 // Structure for discovering alignment
     60 union MemoryAligner {
     61   void*  p;
     62   double d;
     63   size_t s;
     64 };
     65 
     66 static SpinLock spinlock = SPINLOCK_INITIALIZER;
     67 
     68 // Page size is initialized on demand
     69 static size_t pagesize = 0;
     70 
     71 // Configuration parameters.
     72 
     73 #if HAVE(MMAP)
     74 static bool use_mmap = true;
     75 #endif
     76 
     77 #if HAVE(VIRTUALALLOC)
     78 static bool use_VirtualAlloc = true;
     79 #endif
     80 
     81 // Flags to keep us from retrying allocators that failed.
     82 static bool devmem_failure = false;
     83 static bool sbrk_failure = false;
     84 static bool mmap_failure = false;
     85 static bool VirtualAlloc_failure = false;
     86 
     87 static const int32_t FLAGS_malloc_devmem_start = 0;
     88 static const int32_t FLAGS_malloc_devmem_limit = 0;
     89 
     90 #if HAVE(MMAP)
     91 
     92 static void* TryMmap(size_t size, size_t *actual_size, size_t alignment) {
     93   // Enforce page alignment
     94   if (pagesize == 0) pagesize = getpagesize();
     95   if (alignment < pagesize) alignment = pagesize;
     96   size = ((size + alignment - 1) / alignment) * alignment;
     97 
     98   // could theoretically return the "extra" bytes here, but this
     99   // is simple and correct.
    100   if (actual_size)
    101     *actual_size = size;
    102 
    103   // Ask for extra memory if alignment > pagesize
    104   size_t extra = 0;
    105   if (alignment > pagesize) {
    106     extra = alignment - pagesize;
    107   }
    108   Checked<size_t> mapSize = Checked<size_t>(size) + extra + 2 * pagesize;
    109   void* result = mmap(NULL, mapSize.unsafeGet(),
    110                       PROT_READ | PROT_WRITE,
    111                       MAP_PRIVATE|MAP_ANONYMOUS,
    112                       VM_TAG_FOR_TCMALLOC_MEMORY, 0);
    113   if (result == reinterpret_cast<void*>(MAP_FAILED)) {
    114     mmap_failure = true;
    115     return NULL;
    116   }
    117   mmap(result, pagesize, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, VM_TAG_FOR_TCMALLOC_MEMORY, 0);
    118   mmap(static_cast<char*>(result) + (mapSize - pagesize).unsafeGet(), pagesize, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, VM_TAG_FOR_TCMALLOC_MEMORY, 0);
    119   result = static_cast<char*>(result) + pagesize;
    120   // Adjust the return memory so it is aligned
    121   uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
    122   size_t adjust = 0;
    123   if ((ptr & (alignment - 1)) != 0) {
    124     adjust = alignment - (ptr & (alignment - 1));
    125   }
    126 
    127   // Return the unused memory to the system
    128   if (adjust > 0) {
    129     munmap(reinterpret_cast<void*>(ptr), adjust);
    130   }
    131   if (adjust < extra) {
    132     munmap(reinterpret_cast<void*>(ptr + adjust + size), extra - adjust);
    133   }
    134 
    135   ptr += adjust;
    136   return reinterpret_cast<void*>(ptr);
    137 }
    138 
    139 #endif /* HAVE(MMAP) */
    140 
    141 #if HAVE(VIRTUALALLOC)
    142 
    143 static void* TryVirtualAlloc(size_t size, size_t *actual_size, size_t alignment) {
    144   // Enforce page alignment
    145   if (pagesize == 0) {
    146     SYSTEM_INFO system_info;
    147     GetSystemInfo(&system_info);
    148     pagesize = system_info.dwPageSize;
    149   }
    150 
    151   if (alignment < pagesize) alignment = pagesize;
    152   size = ((size + alignment - 1) / alignment) * alignment;
    153 
    154   // could theoretically return the "extra" bytes here, but this
    155   // is simple and correct.
    156   if (actual_size)
    157     *actual_size = size;
    158 
    159   // Ask for extra memory if alignment > pagesize
    160   size_t extra = 0;
    161   if (alignment > pagesize) {
    162     extra = alignment - pagesize;
    163   }
    164   void* result = VirtualAlloc(NULL, size + extra,
    165                               MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
    166                               PAGE_READWRITE);
    167 
    168   if (result == NULL) {
    169     VirtualAlloc_failure = true;
    170     return NULL;
    171   }
    172 
    173   // Adjust the return memory so it is aligned
    174   uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
    175   size_t adjust = 0;
    176   if ((ptr & (alignment - 1)) != 0) {
    177     adjust = alignment - (ptr & (alignment - 1));
    178   }
    179 
    180   // Return the unused memory to the system - we'd like to release but the best we can do
    181   // is decommit, since Windows only lets you free the whole allocation.
    182   if (adjust > 0) {
    183     VirtualFree(reinterpret_cast<void*>(ptr), adjust, MEM_DECOMMIT);
    184   }
    185   if (adjust < extra) {
    186     VirtualFree(reinterpret_cast<void*>(ptr + adjust + size), extra-adjust, MEM_DECOMMIT);
    187   }
    188 
    189   ptr += adjust;
    190   return reinterpret_cast<void*>(ptr);
    191 }
    192 
    193 #endif /* HAVE(MMAP) */
    194 
    195 void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size, size_t alignment) {
    196   // Discard requests that overflow
    197   if (size + alignment < size) return NULL;
    198 
    199   SpinLockHolder lock_holder(&spinlock);
    200 
    201   // Enforce minimum alignment
    202   if (alignment < sizeof(MemoryAligner)) alignment = sizeof(MemoryAligner);
    203 
    204   // Try twice, once avoiding allocators that failed before, and once
    205   // more trying all allocators even if they failed before.
    206   for (int i = 0; i < 2; i++) {
    207 
    208 #if HAVE(MMAP)
    209     if (use_mmap && !mmap_failure) {
    210       void* result = TryMmap(size, actual_size, alignment);
    211       if (result != NULL) return result;
    212     }
    213 #endif
    214 
    215 #if HAVE(VIRTUALALLOC)
    216     if (use_VirtualAlloc && !VirtualAlloc_failure) {
    217       void* result = TryVirtualAlloc(size, actual_size, alignment);
    218       if (result != NULL) return result;
    219     }
    220 #endif
    221 
    222     // nothing worked - reset failure flags and try again
    223     devmem_failure = false;
    224     sbrk_failure = false;
    225     mmap_failure = false;
    226     VirtualAlloc_failure = false;
    227   }
    228   return NULL;
    229 }
    230 
    231 #if HAVE(MADV_FREE_REUSE)
    232 
    233 void TCMalloc_SystemRelease(void* start, size_t length)
    234 {
    235     int madviseResult;
    236 
    237     while ((madviseResult = madvise(start, length, MADV_FREE_REUSABLE)) == -1 && errno == EAGAIN) { }
    238 
    239     // Although really advisory, if madvise fail, we want to know about it.
    240     ASSERT_UNUSED(madviseResult, madviseResult != -1);
    241 }
    242 
    243 #elif HAVE(MADV_FREE) || HAVE(MADV_DONTNEED)
    244 
    245 void TCMalloc_SystemRelease(void* start, size_t length)
    246 {
    247     // MADV_FREE clears the modified bit on pages, which allows
    248     // them to be discarded immediately.
    249 #if HAVE(MADV_FREE)
    250     const int advice = MADV_FREE;
    251 #else
    252     const int advice = MADV_DONTNEED;
    253 #endif
    254   if (FLAGS_malloc_devmem_start) {
    255     // It's not safe to use MADV_DONTNEED if we've been mapping
    256     // /dev/mem for heap memory
    257     return;
    258   }
    259   if (pagesize == 0) pagesize = getpagesize();
    260   const size_t pagemask = pagesize - 1;
    261 
    262   size_t new_start = reinterpret_cast<size_t>(start);
    263   size_t end = new_start + length;
    264   size_t new_end = end;
    265 
    266   // Round up the starting address and round down the ending address
    267   // to be page aligned:
    268   new_start = (new_start + pagesize - 1) & ~pagemask;
    269   new_end = new_end & ~pagemask;
    270 
    271   ASSERT((new_start & pagemask) == 0);
    272   ASSERT((new_end & pagemask) == 0);
    273   ASSERT(new_start >= reinterpret_cast<size_t>(start));
    274   ASSERT(new_end <= end);
    275 
    276   if (new_end > new_start) {
    277     // Note -- ignoring most return codes, because if this fails it
    278     // doesn't matter...
    279     while (madvise(reinterpret_cast<char*>(new_start), new_end - new_start,
    280                    advice) == -1 &&
    281            errno == EAGAIN) {
    282       // NOP
    283     }
    284   }
    285 }
    286 
    287 #elif HAVE(MMAP)
    288 
    289 void TCMalloc_SystemRelease(void* start, size_t length)
    290 {
    291   void* newAddress = mmap(start, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
    292   // If the mmap failed then that's ok, we just won't return the memory to the system.
    293   ASSERT_UNUSED(newAddress, newAddress == start || newAddress == reinterpret_cast<void*>(MAP_FAILED));
    294 }
    295 
    296 #elif HAVE(VIRTUALALLOC)
    297 
    298 void TCMalloc_SystemRelease(void* start, size_t length)
    299 {
    300     if (VirtualFree(start, length, MEM_DECOMMIT))
    301         return;
    302 
    303     // The decommit may fail if the memory region consists of allocations
    304     // from more than one call to VirtualAlloc.  In this case, fall back to
    305     // using VirtualQuery to retrieve the allocation boundaries and decommit
    306     // them each individually.
    307 
    308     char* ptr = static_cast<char*>(start);
    309     char* end = ptr + length;
    310     MEMORY_BASIC_INFORMATION info;
    311     while (ptr < end) {
    312         size_t resultSize = VirtualQuery(ptr, &info, sizeof(info));
    313         ASSERT_UNUSED(resultSize, resultSize == sizeof(info));
    314 
    315         size_t decommitSize = min<size_t>(info.RegionSize, end - ptr);
    316         BOOL success = VirtualFree(ptr, decommitSize, MEM_DECOMMIT);
    317         ASSERT_UNUSED(success, success);
    318         ptr += decommitSize;
    319     }
    320 }
    321 
    322 #else
    323 
    324 // Platforms that don't support returning memory use an empty inline version of TCMalloc_SystemRelease
    325 // declared in TCSystemAlloc.h
    326 
    327 #endif
    328 
    329 #if HAVE(MADV_FREE_REUSE)
    330 
    331 void TCMalloc_SystemCommit(void* start, size_t length)
    332 {
    333     while (madvise(start, length, MADV_FREE_REUSE) == -1 && errno == EAGAIN) { }
    334 }
    335 
    336 #elif HAVE(VIRTUALALLOC)
    337 
    338 void TCMalloc_SystemCommit(void* start, size_t length)
    339 {
    340     if (VirtualAlloc(start, length, MEM_COMMIT, PAGE_READWRITE) == start)
    341         return;
    342 
    343     // The commit may fail if the memory region consists of allocations
    344     // from more than one call to VirtualAlloc.  In this case, fall back to
    345     // using VirtualQuery to retrieve the allocation boundaries and commit them
    346     // each individually.
    347 
    348     char* ptr = static_cast<char*>(start);
    349     char* end = ptr + length;
    350     MEMORY_BASIC_INFORMATION info;
    351     while (ptr < end) {
    352         size_t resultSize = VirtualQuery(ptr, &info, sizeof(info));
    353         ASSERT_UNUSED(resultSize, resultSize == sizeof(info));
    354 
    355         size_t commitSize = min<size_t>(info.RegionSize, end - ptr);
    356         void* newAddress = VirtualAlloc(ptr, commitSize, MEM_COMMIT, PAGE_READWRITE);
    357         ASSERT_UNUSED(newAddress, newAddress == ptr);
    358         ptr += commitSize;
    359     }
    360 }
    361 
    362 #else
    363 
    364 // Platforms that don't need to explicitly commit memory use an empty inline version of TCMalloc_SystemCommit
    365 // declared in TCSystemAlloc.h
    366 
    367 #endif
    368 
    369 #endif // #if !USE(SYSTEM_MALLOC)
    370 
    371