diff options
Diffstat (limited to 'third_party/tcmalloc/chromium/src/pagemap.h')
-rw-r--r-- | third_party/tcmalloc/chromium/src/pagemap.h | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/third_party/tcmalloc/chromium/src/pagemap.h b/third_party/tcmalloc/chromium/src/pagemap.h index 3559932..0d78991 100644 --- a/third_party/tcmalloc/chromium/src/pagemap.h +++ b/third_party/tcmalloc/chromium/src/pagemap.h @@ -53,6 +53,12 @@ #else #include <sys/types.h> #endif +#ifdef WIN32 +// TODO(jar): This is not needed when TCMalloc_PageMap1_LazyCommit has an API +// supporting commit and reservation of memory. +#include "common.h" +#endif + #include "internal_logging.h" // Single-level array @@ -101,6 +107,192 @@ class TCMalloc_PageMap1 { } }; +#ifdef WIN32 +// Lazy commit, single-level array. +// Very similar to PageMap1, except the page map is only committed as needed. +// Since we don't return memory to the OS, the committed portion of the map will +// only grow, and we'll only be called to Ensure when we really grow the heap. +// We maintain a bit map to help us deduce if we've already committed a range +// in our map. +template <int BITS> +class TCMalloc_PageMap1_LazyCommit { + private: + // Dimension of our page map array_. + static const int LENGTH = 1 << BITS; + + // The page map array that sits in reserved virtual space. Pages of this + // array are committed as they are needed. For each page of virtual memory, + // we potentially have a pointer to a span instance. + void** array_; + + // A bit vector that allows us to deduce what pages in array_ are committed. + // Note that 2^3 = 8 bits per char, and hence the use of the magical "3" in + // the array range gives us the effective "divide by 8". + char committed_[sizeof(void*) << (BITS - kPageShift - 3)]; + + // Given an |index| into |array_|, find the page number in |array_| that holds + // that element. + size_t ContainingPage(size_t index) const { + return (index * sizeof(*array_)) >> kPageShift; + } + + // Find out if the given page_num index in array_ is in committed memory. + bool IsCommitted(size_t page_num) const { + return committed_[page_num >> 3] & (1 << (page_num & 0x7)); + } + + // Remember that the given page_num index in array_ is in committed memory. + void SetCommitted(size_t page_num) { + committed_[page_num >> 3] |= (1 << (page_num & 0x7)); + } + + public: + typedef uintptr_t Number; + + explicit TCMalloc_PageMap1_LazyCommit(void* (*allocator)(size_t)) { + // TODO(jar): We need a reservation function, but current API to this class + // only provides an allocator. + // Get decommitted memory. We will commit as necessary. + array_ = reinterpret_cast<void**>(VirtualAlloc( + NULL, sizeof(*array_) << BITS, MEM_RESERVE, PAGE_READWRITE)); + + // Make sure we divided LENGTH evenly. + ASSERT(sizeof(committed_) * 8 == (LENGTH * sizeof(*array_)) >> kPageShift); + // Indicate that none of the pages of array_ have been committed yet. + memset(committed_, 0, sizeof(committed_)); + } + + // Ensure that the map contains initialized and committed entries in array_ to + // describe pages "x .. x+n-1". + // Returns true if successful, false if we could not ensure this. + // If we have to commit more memory in array_ (which also clears said memory), + // then we'll set some of the bits in committed_ to remember this fact. + // Only the bits of committed_ near end-points for calls to Ensure() are ever + // set, as the calls to Ensure() will never have overlapping ranges other than + // at their end-points. + // + // Example: Suppose the OS allocates memory in pages including 40...50, and + // later the OS allocates memory in pages 51...83. When the first allocation + // of 40...50 is observed, then Ensure of (39,51) will be called. The range + // shown in the arguments is extended so that tcmalloc can look to see if + // adjacent pages are part of a span that can be coaleced. Later, when pages + // 51...83 are allocated, Ensure() will be called with arguments (50,84), + // broadened again for the same reason. + // + // After the above, we would NEVER get a call such as Ensure(45,60), as that + // overlaps with the interior of prior ensured regions. We ONLY get an Ensure + // call when the OS has allocated memory, and since we NEVER give memory back + // to the OS, the OS can't possible allocate the same region to us twice, and + // can't induce an Ensure() on an interior of previous Ensure call. + // + // Also note that OS allocations are NOT guaranteed to be consecutive (there + // may be "holes" where code etc. uses the virtual addresses), or to appear in + // any order, such as lowest to highest, or vice versa (as other independent + // allocation systems in the process may be performing VirtualAllocations and + // VirtualFrees asynchronously.) + bool Ensure(Number x, size_t n) { + if (n > LENGTH - x) + return false; // We won't Ensure mapping for last pages in memory. + ASSERT(n > 0); + + // For a given page number in memory, calculate what page in array_ needs to + // be memory resident. Note that we really only need a few bytes in array_ + // for each page of virtual space we have to map, but we can only commit + // whole pages of array_. For instance, a 4K page of array_ has about 1k + // entries, and hence can map about 1K pages, or a total of about 4MB + // typically. As a result, it is possible that the first entry in array_, + // and the n'th entry in array_, will sit in the same page of array_. + size_t first_page = ContainingPage(x); + size_t last_page = ContainingPage(x + n - 1); + + // Check at each boundary, to see if we need to commit at that end. Some + // other neighbor may have already forced us to commit at either or both + // boundaries. + if (IsCommitted(first_page)) { + if (first_page == last_page) return true; + ++first_page; + if (IsCommitted(first_page)) { + if (first_page == last_page) return true; + ++first_page; + } + } + + if (IsCommitted(last_page)) { + if (first_page == last_page) return true; + --last_page; + if (IsCommitted(last_page)) { + if (first_page == last_page) return true; + --last_page; + } + } + + ASSERT(!IsCommitted(last_page)); + ASSERT(!IsCommitted(first_page)); + + void* start = reinterpret_cast<char*>(array_) + (first_page << kPageShift); + size_t length = (last_page - first_page + 1) << kPageShift; + +#ifndef NDEBUG + // Validate we are committing new sections, and hence we're not clearing any + // existing data. + MEMORY_BASIC_INFORMATION info = {0}; + size_t result = VirtualQuery(start, &info, sizeof(info)); + ASSERT(result); + ASSERT(0 == (info.State & MEM_COMMIT)); // It starts with uncommitted. + ASSERT(info.RegionSize >= length); // Entire length is uncommitted. +#endif + + // TODO(jar): We need a commit that automatically tallies metadata_bytes. + TCMalloc_SystemCommit(start, length); + tcmalloc::increment_metadata_system_bytes(length); + +#ifndef NDEBUG + result = VirtualQuery(start, &info, sizeof(info)); + ASSERT(result); + ASSERT(0 != (info.State & MEM_COMMIT)); // Now it is committed. + ASSERT(info.RegionSize >= length); // Entire length is committed. +#endif + + // As noted in the large comment/example describing this method, we will + // never be called with a range of pages very much inside this |first_page| + // to |last_page| range. + // As a result, we only need to set bits for each end of that range, and one + // page inside each end. + SetCommitted(first_page); + if (first_page < last_page) { + SetCommitted(last_page); + SetCommitted(first_page + 1); // These may be duplicates now. + SetCommitted(last_page - 1); + } + + return true; + } + + // This is a premature call to get all the meta-memory allocated, so as to + // avoid virtual space fragmentation. Since we pre-reserved all memory, we + // don't need to do anything here (we won't fragment virtual space). + void PreallocateMoreMemory() {} + + // Return the current value for KEY. Returns NULL if not yet set, + // or if k is out of range. + void* get(Number k) const { + if ((k >> BITS) > 0) { + return NULL; + } + return array_[k]; + } + + // REQUIRES "k" is in range "[0,2^BITS-1]". + // REQUIRES "k" has been ensured before. + // + // Sets the value for KEY. + void set(Number k, void* v) { + array_[k] = v; + } +}; +#endif // WIN32 + + // Two-level radix tree template <int BITS> class TCMalloc_PageMap2 { |