]> git.saurik.com Git - apple/javascriptcore.git/blame - wtf/TCSystemAlloc.cpp
JavaScriptCore-721.26.tar.gz
[apple/javascriptcore.git] / wtf / TCSystemAlloc.cpp
CommitLineData
b37bf2e1
A
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"
ba379fdc
A
34#include "TCSystemAlloc.h"
35
36#include <algorithm>
37#include <fcntl.h>
38#include "Assertions.h"
39#include "TCSpinLock.h"
40#include "UnusedParam.h"
f9bf01c6 41#include "VMTags.h"
ba379fdc 42
b37bf2e1
A
43#if HAVE(STDINT_H)
44#include <stdint.h>
45#elif HAVE(INTTYPES_H)
46#include <inttypes.h>
47#else
48#include <sys/types.h>
49#endif
ba379fdc 50
f9bf01c6 51#if OS(WINDOWS)
b37bf2e1
A
52#include "windows.h"
53#else
54#include <errno.h>
55#include <unistd.h>
56#include <sys/mman.h>
57#endif
b37bf2e1
A
58
59#ifndef MAP_ANONYMOUS
60#define MAP_ANONYMOUS MAP_ANON
61#endif
62
ba379fdc
A
63using namespace std;
64
b37bf2e1
A
65// Structure for discovering alignment
66union MemoryAligner {
67 void* p;
68 double d;
69 size_t s;
70};
71
72static SpinLock spinlock = SPINLOCK_INITIALIZER;
73
74// Page size is initialized on demand
75static size_t pagesize = 0;
76
77// Configuration parameters.
78//
79// if use_devmem is true, either use_sbrk or use_mmap must also be true.
80// For 2.2 kernels, it looks like the sbrk address space (500MBish) and
81// the mmap address space (1300MBish) are disjoint, so we need both allocators
82// to get as much virtual memory as possible.
83#ifndef WTF_CHANGES
84static bool use_devmem = false;
85#endif
86
87#if HAVE(SBRK)
88static bool use_sbrk = false;
89#endif
90
91#if HAVE(MMAP)
92static bool use_mmap = true;
93#endif
94
95#if HAVE(VIRTUALALLOC)
96static bool use_VirtualAlloc = true;
97#endif
98
99// Flags to keep us from retrying allocators that failed.
100static bool devmem_failure = false;
101static bool sbrk_failure = false;
102static bool mmap_failure = false;
103static bool VirtualAlloc_failure = false;
104
105#ifndef WTF_CHANGES
106DEFINE_int32(malloc_devmem_start, 0,
107 "Physical memory starting location in MB for /dev/mem allocation."
108 " Setting this to 0 disables /dev/mem allocation");
109DEFINE_int32(malloc_devmem_limit, 0,
110 "Physical memory limit location in MB for /dev/mem allocation."
111 " Setting this to 0 means no limit.");
112#else
113static const int32_t FLAGS_malloc_devmem_start = 0;
114static const int32_t FLAGS_malloc_devmem_limit = 0;
115#endif
116
117#if HAVE(SBRK)
118
119static void* TrySbrk(size_t size, size_t *actual_size, size_t alignment) {
120 size = ((size + alignment - 1) / alignment) * alignment;
121
122 // could theoretically return the "extra" bytes here, but this
123 // is simple and correct.
124 if (actual_size)
125 *actual_size = size;
126
127 void* result = sbrk(size);
128 if (result == reinterpret_cast<void*>(-1)) {
129 sbrk_failure = true;
130 return NULL;
131 }
132
133 // Is it aligned?
134 uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
135 if ((ptr & (alignment-1)) == 0) return result;
136
137 // Try to get more memory for alignment
138 size_t extra = alignment - (ptr & (alignment-1));
139 void* r2 = sbrk(extra);
140 if (reinterpret_cast<uintptr_t>(r2) == (ptr + size)) {
141 // Contiguous with previous result
142 return reinterpret_cast<void*>(ptr + extra);
143 }
144
145 // Give up and ask for "size + alignment - 1" bytes so
146 // that we can find an aligned region within it.
147 result = sbrk(size + alignment - 1);
148 if (result == reinterpret_cast<void*>(-1)) {
149 sbrk_failure = true;
150 return NULL;
151 }
152 ptr = reinterpret_cast<uintptr_t>(result);
153 if ((ptr & (alignment-1)) != 0) {
154 ptr += alignment - (ptr & (alignment-1));
155 }
156 return reinterpret_cast<void*>(ptr);
157}
158
159#endif /* HAVE(SBRK) */
160
161#if HAVE(MMAP)
162
163static void* TryMmap(size_t size, size_t *actual_size, size_t alignment) {
164 // Enforce page alignment
165 if (pagesize == 0) pagesize = getpagesize();
166 if (alignment < pagesize) alignment = pagesize;
167 size = ((size + alignment - 1) / alignment) * alignment;
168
169 // could theoretically return the "extra" bytes here, but this
170 // is simple and correct.
171 if (actual_size)
172 *actual_size = size;
173
174 // Ask for extra memory if alignment > pagesize
175 size_t extra = 0;
176 if (alignment > pagesize) {
177 extra = alignment - pagesize;
178 }
179 void* result = mmap(NULL, size + extra,
9dae56ea 180 PROT_READ | PROT_WRITE,
b37bf2e1 181 MAP_PRIVATE|MAP_ANONYMOUS,
f9bf01c6 182 VM_TAG_FOR_TCMALLOC_MEMORY, 0);
b37bf2e1
A
183 if (result == reinterpret_cast<void*>(MAP_FAILED)) {
184 mmap_failure = true;
185 return NULL;
186 }
187
188 // Adjust the return memory so it is aligned
189 uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
190 size_t adjust = 0;
191 if ((ptr & (alignment - 1)) != 0) {
192 adjust = alignment - (ptr & (alignment - 1));
193 }
194
195 // Return the unused memory to the system
196 if (adjust > 0) {
197 munmap(reinterpret_cast<void*>(ptr), adjust);
198 }
199 if (adjust < extra) {
200 munmap(reinterpret_cast<void*>(ptr + adjust + size), extra - adjust);
201 }
202
203 ptr += adjust;
204 return reinterpret_cast<void*>(ptr);
205}
206
207#endif /* HAVE(MMAP) */
208
209#if HAVE(VIRTUALALLOC)
210
211static void* TryVirtualAlloc(size_t size, size_t *actual_size, size_t alignment) {
212 // Enforce page alignment
213 if (pagesize == 0) {
214 SYSTEM_INFO system_info;
215 GetSystemInfo(&system_info);
216 pagesize = system_info.dwPageSize;
217 }
218
219 if (alignment < pagesize) alignment = pagesize;
220 size = ((size + alignment - 1) / alignment) * alignment;
221
222 // could theoretically return the "extra" bytes here, but this
223 // is simple and correct.
224 if (actual_size)
225 *actual_size = size;
226
227 // Ask for extra memory if alignment > pagesize
228 size_t extra = 0;
229 if (alignment > pagesize) {
230 extra = alignment - pagesize;
231 }
232 void* result = VirtualAlloc(NULL, size + extra,
233 MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
234 PAGE_READWRITE);
235
236 if (result == NULL) {
237 VirtualAlloc_failure = true;
238 return NULL;
239 }
240
241 // Adjust the return memory so it is aligned
242 uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
243 size_t adjust = 0;
244 if ((ptr & (alignment - 1)) != 0) {
245 adjust = alignment - (ptr & (alignment - 1));
246 }
247
248 // Return the unused memory to the system - we'd like to release but the best we can do
249 // is decommit, since Windows only lets you free the whole allocation.
250 if (adjust > 0) {
251 VirtualFree(reinterpret_cast<void*>(ptr), adjust, MEM_DECOMMIT);
252 }
253 if (adjust < extra) {
254 VirtualFree(reinterpret_cast<void*>(ptr + adjust + size), extra-adjust, MEM_DECOMMIT);
255 }
256
257 ptr += adjust;
258 return reinterpret_cast<void*>(ptr);
259}
260
261#endif /* HAVE(MMAP) */
262
263#ifndef WTF_CHANGES
264static void* TryDevMem(size_t size, size_t *actual_size, size_t alignment) {
265 static bool initialized = false;
266 static off_t physmem_base; // next physical memory address to allocate
267 static off_t physmem_limit; // maximum physical address allowed
268 static int physmem_fd; // file descriptor for /dev/mem
269
270 // Check if we should use /dev/mem allocation. Note that it may take
271 // a while to get this flag initialized, so meanwhile we fall back to
272 // the next allocator. (It looks like 7MB gets allocated before
273 // this flag gets initialized -khr.)
274 if (FLAGS_malloc_devmem_start == 0) {
275 // NOTE: not a devmem_failure - we'd like TCMalloc_SystemAlloc to
276 // try us again next time.
277 return NULL;
278 }
279
280 if (!initialized) {
281 physmem_fd = open("/dev/mem", O_RDWR);
282 if (physmem_fd < 0) {
283 devmem_failure = true;
284 return NULL;
285 }
286 physmem_base = FLAGS_malloc_devmem_start*1024LL*1024LL;
287 physmem_limit = FLAGS_malloc_devmem_limit*1024LL*1024LL;
288 initialized = true;
289 }
290
291 // Enforce page alignment
292 if (pagesize == 0) pagesize = getpagesize();
293 if (alignment < pagesize) alignment = pagesize;
294 size = ((size + alignment - 1) / alignment) * alignment;
295
296 // could theoretically return the "extra" bytes here, but this
297 // is simple and correct.
298 if (actual_size)
299 *actual_size = size;
300
301 // Ask for extra memory if alignment > pagesize
302 size_t extra = 0;
303 if (alignment > pagesize) {
304 extra = alignment - pagesize;
305 }
306
307 // check to see if we have any memory left
308 if (physmem_limit != 0 && physmem_base + size + extra > physmem_limit) {
309 devmem_failure = true;
310 return NULL;
311 }
9dae56ea 312 void *result = mmap(0, size + extra, PROT_READ | PROT_WRITE,
b37bf2e1
A
313 MAP_SHARED, physmem_fd, physmem_base);
314 if (result == reinterpret_cast<void*>(MAP_FAILED)) {
315 devmem_failure = true;
316 return NULL;
317 }
318 uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
319
320 // Adjust the return memory so it is aligned
321 size_t adjust = 0;
322 if ((ptr & (alignment - 1)) != 0) {
323 adjust = alignment - (ptr & (alignment - 1));
324 }
325
326 // Return the unused virtual memory to the system
327 if (adjust > 0) {
328 munmap(reinterpret_cast<void*>(ptr), adjust);
329 }
330 if (adjust < extra) {
331 munmap(reinterpret_cast<void*>(ptr + adjust + size), extra - adjust);
332 }
333
334 ptr += adjust;
335 physmem_base += adjust + size;
336
337 return reinterpret_cast<void*>(ptr);
338}
339#endif
340
341void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size, size_t alignment) {
342 // Discard requests that overflow
343 if (size + alignment < size) return NULL;
344
345 SpinLockHolder lock_holder(&spinlock);
346
347 // Enforce minimum alignment
348 if (alignment < sizeof(MemoryAligner)) alignment = sizeof(MemoryAligner);
349
350 // Try twice, once avoiding allocators that failed before, and once
351 // more trying all allocators even if they failed before.
352 for (int i = 0; i < 2; i++) {
353
354#ifndef WTF_CHANGES
355 if (use_devmem && !devmem_failure) {
356 void* result = TryDevMem(size, actual_size, alignment);
357 if (result != NULL) return result;
358 }
359#endif
360
361#if HAVE(SBRK)
362 if (use_sbrk && !sbrk_failure) {
363 void* result = TrySbrk(size, actual_size, alignment);
364 if (result != NULL) return result;
365 }
366#endif
367
368#if HAVE(MMAP)
369 if (use_mmap && !mmap_failure) {
370 void* result = TryMmap(size, actual_size, alignment);
371 if (result != NULL) return result;
372 }
373#endif
374
375#if HAVE(VIRTUALALLOC)
376 if (use_VirtualAlloc && !VirtualAlloc_failure) {
377 void* result = TryVirtualAlloc(size, actual_size, alignment);
378 if (result != NULL) return result;
379 }
380#endif
381
382 // nothing worked - reset failure flags and try again
383 devmem_failure = false;
384 sbrk_failure = false;
385 mmap_failure = false;
386 VirtualAlloc_failure = false;
387 }
388 return NULL;
389}
390
ba379fdc
A
391#if HAVE(MADV_FREE_REUSE)
392
393void TCMalloc_SystemRelease(void* start, size_t length)
394{
395 while (madvise(start, length, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN) { }
396}
397
398#elif HAVE(MADV_FREE) || HAVE(MADV_DONTNEED)
399
b37bf2e1
A
400void TCMalloc_SystemRelease(void* start, size_t length)
401{
ba379fdc
A
402 // MADV_FREE clears the modified bit on pages, which allows
403 // them to be discarded immediately.
9dae56ea 404#if HAVE(MADV_FREE)
ba379fdc 405 const int advice = MADV_FREE;
9dae56ea 406#else
ba379fdc 407 const int advice = MADV_DONTNEED;
9dae56ea 408#endif
b37bf2e1
A
409 if (FLAGS_malloc_devmem_start) {
410 // It's not safe to use MADV_DONTNEED if we've been mapping
411 // /dev/mem for heap memory
412 return;
413 }
414 if (pagesize == 0) pagesize = getpagesize();
415 const size_t pagemask = pagesize - 1;
416
417 size_t new_start = reinterpret_cast<size_t>(start);
418 size_t end = new_start + length;
419 size_t new_end = end;
420
421 // Round up the starting address and round down the ending address
422 // to be page aligned:
423 new_start = (new_start + pagesize - 1) & ~pagemask;
424 new_end = new_end & ~pagemask;
425
426 ASSERT((new_start & pagemask) == 0);
427 ASSERT((new_end & pagemask) == 0);
428 ASSERT(new_start >= reinterpret_cast<size_t>(start));
429 ASSERT(new_end <= end);
430
431 if (new_end > new_start) {
432 // Note -- ignoring most return codes, because if this fails it
433 // doesn't matter...
9dae56ea
A
434 while (madvise(reinterpret_cast<char*>(new_start), new_end - new_start,
435 advice) == -1 &&
b37bf2e1
A
436 errno == EAGAIN) {
437 // NOP
438 }
b37bf2e1 439 }
ba379fdc 440}
b37bf2e1 441
ba379fdc
A
442#elif HAVE(MMAP)
443
444void TCMalloc_SystemRelease(void* start, size_t length)
445{
9bcd318d 446 void* newAddress = mmap(start, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
b37bf2e1 447 // If the mmap failed then that's ok, we just won't return the memory to the system.
9dae56ea 448 ASSERT_UNUSED(newAddress, newAddress == start || newAddress == reinterpret_cast<void*>(MAP_FAILED));
ba379fdc
A
449}
450
451#elif HAVE(VIRTUALALLOC)
452
453void TCMalloc_SystemRelease(void* start, size_t length)
454{
455 if (VirtualFree(start, length, MEM_DECOMMIT))
456 return;
457
458 // The decommit may fail if the memory region consists of allocations
459 // from more than one call to VirtualAlloc. In this case, fall back to
460 // using VirtualQuery to retrieve the allocation boundaries and decommit
461 // them each individually.
462
463 char* ptr = static_cast<char*>(start);
464 char* end = ptr + length;
465 MEMORY_BASIC_INFORMATION info;
466 while (ptr < end) {
467 size_t resultSize = VirtualQuery(ptr, &info, sizeof(info));
468 ASSERT_UNUSED(resultSize, resultSize == sizeof(info));
469
470 size_t decommitSize = min<size_t>(info.RegionSize, end - ptr);
471 BOOL success = VirtualFree(ptr, decommitSize, MEM_DECOMMIT);
472 ASSERT_UNUSED(success, success);
473 ptr += decommitSize;
474 }
475}
476
477#else
478
479// Platforms that don't support returning memory use an empty inline version of TCMalloc_SystemRelease
480// declared in TCSystemAlloc.h
9dae56ea 481
9dae56ea 482#endif
ba379fdc
A
483
484#if HAVE(MADV_FREE_REUSE)
485
486void TCMalloc_SystemCommit(void* start, size_t length)
487{
488 while (madvise(start, length, MADV_FREE_REUSE) == -1 && errno == EAGAIN) { }
9dae56ea
A
489}
490
ba379fdc
A
491#elif HAVE(VIRTUALALLOC)
492
493void TCMalloc_SystemCommit(void* start, size_t length)
9dae56ea 494{
ba379fdc
A
495 if (VirtualAlloc(start, length, MEM_COMMIT, PAGE_READWRITE) == start)
496 return;
497
498 // The commit may fail if the memory region consists of allocations
499 // from more than one call to VirtualAlloc. In this case, fall back to
500 // using VirtualQuery to retrieve the allocation boundaries and commit them
501 // each individually.
502
503 char* ptr = static_cast<char*>(start);
504 char* end = ptr + length;
505 MEMORY_BASIC_INFORMATION info;
506 while (ptr < end) {
507 size_t resultSize = VirtualQuery(ptr, &info, sizeof(info));
508 ASSERT_UNUSED(resultSize, resultSize == sizeof(info));
509
510 size_t commitSize = min<size_t>(info.RegionSize, end - ptr);
511 void* newAddress = VirtualAlloc(ptr, commitSize, MEM_COMMIT, PAGE_READWRITE);
512 ASSERT_UNUSED(newAddress, newAddress == ptr);
513 ptr += commitSize;
514 }
b37bf2e1 515}
ba379fdc
A
516
517#else
518
519// Platforms that don't need to explicitly commit memory use an empty inline version of TCMalloc_SystemCommit
520// declared in TCSystemAlloc.h
521
9dae56ea 522#endif