Not reviewed.
[WebKit-https.git] / JavaScriptCore / wtf / TCSystemAlloc.cpp
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
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 <unistd.h>
45 #include <sys/mman.h>
46 #endif
47 #include <fcntl.h>
48 #include "TCSystemAlloc.h"
49 #include "TCSpinLock.h"
50
51 #ifndef MAP_ANONYMOUS
52 #define MAP_ANONYMOUS MAP_ANON
53 #endif
54
55 // Structure for discovering alignment
56 union MemoryAligner {
57   void*  p;
58   double d;
59   size_t s;
60 };
61
62 static SpinLock spinlock = SPINLOCK_INITIALIZER;
63   
64 // Page size is initialized on demand
65 static size_t pagesize = 0;
66
67 // Configuration parameters.
68 //
69 // if use_devmem is true, either use_sbrk or use_mmap must also be true.
70 // For 2.2 kernels, it looks like the sbrk address space (500MBish) and
71 // the mmap address space (1300MBish) are disjoint, so we need both allocators
72 // to get as much virtual memory as possible.
73 #ifndef WTF_CHANGES
74 static bool use_devmem = false;
75 #endif
76 static bool use_sbrk = false;
77 static bool use_mmap = true;
78 #if HAVE(VIRTUALALLOC)
79 static bool use_VirtualAlloc = true;
80 #endif
81
82 // Flags to keep us from retrying allocators that failed.
83 static bool devmem_failure = false;
84 static bool sbrk_failure = false;
85 static bool mmap_failure = false;
86 static bool VirtualAlloc_failure = false;
87
88 #ifndef WTF_CHANGES
89 DEFINE_int32(malloc_devmem_start, 0,
90              "Physical memory starting location in MB for /dev/mem allocation."
91              "  Setting this to 0 disables /dev/mem allocation");
92 DEFINE_int32(malloc_devmem_limit, 0,
93              "Physical memory limit location in MB for /dev/mem allocation."
94              "  Setting this to 0 means no limit.");
95 #else
96 static const int32_t FLAGS_malloc_devmem_start = 0;
97 static const int32_t FLAGS_malloc_devmem_limit = 0;
98 #endif
99
100 #if HAVE(SBRK)
101
102 static void* TrySbrk(size_t size, size_t alignment) {
103   size = ((size + alignment - 1) / alignment) * alignment;
104   void* result = sbrk(size);
105   if (result == reinterpret_cast<void*>(-1)) {
106     sbrk_failure = true;
107     return NULL;
108   }
109
110   // Is it aligned?
111   uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
112   if ((ptr & (alignment-1)) == 0)  return result;
113
114   // Try to get more memory for alignment
115   size_t extra = alignment - (ptr & (alignment-1));
116   void* r2 = sbrk(extra);
117   if (reinterpret_cast<uintptr_t>(r2) == (ptr + size)) {
118     // Contiguous with previous result
119     return reinterpret_cast<void*>(ptr + extra);
120   }
121
122   // Give up and ask for "size + alignment - 1" bytes so
123   // that we can find an aligned region within it.
124   result = sbrk(size + alignment - 1);
125   if (result == reinterpret_cast<void*>(-1)) {
126     sbrk_failure = true;
127     return NULL;
128   }
129   ptr = reinterpret_cast<uintptr_t>(result);
130   if ((ptr & (alignment-1)) != 0) {
131     ptr += alignment - (ptr & (alignment-1));
132   }
133   return reinterpret_cast<void*>(ptr);
134 }
135
136 #endif /* HAVE(SBRK) */
137
138 #if HAVE(MMAP)
139
140 static void* TryMmap(size_t size, size_t alignment) {
141   // Enforce page alignment
142   if (pagesize == 0) pagesize = getpagesize();
143   if (alignment < pagesize) alignment = pagesize;
144   size = ((size + alignment - 1) / alignment) * alignment;
145
146   // Ask for extra memory if alignment > pagesize
147   size_t extra = 0;
148   if (alignment > pagesize) {
149     extra = alignment - pagesize;
150   }
151   void* result = mmap(NULL, size + extra,
152                       PROT_READ|PROT_WRITE,
153                       MAP_PRIVATE|MAP_ANONYMOUS,
154                       -1, 0);
155   if (result == reinterpret_cast<void*>(MAP_FAILED)) {
156     mmap_failure = true;
157     return NULL;
158   }
159
160   // Adjust the return memory so it is aligned
161   uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
162   size_t adjust = 0;
163   if ((ptr & (alignment - 1)) != 0) {
164     adjust = alignment - (ptr & (alignment - 1));
165   }
166
167   // Return the unused memory to the system
168   if (adjust > 0) {
169     munmap(reinterpret_cast<void*>(ptr), adjust);
170   }
171   if (adjust < extra) {
172     munmap(reinterpret_cast<void*>(ptr + adjust + size), extra - adjust);
173   }
174
175   ptr += adjust;
176   return reinterpret_cast<void*>(ptr);
177 }
178
179 #endif /* HAVE(MMAP) */
180
181 #if HAVE(VIRTUALALLOC)
182
183 static void* TryVirtualAlloc(size_t size, size_t alignment) {
184   // Enforce page alignment
185   if (pagesize == 0) {
186     SYSTEM_INFO system_info;
187     GetSystemInfo(&system_info);
188     pagesize = system_info.dwPageSize;
189   }
190   if (alignment < pagesize) alignment = pagesize;
191   size = ((size + alignment - 1) / alignment) * alignment;
192
193   // Ask for extra memory if alignment > pagesize
194   size_t extra = 0;
195   if (alignment > pagesize) {
196     extra = alignment - pagesize;
197   }
198   void* result = VirtualAlloc(NULL, size + extra,
199                               MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, 
200                               PAGE_READWRITE);
201
202   if (result == NULL) {
203     VirtualAlloc_failure = true;
204     return NULL;
205   }
206
207   // Adjust the return memory so it is aligned
208   uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
209   size_t adjust = 0;
210   if ((ptr & (alignment - 1)) != 0) {
211     adjust = alignment - (ptr & (alignment - 1));
212   }
213
214   // Return the unused memory to the system - we'd like to release but the best we can do
215   // is decommit, since Windows only lets you free the whole allocation.
216   if (adjust > 0) {
217     VirtualFree(reinterpret_cast<void*>(ptr), adjust, MEM_DECOMMIT);
218   }
219   if (adjust < extra) {
220     VirtualFree(reinterpret_cast<void*>(ptr + adjust + size), extra-adjust, MEM_DECOMMIT);
221   }
222
223   ptr += adjust;
224   return reinterpret_cast<void*>(ptr);
225 }
226
227 #endif /* HAVE(MMAP) */
228
229 #ifndef WTF_CHANGES
230 static void* TryDevMem(size_t size, size_t alignment) {
231   static bool initialized = false;
232   static off_t physmem_base;  // next physical memory address to allocate
233   static off_t physmem_limit; // maximum physical address allowed
234   static int physmem_fd;      // file descriptor for /dev/mem
235   
236   // Check if we should use /dev/mem allocation.  Note that it may take
237   // a while to get this flag initialized, so meanwhile we fall back to
238   // the next allocator.  (It looks like 7MB gets allocated before
239   // this flag gets initialized -khr.)
240   if (FLAGS_malloc_devmem_start == 0) {
241     // NOTE: not a devmem_failure - we'd like TCMalloc_SystemAlloc to
242     // try us again next time.
243     return NULL;
244   }
245   
246   if (!initialized) {
247     physmem_fd = open("/dev/mem", O_RDWR);
248     if (physmem_fd < 0) {
249       devmem_failure = true;
250       return NULL;
251     }
252     physmem_base = FLAGS_malloc_devmem_start*1024LL*1024LL;
253     physmem_limit = FLAGS_malloc_devmem_limit*1024LL*1024LL;
254     initialized = true;
255   }
256   
257   // Enforce page alignment
258   if (pagesize == 0) pagesize = getpagesize();
259   if (alignment < pagesize) alignment = pagesize;
260   size = ((size + alignment - 1) / alignment) * alignment;
261
262   // Ask for extra memory if alignment > pagesize
263   size_t extra = 0;
264   if (alignment > pagesize) {
265     extra = alignment - pagesize;
266   }
267   
268   // check to see if we have any memory left
269   if (physmem_limit != 0 && physmem_base + size + extra > physmem_limit) {
270     devmem_failure = true;
271     return NULL;
272   }
273   void *result = mmap(0, size + extra, PROT_WRITE|PROT_READ,
274                       MAP_SHARED, physmem_fd, physmem_base);
275   if (result == reinterpret_cast<void*>(MAP_FAILED)) {
276     devmem_failure = true;
277     return NULL;
278   }
279   uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
280   
281   // Adjust the return memory so it is aligned
282   size_t adjust = 0;
283   if ((ptr & (alignment - 1)) != 0) {
284     adjust = alignment - (ptr & (alignment - 1));
285   }
286   
287   // Return the unused virtual memory to the system
288   if (adjust > 0) {
289     munmap(reinterpret_cast<void*>(ptr), adjust);
290   }
291   if (adjust < extra) {
292     munmap(reinterpret_cast<void*>(ptr + adjust + size), extra - adjust);
293   }
294   
295   ptr += adjust;
296   physmem_base += adjust + size;
297   
298   return reinterpret_cast<void*>(ptr);
299 }
300 #endif
301
302 void* TCMalloc_SystemAlloc(size_t size, size_t alignment) {
303 #ifndef WTF_CHANGES
304   if (TCMallocDebug::level >= TCMallocDebug::kVerbose) {
305     MESSAGE("TCMalloc_SystemAlloc(%" PRIuS ", %" PRIuS")\n", 
306             size, alignment);
307   }
308 #endif
309   SpinLockHolder lock_holder(&spinlock);
310
311   // Enforce minimum alignment
312   if (alignment < sizeof(MemoryAligner)) alignment = sizeof(MemoryAligner);
313
314   // Try twice, once avoiding allocators that failed before, and once
315   // more trying all allocators even if they failed before.
316   for (int i = 0; i < 2; i++) {
317
318 #ifndef WTF_CHANGES
319     if (use_devmem && !devmem_failure) {
320       void* result = TryDevMem(size, alignment);
321       if (result != NULL) return result;
322     }
323 #endif
324     
325 #if HAVE(SBRK)
326     if (use_sbrk && !sbrk_failure) {
327       void* result = TrySbrk(size, alignment);
328       if (result != NULL) return result;
329     }
330 #endif
331
332 #if HAVE(MMAP)    
333     if (use_mmap && !mmap_failure) {
334       void* result = TryMmap(size, alignment);
335       if (result != NULL) return result;
336     }
337 #endif
338
339 #if HAVE(VIRTUALALLOC)
340     if (use_VirtualAlloc && !VirtualAlloc_failure) {
341       void* result = TryVirtualAlloc(size, alignment);
342       if (result != NULL) return result;
343     }
344 #endif
345
346     // nothing worked - reset failure flags and try again
347     devmem_failure = false;
348     sbrk_failure = false;
349     mmap_failure = false;
350     VirtualAlloc_failure = false;
351   }
352   return NULL;
353 }