1 // Copyright (c) 2005, 2007, Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
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
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.
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.
31 // Author: Sanjay Ghemawat
34 #include "TCSystemAlloc.h"
38 #include "Assertions.h"
39 #include "TCSpinLock.h"
40 #include "UnusedParam.h"
44 #elif HAVE(INTTYPES_H)
47 #include <sys/types.h>
59 #define MAP_ANONYMOUS MAP_ANON
64 // Structure for discovering alignment
71 static SpinLock spinlock
= SPINLOCK_INITIALIZER
;
73 // Page size is initialized on demand
74 static size_t pagesize
= 0;
76 // Configuration parameters.
78 // if use_devmem is true, either use_sbrk or use_mmap must also be true.
79 // For 2.2 kernels, it looks like the sbrk address space (500MBish) and
80 // the mmap address space (1300MBish) are disjoint, so we need both allocators
81 // to get as much virtual memory as possible.
83 static bool use_devmem
= false;
87 static bool use_sbrk
= false;
91 static bool use_mmap
= true;
94 #if HAVE(VIRTUALALLOC)
95 static bool use_VirtualAlloc
= true;
98 // Flags to keep us from retrying allocators that failed.
99 static bool devmem_failure
= false;
100 static bool sbrk_failure
= false;
101 static bool mmap_failure
= false;
102 static bool VirtualAlloc_failure
= false;
105 DEFINE_int32(malloc_devmem_start
, 0,
106 "Physical memory starting location in MB for /dev/mem allocation."
107 " Setting this to 0 disables /dev/mem allocation");
108 DEFINE_int32(malloc_devmem_limit
, 0,
109 "Physical memory limit location in MB for /dev/mem allocation."
110 " Setting this to 0 means no limit.");
112 static const int32_t FLAGS_malloc_devmem_start
= 0;
113 static const int32_t FLAGS_malloc_devmem_limit
= 0;
118 static void* TrySbrk(size_t size
, size_t *actual_size
, size_t alignment
) {
119 size
= ((size
+ alignment
- 1) / alignment
) * alignment
;
121 // could theoretically return the "extra" bytes here, but this
122 // is simple and correct.
126 void* result
= sbrk(size
);
127 if (result
== reinterpret_cast<void*>(-1)) {
133 uintptr_t ptr
= reinterpret_cast<uintptr_t>(result
);
134 if ((ptr
& (alignment
-1)) == 0) return result
;
136 // Try to get more memory for alignment
137 size_t extra
= alignment
- (ptr
& (alignment
-1));
138 void* r2
= sbrk(extra
);
139 if (reinterpret_cast<uintptr_t>(r2
) == (ptr
+ size
)) {
140 // Contiguous with previous result
141 return reinterpret_cast<void*>(ptr
+ extra
);
144 // Give up and ask for "size + alignment - 1" bytes so
145 // that we can find an aligned region within it.
146 result
= sbrk(size
+ alignment
- 1);
147 if (result
== reinterpret_cast<void*>(-1)) {
151 ptr
= reinterpret_cast<uintptr_t>(result
);
152 if ((ptr
& (alignment
-1)) != 0) {
153 ptr
+= alignment
- (ptr
& (alignment
-1));
155 return reinterpret_cast<void*>(ptr
);
158 #endif /* HAVE(SBRK) */
162 static void* TryMmap(size_t size
, size_t *actual_size
, size_t alignment
) {
163 // Enforce page alignment
164 if (pagesize
== 0) pagesize
= getpagesize();
165 if (alignment
< pagesize
) alignment
= pagesize
;
166 size
= ((size
+ alignment
- 1) / alignment
) * alignment
;
168 // could theoretically return the "extra" bytes here, but this
169 // is simple and correct.
173 // Ask for extra memory if alignment > pagesize
175 if (alignment
> pagesize
) {
176 extra
= alignment
- pagesize
;
178 void* result
= mmap(NULL
, size
+ extra
,
179 PROT_READ
| PROT_WRITE
,
180 MAP_PRIVATE
|MAP_ANONYMOUS
,
182 if (result
== reinterpret_cast<void*>(MAP_FAILED
)) {
187 // Adjust the return memory so it is aligned
188 uintptr_t ptr
= reinterpret_cast<uintptr_t>(result
);
190 if ((ptr
& (alignment
- 1)) != 0) {
191 adjust
= alignment
- (ptr
& (alignment
- 1));
194 // Return the unused memory to the system
196 munmap(reinterpret_cast<void*>(ptr
), adjust
);
198 if (adjust
< extra
) {
199 munmap(reinterpret_cast<void*>(ptr
+ adjust
+ size
), extra
- adjust
);
203 return reinterpret_cast<void*>(ptr
);
206 #endif /* HAVE(MMAP) */
208 #if HAVE(VIRTUALALLOC)
210 static void* TryVirtualAlloc(size_t size
, size_t *actual_size
, size_t alignment
) {
211 // Enforce page alignment
213 SYSTEM_INFO system_info
;
214 GetSystemInfo(&system_info
);
215 pagesize
= system_info
.dwPageSize
;
218 if (alignment
< pagesize
) alignment
= pagesize
;
219 size
= ((size
+ alignment
- 1) / alignment
) * alignment
;
221 // could theoretically return the "extra" bytes here, but this
222 // is simple and correct.
226 // Ask for extra memory if alignment > pagesize
228 if (alignment
> pagesize
) {
229 extra
= alignment
- pagesize
;
231 void* result
= VirtualAlloc(NULL
, size
+ extra
,
232 MEM_RESERVE
| MEM_COMMIT
| MEM_TOP_DOWN
,
235 if (result
== NULL
) {
236 VirtualAlloc_failure
= true;
240 // Adjust the return memory so it is aligned
241 uintptr_t ptr
= reinterpret_cast<uintptr_t>(result
);
243 if ((ptr
& (alignment
- 1)) != 0) {
244 adjust
= alignment
- (ptr
& (alignment
- 1));
247 // Return the unused memory to the system - we'd like to release but the best we can do
248 // is decommit, since Windows only lets you free the whole allocation.
250 VirtualFree(reinterpret_cast<void*>(ptr
), adjust
, MEM_DECOMMIT
);
252 if (adjust
< extra
) {
253 VirtualFree(reinterpret_cast<void*>(ptr
+ adjust
+ size
), extra
-adjust
, MEM_DECOMMIT
);
257 return reinterpret_cast<void*>(ptr
);
260 #endif /* HAVE(MMAP) */
263 static void* TryDevMem(size_t size
, size_t *actual_size
, size_t alignment
) {
264 static bool initialized
= false;
265 static off_t physmem_base
; // next physical memory address to allocate
266 static off_t physmem_limit
; // maximum physical address allowed
267 static int physmem_fd
; // file descriptor for /dev/mem
269 // Check if we should use /dev/mem allocation. Note that it may take
270 // a while to get this flag initialized, so meanwhile we fall back to
271 // the next allocator. (It looks like 7MB gets allocated before
272 // this flag gets initialized -khr.)
273 if (FLAGS_malloc_devmem_start
== 0) {
274 // NOTE: not a devmem_failure - we'd like TCMalloc_SystemAlloc to
275 // try us again next time.
280 physmem_fd
= open("/dev/mem", O_RDWR
);
281 if (physmem_fd
< 0) {
282 devmem_failure
= true;
285 physmem_base
= FLAGS_malloc_devmem_start
*1024LL*1024LL;
286 physmem_limit
= FLAGS_malloc_devmem_limit
*1024LL*1024LL;
290 // Enforce page alignment
291 if (pagesize
== 0) pagesize
= getpagesize();
292 if (alignment
< pagesize
) alignment
= pagesize
;
293 size
= ((size
+ alignment
- 1) / alignment
) * alignment
;
295 // could theoretically return the "extra" bytes here, but this
296 // is simple and correct.
300 // Ask for extra memory if alignment > pagesize
302 if (alignment
> pagesize
) {
303 extra
= alignment
- pagesize
;
306 // check to see if we have any memory left
307 if (physmem_limit
!= 0 && physmem_base
+ size
+ extra
> physmem_limit
) {
308 devmem_failure
= true;
311 void *result
= mmap(0, size
+ extra
, PROT_READ
| PROT_WRITE
,
312 MAP_SHARED
, physmem_fd
, physmem_base
);
313 if (result
== reinterpret_cast<void*>(MAP_FAILED
)) {
314 devmem_failure
= true;
317 uintptr_t ptr
= reinterpret_cast<uintptr_t>(result
);
319 // Adjust the return memory so it is aligned
321 if ((ptr
& (alignment
- 1)) != 0) {
322 adjust
= alignment
- (ptr
& (alignment
- 1));
325 // Return the unused virtual memory to the system
327 munmap(reinterpret_cast<void*>(ptr
), adjust
);
329 if (adjust
< extra
) {
330 munmap(reinterpret_cast<void*>(ptr
+ adjust
+ size
), extra
- adjust
);
334 physmem_base
+= adjust
+ size
;
336 return reinterpret_cast<void*>(ptr
);
340 void* TCMalloc_SystemAlloc(size_t size
, size_t *actual_size
, size_t alignment
) {
341 // Discard requests that overflow
342 if (size
+ alignment
< size
) return NULL
;
344 SpinLockHolder
lock_holder(&spinlock
);
346 // Enforce minimum alignment
347 if (alignment
< sizeof(MemoryAligner
)) alignment
= sizeof(MemoryAligner
);
349 // Try twice, once avoiding allocators that failed before, and once
350 // more trying all allocators even if they failed before.
351 for (int i
= 0; i
< 2; i
++) {
354 if (use_devmem
&& !devmem_failure
) {
355 void* result
= TryDevMem(size
, actual_size
, alignment
);
356 if (result
!= NULL
) return result
;
361 if (use_sbrk
&& !sbrk_failure
) {
362 void* result
= TrySbrk(size
, actual_size
, alignment
);
363 if (result
!= NULL
) return result
;
368 if (use_mmap
&& !mmap_failure
) {
369 void* result
= TryMmap(size
, actual_size
, alignment
);
370 if (result
!= NULL
) return result
;
374 #if HAVE(VIRTUALALLOC)
375 if (use_VirtualAlloc
&& !VirtualAlloc_failure
) {
376 void* result
= TryVirtualAlloc(size
, actual_size
, alignment
);
377 if (result
!= NULL
) return result
;
381 // nothing worked - reset failure flags and try again
382 devmem_failure
= false;
383 sbrk_failure
= false;
384 mmap_failure
= false;
385 VirtualAlloc_failure
= false;
390 #if HAVE(MADV_FREE_REUSE)
392 void TCMalloc_SystemRelease(void* start
, size_t length
)
394 while (madvise(start
, length
, MADV_FREE_REUSABLE
) == -1 && errno
== EAGAIN
) { }
397 #elif HAVE(MADV_FREE) || HAVE(MADV_DONTNEED)
399 void TCMalloc_SystemRelease(void* start
, size_t length
)
401 // MADV_FREE clears the modified bit on pages, which allows
402 // them to be discarded immediately.
404 const int advice
= MADV_FREE
;
406 const int advice
= MADV_DONTNEED
;
408 if (FLAGS_malloc_devmem_start
) {
409 // It's not safe to use MADV_DONTNEED if we've been mapping
410 // /dev/mem for heap memory
413 if (pagesize
== 0) pagesize
= getpagesize();
414 const size_t pagemask
= pagesize
- 1;
416 size_t new_start
= reinterpret_cast<size_t>(start
);
417 size_t end
= new_start
+ length
;
418 size_t new_end
= end
;
420 // Round up the starting address and round down the ending address
421 // to be page aligned:
422 new_start
= (new_start
+ pagesize
- 1) & ~pagemask
;
423 new_end
= new_end
& ~pagemask
;
425 ASSERT((new_start
& pagemask
) == 0);
426 ASSERT((new_end
& pagemask
) == 0);
427 ASSERT(new_start
>= reinterpret_cast<size_t>(start
));
428 ASSERT(new_end
<= end
);
430 if (new_end
> new_start
) {
431 // Note -- ignoring most return codes, because if this fails it
433 while (madvise(reinterpret_cast<char*>(new_start
), new_end
- new_start
,
443 void TCMalloc_SystemRelease(void* start
, size_t length
)
445 void* newAddress
= mmap(start
, length
, PROT_READ
| PROT_WRITE
, MAP_PRIVATE
| MAP_ANONYMOUS
| MAP_FIXED
, -1, 0);
446 // If the mmap failed then that's ok, we just won't return the memory to the system.
447 ASSERT_UNUSED(newAddress
, newAddress
== start
|| newAddress
== reinterpret_cast<void*>(MAP_FAILED
));
450 #elif HAVE(VIRTUALALLOC)
452 void TCMalloc_SystemRelease(void* start
, size_t length
)
454 if (VirtualFree(start
, length
, MEM_DECOMMIT
))
457 // The decommit may fail if the memory region consists of allocations
458 // from more than one call to VirtualAlloc. In this case, fall back to
459 // using VirtualQuery to retrieve the allocation boundaries and decommit
460 // them each individually.
462 char* ptr
= static_cast<char*>(start
);
463 char* end
= ptr
+ length
;
464 MEMORY_BASIC_INFORMATION info
;
466 size_t resultSize
= VirtualQuery(ptr
, &info
, sizeof(info
));
467 ASSERT_UNUSED(resultSize
, resultSize
== sizeof(info
));
469 size_t decommitSize
= min
<size_t>(info
.RegionSize
, end
- ptr
);
470 BOOL success
= VirtualFree(ptr
, decommitSize
, MEM_DECOMMIT
);
471 ASSERT_UNUSED(success
, success
);
478 // Platforms that don't support returning memory use an empty inline version of TCMalloc_SystemRelease
479 // declared in TCSystemAlloc.h
483 #if HAVE(MADV_FREE_REUSE)
485 void TCMalloc_SystemCommit(void* start
, size_t length
)
487 while (madvise(start
, length
, MADV_FREE_REUSE
) == -1 && errno
== EAGAIN
) { }
490 #elif HAVE(VIRTUALALLOC)
492 void TCMalloc_SystemCommit(void* start
, size_t length
)
494 if (VirtualAlloc(start
, length
, MEM_COMMIT
, PAGE_READWRITE
) == start
)
497 // The commit may fail if the memory region consists of allocations
498 // from more than one call to VirtualAlloc. In this case, fall back to
499 // using VirtualQuery to retrieve the allocation boundaries and commit them
500 // each individually.
502 char* ptr
= static_cast<char*>(start
);
503 char* end
= ptr
+ length
;
504 MEMORY_BASIC_INFORMATION info
;
506 size_t resultSize
= VirtualQuery(ptr
, &info
, sizeof(info
));
507 ASSERT_UNUSED(resultSize
, resultSize
== sizeof(info
));
509 size_t commitSize
= min
<size_t>(info
.RegionSize
, end
- ptr
);
510 void* newAddress
= VirtualAlloc(ptr
, commitSize
, MEM_COMMIT
, PAGE_READWRITE
);
511 ASSERT_UNUSED(newAddress
, newAddress
== ptr
);
518 // Platforms that don't need to explicitly commit memory use an empty inline version of TCMalloc_SystemCommit
519 // declared in TCSystemAlloc.h