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