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