1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=78:
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
6 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 :
8 : #include "jstypes.h"
9 :
10 : #include "js/Utility.h"
11 : #include "gc/Memory.h"
12 :
13 : namespace js {
14 : namespace gc {
15 :
16 : #if defined(XP_WIN)
17 : #include "jswin.h"
18 :
19 : static size_t AllocationGranularity = 0;
20 :
21 : void
22 : InitMemorySubsystem()
23 : {
24 : SYSTEM_INFO sysinfo;
25 : GetSystemInfo(&sysinfo);
26 : JS_OPT_ASSERT(sysinfo.dwPageSize == PageSize);
27 : AllocationGranularity = sysinfo.dwAllocationGranularity;
28 : }
29 :
30 : void *
31 : MapAlignedPages(size_t size, size_t alignment)
32 : {
33 : JS_ASSERT(size >= alignment);
34 : JS_ASSERT(size % alignment == 0);
35 : JS_ASSERT(size % PageSize == 0);
36 : JS_ASSERT(alignment % AllocationGranularity == 0);
37 :
38 : /* Special case: If we want allocation alignment, no further work is needed. */
39 : if (alignment == AllocationGranularity) {
40 : return VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
41 : }
42 :
43 : /*
44 : * Windows requires that there be a 1:1 mapping between VM allocation
45 : * and deallocation operations. Therefore, take care here to acquire the
46 : * final result via one mapping operation. This means unmapping any
47 : * preliminary result that is not correctly aligned.
48 : */
49 : void *p = NULL;
50 : while (!p) {
51 : /*
52 : * Over-allocate in order to map a memory region that is
53 : * definitely large enough then deallocate and allocate again the
54 : * correct sizee, within the over-sized mapping.
55 : *
56 : * Since we're going to unmap the whole thing anyway, the first
57 : * mapping doesn't have to commit pages.
58 : */
59 : p = VirtualAlloc(NULL, size * 2, MEM_RESERVE, PAGE_READWRITE);
60 : if (!p)
61 : return NULL;
62 : void *chunkStart = (void *)(uintptr_t(p) + (alignment - (uintptr_t(p) % alignment)));
63 : UnmapPages(p, size * 2);
64 : p = VirtualAlloc(chunkStart, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
65 :
66 : /* Failure here indicates a race with another thread, so try again. */
67 : }
68 :
69 : JS_ASSERT(uintptr_t(p) % alignment == 0);
70 : return p;
71 : }
72 :
73 : void
74 : UnmapPages(void *p, size_t size)
75 : {
76 : JS_ALWAYS_TRUE(VirtualFree(p, 0, MEM_RELEASE));
77 : }
78 :
79 : bool
80 : MarkPagesUnused(void *p, size_t size)
81 : {
82 : JS_ASSERT(uintptr_t(p) % PageSize == 0);
83 : LPVOID p2 = VirtualAlloc(p, size, MEM_RESET, PAGE_READWRITE);
84 : return p2 == p;
85 : }
86 :
87 : bool
88 : MarkPagesInUse(void *p, size_t size)
89 : {
90 : JS_ASSERT(uintptr_t(p) % PageSize == 0);
91 : return true;
92 : }
93 :
94 : #elif defined(XP_OS2)
95 :
96 : #define INCL_DOSMEMMGR
97 : #include <os2.h>
98 :
99 : #define JS_GC_HAS_MAP_ALIGN 1
100 : #define OS2_MAX_RECURSIONS 16
101 :
102 : void
103 : InitMemorySubsystem()
104 : {
105 : }
106 :
107 : void
108 : UnmapPages(void *addr, size_t size)
109 : {
110 : if (!DosFreeMem(addr))
111 : return;
112 :
113 : /*
114 : * If DosFreeMem() failed, 'addr' is probably part of an "expensive"
115 : * allocation, so calculate the base address and try again.
116 : */
117 : unsigned long cb = 2 * size;
118 : unsigned long flags;
119 : if (DosQueryMem(addr, &cb, &flags) || cb < size)
120 : return;
121 :
122 : uintptr_t base = reinterpret_cast<uintptr_t>(addr) - ((2 * size) - cb);
123 : DosFreeMem(reinterpret_cast<void*>(base));
124 :
125 : return;
126 : }
127 :
128 : static void *
129 : MapAlignedPagesRecursively(size_t size, size_t alignment, int& recursions)
130 : {
131 : if (++recursions >= OS2_MAX_RECURSIONS)
132 : return NULL;
133 :
134 : void *tmp;
135 : if (DosAllocMem(&tmp, size,
136 : OBJ_ANY | PAG_COMMIT | PAG_READ | PAG_WRITE)) {
137 : JS_ALWAYS_TRUE(DosAllocMem(&tmp, size,
138 : PAG_COMMIT | PAG_READ | PAG_WRITE) == 0);
139 : }
140 : size_t offset = reinterpret_cast<uintptr_t>(tmp) & (alignment - 1);
141 : if (!offset)
142 : return tmp;
143 :
144 : /*
145 : * If there are 'filler' bytes of free space above 'tmp', free 'tmp',
146 : * then reallocate it as a 'filler'-sized block; assuming we're not
147 : * in a race with another thread, the next recursion should succeed.
148 : */
149 : size_t filler = size + alignment - offset;
150 : unsigned long cb = filler;
151 : unsigned long flags = 0;
152 : unsigned long rc = DosQueryMem(&(static_cast<char*>(tmp))[size],
153 : &cb, &flags);
154 : if (!rc && (flags & PAG_FREE) && cb >= filler) {
155 : UnmapPages(tmp, 0);
156 : if (DosAllocMem(&tmp, filler,
157 : OBJ_ANY | PAG_COMMIT | PAG_READ | PAG_WRITE)) {
158 : JS_ALWAYS_TRUE(DosAllocMem(&tmp, filler,
159 : PAG_COMMIT | PAG_READ | PAG_WRITE) == 0);
160 : }
161 : }
162 :
163 : void *p = MapAlignedPagesRecursively(size, alignment, recursions);
164 : UnmapPages(tmp, 0);
165 :
166 : return p;
167 : }
168 :
169 : void *
170 : MapAlignedPages(size_t size, size_t alignment)
171 : {
172 : JS_ASSERT(size >= alignment);
173 : JS_ASSERT(size % alignment == 0);
174 : JS_ASSERT(size % PageSize == 0);
175 : JS_ASSERT(alignment % PageSize == 0);
176 :
177 : int recursions = -1;
178 :
179 : /*
180 : * Make up to OS2_MAX_RECURSIONS attempts to get an aligned block
181 : * of the right size by recursively allocating blocks of unaligned
182 : * free memory until only an aligned allocation is possible.
183 : */
184 : void *p = MapAlignedPagesRecursively(size, alignment, recursions);
185 : if (p)
186 : return p;
187 :
188 : /*
189 : * If memory is heavily fragmented, the recursive strategy may fail;
190 : * instead, use the "expensive" strategy: allocate twice as much
191 : * as requested and return an aligned address within this block.
192 : */
193 : if (DosAllocMem(&p, 2 * size,
194 : OBJ_ANY | PAG_COMMIT | PAG_READ | PAG_WRITE)) {
195 : JS_ALWAYS_TRUE(DosAllocMem(&p, 2 * size,
196 : PAG_COMMIT | PAG_READ | PAG_WRITE) == 0);
197 : }
198 :
199 : uintptr_t addr = reinterpret_cast<uintptr_t>(p);
200 : addr = (addr + (alignment - 1)) & ~(alignment - 1);
201 :
202 : return reinterpret_cast<void *>(addr);
203 : }
204 :
205 : bool
206 : MarkPagesUnused(void *p, size_t size)
207 : {
208 : JS_ASSERT(uintptr_t(p) % PageSize == 0);
209 : return true;
210 : }
211 :
212 : bool
213 : MarkPagesInUse(void *p, size_t size)
214 : {
215 : JS_ASSERT(uintptr_t(p) % PageSize == 0);
216 : return true;
217 : }
218 :
219 : #elif defined(SOLARIS)
220 :
221 : #include <sys/mman.h>
222 : #include <unistd.h>
223 :
224 : #ifndef MAP_NOSYNC
225 : # define MAP_NOSYNC 0
226 : #endif
227 :
228 : void
229 : InitMemorySubsystem()
230 : {
231 : }
232 :
233 : void *
234 : MapAlignedPages(size_t size, size_t alignment)
235 : {
236 : JS_ASSERT(size >= alignment);
237 : JS_ASSERT(size % alignment == 0);
238 : JS_ASSERT(size % PageSize == 0);
239 : JS_ASSERT(alignment % PageSize == 0);
240 :
241 : int prot = PROT_READ | PROT_WRITE;
242 : int flags = MAP_PRIVATE | MAP_ANON | MAP_ALIGN | MAP_NOSYNC;
243 :
244 : void *p = mmap((caddr_t)alignment, size, prot, flags, -1, 0);
245 : if (p == MAP_FAILED)
246 : return NULL;
247 : return p;
248 : }
249 :
250 : void
251 : UnmapPages(void *p, size_t size)
252 : {
253 : JS_ALWAYS_TRUE(0 == munmap((caddr_t)p, size));
254 : }
255 :
256 : bool
257 : MarkPagesUnused(void *p, size_t size)
258 : {
259 : JS_ASSERT(uintptr_t(p) % PageSize == 0);
260 : return true;
261 : }
262 :
263 : bool
264 : MarkPagesInUse(void *p, size_t size)
265 : {
266 : JS_ASSERT(uintptr_t(p) % PageSize == 0);
267 : return true;
268 : }
269 :
270 : #elif defined(XP_UNIX) || defined(XP_MACOSX) || defined(DARWIN)
271 :
272 : #include <sys/mman.h>
273 : #include <unistd.h>
274 :
275 : void
276 19811 : InitMemorySubsystem()
277 : {
278 19811 : JS_OPT_ASSERT(size_t(sysconf(_SC_PAGESIZE)) == PageSize);
279 19811 : }
280 :
281 : void *
282 43620 : MapAlignedPages(size_t size, size_t alignment)
283 : {
284 43620 : JS_ASSERT(size >= alignment);
285 43620 : JS_ASSERT(size % alignment == 0);
286 43620 : JS_ASSERT(size % PageSize == 0);
287 43620 : JS_ASSERT(alignment % PageSize == 0);
288 :
289 43620 : int prot = PROT_READ | PROT_WRITE;
290 43620 : int flags = MAP_PRIVATE | MAP_ANON;
291 :
292 : /* Special case: If we want page alignment, no further work is needed. */
293 43620 : if (alignment == PageSize) {
294 0 : return mmap(NULL, size, prot, flags, -1, 0);
295 : }
296 :
297 : /* Overallocate and unmap the region's edges. */
298 43620 : size_t reqSize = JS_MIN(size + 2 * alignment, 2 * size);
299 43620 : void *region = mmap(NULL, reqSize, prot, flags, -1, 0);
300 43620 : if (region == MAP_FAILED)
301 0 : return NULL;
302 :
303 43620 : uintptr_t regionEnd = uintptr_t(region) + reqSize;
304 43620 : uintptr_t offset = uintptr_t(region) % alignment;
305 43620 : JS_ASSERT(offset < reqSize - size);
306 :
307 43620 : void *front = (void *)(uintptr_t(region) + (alignment - offset));
308 43620 : void *end = (void *)(uintptr_t(front) + size);
309 43620 : if (front != region)
310 43620 : JS_ALWAYS_TRUE(0 == munmap(region, alignment - offset));
311 43620 : if (uintptr_t(end) != regionEnd)
312 20051 : JS_ALWAYS_TRUE(0 == munmap(end, regionEnd - uintptr_t(end)));
313 :
314 43620 : JS_ASSERT(uintptr_t(front) % alignment == 0);
315 43620 : return front;
316 : }
317 :
318 : void
319 43616 : UnmapPages(void *p, size_t size)
320 : {
321 43616 : JS_ALWAYS_TRUE(0 == munmap(p, size));
322 43616 : }
323 :
324 : bool
325 4140 : MarkPagesUnused(void *p, size_t size)
326 : {
327 4140 : JS_ASSERT(uintptr_t(p) % PageSize == 0);
328 4140 : int result = madvise(p, size, MADV_DONTNEED);
329 4140 : return result != -1;
330 : }
331 :
332 : bool
333 0 : MarkPagesInUse(void *p, size_t size)
334 : {
335 0 : JS_ASSERT(uintptr_t(p) % PageSize == 0);
336 0 : return true;
337 : }
338 :
339 : #else
340 : #error "Memory mapping functions are not defined for your OS."
341 : #endif
342 :
343 : } /* namespace gc */
344 : } /* namespace js */
|