• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2005, 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 <opensource@google.com>
32 //
33 // A data structure used by the caching malloc.  It maps from page# to
34 // a pointer that contains info about that page.  We use two
35 // representations: one for 32-bit addresses, and another for 64 bit
36 // addresses.  Both representations provide the same interface.  The
37 // first representation is implemented as a flat array, the seconds as
38 // a three-level radix tree that strips away approximately 1/3rd of
39 // the bits every time.
40 //
41 // The BITS parameter should be the number of bits required to hold
42 // a page number.  E.g., with 32 bit pointers and 4K pages (i.e.,
43 // page offset fits in lower 12 bits), BITS == 20.
44 
45 #ifndef TCMALLOC_PAGEMAP_H_
46 #define TCMALLOC_PAGEMAP_H_
47 
48 #include "config.h"
49 
50 #include <stddef.h>                     // for NULL, size_t
51 #include <string.h>                     // for memset
52 #if defined HAVE_STDINT_H
53 #include <stdint.h>
54 #elif defined HAVE_INTTYPES_H
55 #include <inttypes.h>
56 #else
57 #include <sys/types.h>
58 #endif
59 #ifdef WIN32
60 // TODO(jar): This is not needed when TCMalloc_PageMap1_LazyCommit has an API
61 // supporting commit and reservation of memory.
62 #include "common.h"
63 #endif
64 
65 #include "internal_logging.h"  // for ASSERT
66 
67 // Single-level array
68 template <int BITS>
69 class TCMalloc_PageMap1 {
70  private:
71   static const int LENGTH = 1 << BITS;
72 
73   void** array_;
74 
75  public:
76   typedef uintptr_t Number;
77 
TCMalloc_PageMap1(void * (* allocator)(size_t))78   explicit TCMalloc_PageMap1(void* (*allocator)(size_t)) {
79     array_ = reinterpret_cast<void**>((*allocator)(sizeof(void*) << BITS));
80     memset(array_, 0, sizeof(void*) << BITS);
81   }
82 
83   // Ensure that the map contains initialized entries "x .. x+n-1".
84   // Returns true if successful, false if we could not allocate memory.
Ensure(Number x,size_t n)85   bool Ensure(Number x, size_t n) {
86     // Nothing to do since flat array was allocated at start.  All
87     // that's left is to check for overflow (that is, we don't want to
88     // ensure a number y where array_[y] would be an out-of-bounds
89     // access).
90     return n <= LENGTH - x;   // an overflow-free way to do "x + n <= LENGTH"
91   }
92 
PreallocateMoreMemory()93   void PreallocateMoreMemory() {}
94 
95   // Return the current value for KEY.  Returns NULL if not yet set,
96   // or if k is out of range.
get(Number k)97   void* get(Number k) const {
98     if ((k >> BITS) > 0) {
99       return NULL;
100     }
101     return array_[k];
102   }
103 
104   // REQUIRES "k" is in range "[0,2^BITS-1]".
105   // REQUIRES "k" has been ensured before.
106   //
107   // Sets the value 'v' for key 'k'.
set(Number k,void * v)108   void set(Number k, void* v) {
109     array_[k] = v;
110   }
111 
112   // Return the first non-NULL pointer found in this map for
113   // a page number >= k.  Returns NULL if no such number is found.
Next(Number k)114   void* Next(Number k) const {
115     while (k < (1 << BITS)) {
116       if (array_[k] != NULL) return array_[k];
117       k++;
118     }
119     return NULL;
120   }
121 };
122 
123 #ifdef WIN32
124 // Lazy commit, single-level array.
125 // Very similar to PageMap1, except the page map is only committed as needed.
126 // Since we don't return memory to the OS, the committed portion of the map will
127 // only grow, and we'll only be called to Ensure when we really grow the heap.
128 // We maintain a bit map to help us deduce if we've already committed a range
129 // in our map.
130 template <int BITS>
131 class TCMalloc_PageMap1_LazyCommit {
132  private:
133   // Dimension of our page map array_.
134   static const int LENGTH = 1 << BITS;
135 
136   // The page map array that sits in reserved virtual space.  Pages of this
137   // array are committed as they are needed.  For each page of virtual memory,
138   // we potentially have a pointer to a span instance.
139   void** array_;
140 
141   // A bit vector that allows us to deduce what pages in array_ are committed.
142   // Note that 2^3 = 8 bits per char, and hence the use of the magical "3" in
143   // the array range gives us the effective "divide by 8".
144   char committed_[sizeof(void*) << (BITS - kPageShift - 3)];
145 
146   // Given an |index| into |array_|, find the page number in |array_| that holds
147   // that element.
ContainingPage(size_t index)148   size_t ContainingPage(size_t index) const {
149     return (index * sizeof(*array_)) >> kPageShift;
150   }
151 
152   // Find out if the given page_num index in array_ is in committed memory.
IsCommitted(size_t page_num)153   bool IsCommitted(size_t page_num) const {
154     return committed_[page_num >> 3] & (1 << (page_num & 0x7));
155   }
156 
157   // Remember that the given page_num index in array_ is in committed memory.
SetCommitted(size_t page_num)158   void SetCommitted(size_t page_num) {
159     committed_[page_num >> 3] |= (1 << (page_num & 0x7));
160   }
161 
162  public:
163   typedef uintptr_t Number;
164 
TCMalloc_PageMap1_LazyCommit(void * (* allocator)(size_t))165   explicit TCMalloc_PageMap1_LazyCommit(void* (*allocator)(size_t)) {
166     // TODO(jar): We need a reservation function, but current API to this class
167     // only provides an allocator.
168     // Get decommitted memory.  We will commit as necessary.
169     size_t size = sizeof(*array_) << BITS;
170     array_ = reinterpret_cast<void**>(VirtualAlloc(
171         NULL, size, MEM_RESERVE, PAGE_READWRITE));
172     tcmalloc::update_metadata_system_bytes(size);
173     tcmalloc::update_metadata_unmapped_bytes(size);
174 
175     // Make sure we divided LENGTH evenly.
176     ASSERT(sizeof(committed_) * 8 == (LENGTH * sizeof(*array_)) >> kPageShift);
177     // Indicate that none of the pages of array_ have been committed yet.
178     memset(committed_, 0, sizeof(committed_));
179   }
180 
181   // Ensure that the map contains initialized and committed entries in array_ to
182   // describe pages "x .. x+n-1".
183   // Returns true if successful, false if we could not ensure this.
184   // If we have to commit more memory in array_ (which also clears said memory),
185   // then we'll set some of the bits in committed_ to remember this fact.
186   // Only the bits of committed_ near end-points for calls to Ensure() are ever
187   // set, as the calls to Ensure() will never have overlapping ranges other than
188   // at their end-points.
189   //
190   // Example: Suppose the OS allocates memory in pages including 40...50, and
191   // later the OS allocates memory in pages 51...83.  When the first allocation
192   // of 40...50 is observed, then Ensure of (39,51) will be called.  The range
193   // shown in the arguments is extended so that tcmalloc can look to see if
194   // adjacent pages are part of a span that can be coaleced.  Later, when pages
195   // 51...83 are allocated, Ensure() will be called with arguments (50,84),
196   // broadened again for the same reason.
197   //
198   // After the above, we would NEVER get a call such as Ensure(45,60), as that
199   // overlaps with the interior of prior ensured regions.  We ONLY get an Ensure
200   // call when the OS has allocated memory, and since we NEVER give memory back
201   // to the OS, the OS can't possible allocate the same region to us twice, and
202   // can't induce an Ensure() on an interior of previous Ensure call.
203   //
204   // Also note that OS allocations are NOT guaranteed to be consecutive (there
205   // may be "holes" where code etc. uses the virtual addresses), or to appear in
206   // any order, such as lowest to highest, or vice versa (as other independent
207   // allocation systems in the process may be performing VirtualAllocations and
208   // VirtualFrees asynchronously.)
Ensure(Number x,size_t n)209   bool Ensure(Number x, size_t n) {
210     if (n > LENGTH - x)
211       return false;  // We won't Ensure mapping for last pages in memory.
212     ASSERT(n > 0);
213 
214     // For a given page number in memory, calculate what page in array_ needs to
215     // be memory resident.  Note that we really only need a few bytes in array_
216     // for each page of virtual space we have to map, but we can only commit
217     // whole pages of array_.  For instance, a 4K page of array_ has about 1k
218     // entries, and hence can map about 1K pages, or a total of about 4MB
219     // typically. As a result, it is possible that the first entry in array_,
220     // and the n'th entry in array_, will sit in the same page of array_.
221     size_t first_page = ContainingPage(x);
222     size_t last_page = ContainingPage(x + n - 1);
223 
224     // Check at each boundary, to see if we need to commit at that end.  Some
225     // other neighbor may have already forced us to commit at either or both
226     // boundaries.
227     if (IsCommitted(first_page)) {
228       if (first_page == last_page) return true;
229       ++first_page;
230       if (IsCommitted(first_page)) {
231         if (first_page == last_page) return true;
232         ++first_page;
233       }
234     }
235 
236     if (IsCommitted(last_page)) {
237       if (first_page == last_page) return true;
238       --last_page;
239       if (IsCommitted(last_page)) {
240         if (first_page == last_page) return true;
241         --last_page;
242       }
243     }
244 
245     ASSERT(!IsCommitted(last_page));
246     ASSERT(!IsCommitted(first_page));
247 
248     void* start = reinterpret_cast<char*>(array_) + (first_page << kPageShift);
249     size_t length = (last_page - first_page + 1) << kPageShift;
250 
251 #ifndef NDEBUG
252     // Validate we are committing new sections, and hence we're not clearing any
253     // existing data.
254     MEMORY_BASIC_INFORMATION info = {0};
255     size_t result = VirtualQuery(start, &info, sizeof(info));
256     ASSERT(result);
257     ASSERT(0 == (info.State & MEM_COMMIT));  // It starts with uncommitted.
258     ASSERT(info.RegionSize >= length);       // Entire length is uncommitted.
259 #endif
260 
261     TCMalloc_SystemCommit(start, length);
262     tcmalloc::update_metadata_unmapped_bytes(-length);
263 
264 #ifndef NDEBUG
265     result = VirtualQuery(start, &info, sizeof(info));
266     ASSERT(result);
267     ASSERT(0 != (info.State & MEM_COMMIT));  // Now it is committed.
268     ASSERT(info.RegionSize >= length);       // Entire length is committed.
269 #endif
270 
271     // As noted in the large comment/example describing this method, we will
272     // never be called with a range of pages very much inside this |first_page|
273     // to |last_page| range.
274     // As a result, we only need to set bits for each end of that range, and one
275     // page inside each end.
276     SetCommitted(first_page);
277     if (first_page < last_page) {
278       SetCommitted(last_page);
279       SetCommitted(first_page + 1);  // These may be duplicates now.
280       SetCommitted(last_page - 1);
281     }
282 
283     return true;
284   }
285 
286   // This is a premature call to get all the meta-memory allocated, so as to
287   // avoid virtual space fragmentation.  Since we pre-reserved all memory, we
288   // don't need to do anything here (we won't fragment virtual space).
PreallocateMoreMemory()289   void PreallocateMoreMemory() {}
290 
291   // Return the current value for KEY.  Returns NULL if not yet set,
292   // or if k is out of range.
get(Number k)293   void* get(Number k) const {
294     if ((k >> BITS) > 0) {
295       return NULL;
296     }
297     return array_[k];
298   }
299 
300   // REQUIRES "k" is in range "[0,2^BITS-1]".
301   // REQUIRES "k" has been ensured before.
302   //
303   // Sets the value for KEY.
set(Number k,void * v)304   void set(Number k, void* v) {
305     array_[k] = v;
306   }
307   // Return the first non-NULL pointer found in this map for
308   // a page number >= k.  Returns NULL if no such number is found.
Next(Number k)309   void* Next(Number k) const {
310     while (k < (1 << BITS)) {
311       if (array_[k] != NULL) return array_[k];
312       k++;
313     }
314     return NULL;
315   }
316 };
317 #endif  // WIN32
318 
319 
320 // Two-level radix tree
321 template <int BITS>
322 class TCMalloc_PageMap2 {
323  private:
324   // Put 32 entries in the root and (2^BITS)/32 entries in each leaf.
325   static const int ROOT_BITS = 5;
326   static const int ROOT_LENGTH = 1 << ROOT_BITS;
327 
328   static const int LEAF_BITS = BITS - ROOT_BITS;
329   static const int LEAF_LENGTH = 1 << LEAF_BITS;
330 
331   // Leaf node
332   struct Leaf {
333     void* values[LEAF_LENGTH];
334   };
335 
336   Leaf* root_[ROOT_LENGTH];             // Pointers to 32 child nodes
337   void* (*allocator_)(size_t);          // Memory allocator
338 
339  public:
340   typedef uintptr_t Number;
341 
TCMalloc_PageMap2(void * (* allocator)(size_t))342   explicit TCMalloc_PageMap2(void* (*allocator)(size_t)) {
343     allocator_ = allocator;
344     memset(root_, 0, sizeof(root_));
345   }
346 
get(Number k)347   void* get(Number k) const {
348     const Number i1 = k >> LEAF_BITS;
349     const Number i2 = k & (LEAF_LENGTH-1);
350     if ((k >> BITS) > 0 || root_[i1] == NULL) {
351       return NULL;
352     }
353     return root_[i1]->values[i2];
354   }
355 
set(Number k,void * v)356   void set(Number k, void* v) {
357     ASSERT(k >> BITS == 0);
358     const Number i1 = k >> LEAF_BITS;
359     const Number i2 = k & (LEAF_LENGTH-1);
360     root_[i1]->values[i2] = v;
361   }
362 
Ensure(Number start,size_t n)363   bool Ensure(Number start, size_t n) {
364     for (Number key = start; key <= start + n - 1; ) {
365       const Number i1 = key >> LEAF_BITS;
366 
367       // Check for overflow
368       if (i1 >= ROOT_LENGTH)
369         return false;
370 
371       // Make 2nd level node if necessary
372       if (root_[i1] == NULL) {
373         Leaf* leaf = reinterpret_cast<Leaf*>((*allocator_)(sizeof(Leaf)));
374         if (leaf == NULL) return false;
375         memset(leaf, 0, sizeof(*leaf));
376         root_[i1] = leaf;
377       }
378 
379       // Advance key past whatever is covered by this leaf node
380       key = ((key >> LEAF_BITS) + 1) << LEAF_BITS;
381     }
382     return true;
383   }
384 
PreallocateMoreMemory()385   void PreallocateMoreMemory() {
386     // Allocate enough to keep track of all possible pages
387     Ensure(0, 1 << BITS);
388   }
389 
Next(Number k)390   void* Next(Number k) const {
391     while (k < (1 << BITS)) {
392       const Number i1 = k >> LEAF_BITS;
393       Leaf* leaf = root_[i1];
394       if (leaf != NULL) {
395         // Scan forward in leaf
396         for (Number i2 = k & (LEAF_LENGTH - 1); i2 < LEAF_LENGTH; i2++) {
397           if (leaf->values[i2] != NULL) {
398             return leaf->values[i2];
399           }
400         }
401       }
402       // Skip to next top-level entry
403       k = (i1 + 1) << LEAF_BITS;
404     }
405     return NULL;
406   }
407 };
408 
409 // Three-level radix tree
410 template <int BITS>
411 class TCMalloc_PageMap3 {
412  private:
413   // How many bits should we consume at each interior level
414   static const int INTERIOR_BITS = (BITS + 2) / 3; // Round-up
415   static const int INTERIOR_LENGTH = 1 << INTERIOR_BITS;
416 
417   // How many bits should we consume at leaf level
418   static const int LEAF_BITS = BITS - 2*INTERIOR_BITS;
419   static const int LEAF_LENGTH = 1 << LEAF_BITS;
420 
421   // Interior node
422   struct Node {
423     Node* ptrs[INTERIOR_LENGTH];
424   };
425 
426   // Leaf node
427   struct Leaf {
428     void* values[LEAF_LENGTH];
429   };
430 
431   Node* root_;                          // Root of radix tree
432   void* (*allocator_)(size_t);          // Memory allocator
433 
NewNode()434   Node* NewNode() {
435     Node* result = reinterpret_cast<Node*>((*allocator_)(sizeof(Node)));
436     if (result != NULL) {
437       memset(result, 0, sizeof(*result));
438     }
439     return result;
440   }
441 
442  public:
443   typedef uintptr_t Number;
444 
TCMalloc_PageMap3(void * (* allocator)(size_t))445   explicit TCMalloc_PageMap3(void* (*allocator)(size_t)) {
446     allocator_ = allocator;
447     root_ = NewNode();
448   }
449 
get(Number k)450   void* get(Number k) const {
451     const Number i1 = k >> (LEAF_BITS + INTERIOR_BITS);
452     const Number i2 = (k >> LEAF_BITS) & (INTERIOR_LENGTH-1);
453     const Number i3 = k & (LEAF_LENGTH-1);
454     if ((k >> BITS) > 0 ||
455         root_->ptrs[i1] == NULL || root_->ptrs[i1]->ptrs[i2] == NULL) {
456       return NULL;
457     }
458     return reinterpret_cast<Leaf*>(root_->ptrs[i1]->ptrs[i2])->values[i3];
459   }
460 
set(Number k,void * v)461   void set(Number k, void* v) {
462     ASSERT(k >> BITS == 0);
463     const Number i1 = k >> (LEAF_BITS + INTERIOR_BITS);
464     const Number i2 = (k >> LEAF_BITS) & (INTERIOR_LENGTH-1);
465     const Number i3 = k & (LEAF_LENGTH-1);
466     reinterpret_cast<Leaf*>(root_->ptrs[i1]->ptrs[i2])->values[i3] = v;
467   }
468 
Ensure(Number start,size_t n)469   bool Ensure(Number start, size_t n) {
470     for (Number key = start; key <= start + n - 1; ) {
471       const Number i1 = key >> (LEAF_BITS + INTERIOR_BITS);
472       const Number i2 = (key >> LEAF_BITS) & (INTERIOR_LENGTH-1);
473 
474       // Check for overflow
475       if (i1 >= INTERIOR_LENGTH || i2 >= INTERIOR_LENGTH)
476         return false;
477 
478       // Make 2nd level node if necessary
479       if (root_->ptrs[i1] == NULL) {
480         Node* n = NewNode();
481         if (n == NULL) return false;
482         root_->ptrs[i1] = n;
483       }
484 
485       // Make leaf node if necessary
486       if (root_->ptrs[i1]->ptrs[i2] == NULL) {
487         Leaf* leaf = reinterpret_cast<Leaf*>((*allocator_)(sizeof(Leaf)));
488         if (leaf == NULL) return false;
489         memset(leaf, 0, sizeof(*leaf));
490         root_->ptrs[i1]->ptrs[i2] = reinterpret_cast<Node*>(leaf);
491       }
492 
493       // Advance key past whatever is covered by this leaf node
494       key = ((key >> LEAF_BITS) + 1) << LEAF_BITS;
495     }
496     return true;
497   }
498 
PreallocateMoreMemory()499   void PreallocateMoreMemory() {
500   }
501 
Next(Number k)502   void* Next(Number k) const {
503     while (k < (Number(1) << BITS)) {
504       const Number i1 = k >> (LEAF_BITS + INTERIOR_BITS);
505       const Number i2 = (k >> LEAF_BITS) & (INTERIOR_LENGTH-1);
506       if (root_->ptrs[i1] == NULL) {
507         // Advance to next top-level entry
508         k = (i1 + 1) << (LEAF_BITS + INTERIOR_BITS);
509       } else {
510         Leaf* leaf = reinterpret_cast<Leaf*>(root_->ptrs[i1]->ptrs[i2]);
511         if (leaf != NULL) {
512           for (Number i3 = (k & (LEAF_LENGTH-1)); i3 < LEAF_LENGTH; i3++) {
513             if (leaf->values[i3] != NULL) {
514               return leaf->values[i3];
515             }
516           }
517         }
518         // Advance to next interior entry
519         k = ((k >> LEAF_BITS) + 1) << LEAF_BITS;
520       }
521     }
522     return NULL;
523   }
524 };
525 
526 #endif  // TCMALLOC_PAGEMAP_H_
527