Commit | Line | Data |
---|---|---|
587fd086 FP |
1 | /* Heap management routines for GNU Emacs on the Microsoft Windows |
2 | API. Copyright (C) 1994, 2001-2014 Free Software Foundation, Inc. | |
95ed0025 | 3 | |
587fd086 | 4 | This file is part of GNU Emacs. |
95ed0025 | 5 | |
587fd086 FP |
6 | GNU Emacs is free software: you can redistribute it and/or modify |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation, either version 3 of the License, or | |
9 | (at your option) any later version. | |
95ed0025 | 10 | |
587fd086 FP |
11 | GNU Emacs is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
95ed0025 | 15 | |
587fd086 FP |
16 | You should have received a copy of the GNU General Public License |
17 | along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |
95ed0025 | 18 | |
9ec0b715 | 19 | /* |
587fd086 | 20 | Geoff Voelker (voelker@cs.washington.edu) 7-29-94 |
95ed0025 RS |
21 | */ |
22 | ||
587fd086 FP |
23 | /* |
24 | Heavily modified by Fabrice Popineau (fabrice.popineau@gmail.com) 28-02-2014 | |
25 | */ | |
26 | ||
27 | /* | |
28 | Memory allocation scheme for w32/w64: | |
29 | ||
30 | - Buffers are mmap'ed using a very simple emulation of mmap/munmap | |
31 | - During the temacs phase: | |
32 | * we use a private heap declared to be stored into the `dumped_data' | |
33 | * unfortunately, this heap cannot be made growable, so the size of | |
34 | blocks it can allocate is limited to (0x80000 - pagesize) | |
35 | * the blocks that are larger than this are allocated from the end | |
36 | of the `dumped_data' array; there are not so many of them. | |
37 | We use a very simple first-fit scheme to reuse those blocks. | |
38 | * we check that the private heap does not cross the area used | |
39 | by the bigger chunks. | |
40 | - During the emacs phase: | |
41 | * we create a private heap for new memory blocks | |
42 | * we make sure that we never free a block that has been dumped. | |
43 | Freeing a dumped block could work in principle, but may prove | |
44 | unreliable if we distribute binaries of emacs.exe: MS does not | |
45 | guarantee that the heap data structures are the same across all | |
46 | versions of their OS, even though the API is available since XP. */ | |
47 | ||
4838e624 | 48 | #include <config.h> |
95ed0025 | 49 | #include <stdio.h> |
6c572f9a | 50 | #include <errno.h> |
95ed0025 | 51 | |
587fd086 | 52 | #include <sys/mman.h> |
501199a3 | 53 | #include "w32common.h" |
489f9371 | 54 | #include "w32heap.h" |
8dfdd41f | 55 | #include "lisp.h" /* for VALMASK */ |
95ed0025 | 56 | |
587fd086 FP |
57 | /* We chose to leave those declarations here. They are used only in |
58 | this file. The RtlCreateHeap is available since XP. It is located | |
59 | in ntdll.dll and is available with the DDK. People often | |
60 | complained that HeapCreate doesn't offer the ability to create a | |
61 | heap at a given place, which we need here, and which RtlCreateHeap | |
62 | provides. We reproduce here the definitions available with the | |
63 | DDK. */ | |
64 | ||
65 | typedef PVOID (WINAPI * RtlCreateHeap_Proc) ( | |
66 | /* _In_ */ ULONG Flags, | |
67 | /* _In_opt_ */ PVOID HeapBase, | |
68 | /* _In_opt_ */ SIZE_T ReserveSize, | |
69 | /* _In_opt_ */ SIZE_T CommitSize, | |
70 | /* _In_opt_ */ PVOID Lock, | |
71 | /* _In_opt_ */ PVOID Parameters | |
72 | ); | |
73 | ||
74 | typedef LONG NTSTATUS; | |
75 | ||
76 | typedef NTSTATUS | |
77 | (NTAPI * PRTL_HEAP_COMMIT_ROUTINE)( | |
78 | IN PVOID Base, | |
79 | IN OUT PVOID *CommitAddress, | |
80 | IN OUT PSIZE_T CommitSize | |
81 | ); | |
82 | ||
83 | typedef struct _RTL_HEAP_PARAMETERS { | |
84 | ULONG Length; | |
85 | SIZE_T SegmentReserve; | |
86 | SIZE_T SegmentCommit; | |
87 | SIZE_T DeCommitFreeBlockThreshold; | |
88 | SIZE_T DeCommitTotalFreeThreshold; | |
89 | SIZE_T MaximumAllocationSize; | |
90 | SIZE_T VirtualMemoryThreshold; | |
91 | SIZE_T InitialCommit; | |
92 | SIZE_T InitialReserve; | |
93 | PRTL_HEAP_COMMIT_ROUTINE CommitRoutine; | |
94 | SIZE_T Reserved[ 2 ]; | |
95 | } RTL_HEAP_PARAMETERS, *PRTL_HEAP_PARAMETERS; | |
96 | ||
97 | /* We reserve space for dumping emacs lisp byte-code inside a static | |
98 | array. By storing it in an array, the generic mechanism in | |
99 | unexecw32.c will be able to dump it without the need to add a | |
100 | special segment to the executable. In order to be able to do this | |
101 | without losing too much space, we need to create a Windows heap at | |
102 | the specific address of the static array. The RtlCreateHeap | |
103 | available inside the NT kernel since XP will do this. It allows to | |
104 | create a non-growable heap at a specific address. So before | |
105 | dumping, we create a non-growable heap at the address of the | |
106 | dumped_data[] array. After dumping, we reuse memory allocated | |
107 | there without being able to free it (but most of it is not meant to | |
108 | be freed anyway), and we use a new private heap for all new | |
109 | allocations. */ | |
110 | ||
89b36202 EZ |
111 | /* FIXME: Most of the space reserved for dumped_data[] is only used by |
112 | the 1st bootstrap-emacs.exe built while bootstrapping. Once the | |
113 | preloaded Lisp files are byte-compiled, the next loadup uses less | |
114 | than half of the size stated below. It would be nice to find a way | |
115 | to build only the first bootstrap-emacs.exe with the large size, | |
116 | and reset that to a lower value afterwards. */ | |
117 | #ifdef _WIN64 | |
118 | # define DUMPED_HEAP_SIZE (18*1024*1024) | |
119 | #else | |
120 | # define DUMPED_HEAP_SIZE (12*1024*1024) | |
121 | #endif | |
122 | ||
123 | static unsigned char dumped_data[DUMPED_HEAP_SIZE]; | |
e54c8cd1 | 124 | |
30d2b1c2 | 125 | /* Info for managing our preload heap, which is essentially a fixed size |
587fd086 FP |
126 | data area in the executable. */ |
127 | /* Info for keeping track of our heap. */ | |
95ed0025 RS |
128 | unsigned char *data_region_base = NULL; |
129 | unsigned char *data_region_end = NULL; | |
587fd086 | 130 | static DWORD_PTR committed = 0; |
95ed0025 | 131 | |
587fd086 FP |
132 | /* The maximum block size that can be handled by a non-growable w32 |
133 | heap is limited by the MaxBlockSize value below. | |
134 | ||
135 | This point deserves and explanation. | |
136 | ||
137 | The W32 heap allocator can be used for a growable | |
138 | heap or a non-growable one. | |
139 | ||
140 | A growable heap is not compatible with a fixed base address for the | |
141 | heap. Only a non-growable one is. One drawback of non-growable | |
142 | heaps is that they can hold only objects smaller than a certain | |
143 | size (the one defined below). Most of the largest blocks are GC'ed | |
144 | before dumping. In any case and to be safe, we implement a simple | |
145 | first-fit allocation algorithm starting at the end of the | |
146 | dumped_data[] array like depicted below: | |
95ed0025 | 147 | |
587fd086 FP |
148 | ---------------------------------------------- |
149 | | | | | | |
150 | | Private heap |-> <-| Big chunks | | |
151 | | | | | | |
152 | ---------------------------------------------- | |
153 | ^ ^ ^ | |
154 | dumped_data dumped_data bc_limit | |
155 | + committed | |
156 | ||
157 | */ | |
158 | #define HEAP_ENTRY_SHIFT 3 | |
159 | #define PAGE_SIZE 0x1000 | |
160 | #define MaxBlockSize (0x80000 - PAGE_SIZE) | |
161 | ||
162 | #define MAX_BLOCKS 0x40 | |
163 | ||
164 | static struct | |
95ed0025 | 165 | { |
587fd086 FP |
166 | unsigned char *address; |
167 | size_t size; | |
168 | DWORD occupied; | |
169 | } blocks[MAX_BLOCKS]; | |
170 | ||
171 | static DWORD blocks_number = 0; | |
172 | static unsigned char *bc_limit; | |
173 | ||
174 | /* Handle for the private heap: | |
175 | - inside the dumped_data[] array before dump, | |
176 | - outside of it after dump. | |
177 | */ | |
178 | HANDLE heap = NULL; | |
179 | ||
180 | /* We redirect the standard allocation functions. */ | |
181 | malloc_fn the_malloc_fn; | |
182 | realloc_fn the_realloc_fn; | |
183 | free_fn the_free_fn; | |
95ed0025 | 184 | |
587fd086 FP |
185 | /* It doesn't seem to be useful to allocate from a file mapping. |
186 | It would be if the memory was shared. | |
187 | http://stackoverflow.com/questions/307060/what-is-the-purpose-of-allocating-pages-in-the-pagefile-with-createfilemapping */ | |
188 | ||
189 | /* This is the function to commit memory when the heap allocator | |
190 | claims for new memory. Before dumping, we allocate space | |
191 | from the fixed size dumped_data[] array. | |
192 | */ | |
193 | NTSTATUS NTAPI | |
194 | dumped_data_commit (PVOID Base, PVOID *CommitAddress, PSIZE_T CommitSize) | |
011db670 | 195 | { |
587fd086 FP |
196 | /* This is used before dumping. |
197 | ||
198 | The private heap is stored at dumped_data[] address. | |
199 | We commit contiguous areas of the dumped_data array | |
200 | as requests arrive. */ | |
201 | *CommitAddress = data_region_base + committed; | |
202 | committed += *CommitSize; | |
203 | if (((unsigned char *)(*CommitAddress)) + *CommitSize >= bc_limit) | |
709fd16b | 204 | { |
587fd086 FP |
205 | /* Check that the private heap area does not overlap the big |
206 | chunks area. */ | |
207 | fprintf(stderr, | |
208 | "dumped_data_commit: memory exhausted.\nEnlarge dumped_data[]!\n"); | |
209 | exit (-1); | |
709fd16b | 210 | } |
587fd086 | 211 | return 0; |
011db670 | 212 | } |
587fd086 FP |
213 | |
214 | /* Heap creation. */ | |
215 | ||
904e7cf8 | 216 | /* We want to turn on Low Fragmentation Heap for XP and older systems. |
587fd086 FP |
217 | MinGW32 lacks those definitions. */ |
218 | #ifndef _W64 | |
219 | typedef enum _HEAP_INFORMATION_CLASS { | |
220 | HeapCompatibilityInformation | |
221 | } HEAP_INFORMATION_CLASS; | |
222 | ||
223 | typedef WINBASEAPI BOOL (WINAPI * HeapSetInformation_Proc)(HANDLE,HEAP_INFORMATION_CLASS,PVOID,SIZE_T); | |
224 | #endif | |
225 | ||
226 | void | |
227 | init_heap (void) | |
fab624aa | 228 | { |
587fd086 FP |
229 | if (using_dynamic_heap) |
230 | { | |
231 | unsigned long enable_lfh = 2; | |
232 | ||
233 | /* After dumping, use a new private heap. We explicitly enable | |
e67cf8c6 EZ |
234 | the low fragmentation heap (LFH) here, for the sake of pre |
235 | Vista versions. Note: this will harmlessly fail on Vista and | |
236 | later, where the low-fragmentation heap is enabled by | |
587fd086 FP |
237 | default. It will also fail on pre-Vista versions when Emacs |
238 | is run under a debugger; set _NO_DEBUG_HEAP=1 in the | |
239 | environment before starting GDB to get low fragmentation heap | |
240 | on XP and older systems, for the price of losing "certain | |
241 | heap debug options"; for the details see | |
242 | http://msdn.microsoft.com/en-us/library/windows/desktop/aa366705%28v=vs.85%29.aspx. */ | |
243 | data_region_end = data_region_base; | |
244 | ||
245 | /* Create the private heap. */ | |
246 | heap = HeapCreate(0, 0, 0); | |
247 | ||
248 | #ifndef _W64 | |
904e7cf8 | 249 | /* Set the low-fragmentation heap for OS before Vista. */ |
587fd086 FP |
250 | HMODULE hm_kernel32dll = LoadLibrary("kernel32.dll"); |
251 | HeapSetInformation_Proc s_pfn_Heap_Set_Information = (HeapSetInformation_Proc) GetProcAddress(hm_kernel32dll, "HeapSetInformation"); | |
252 | if (s_pfn_Heap_Set_Information != NULL) | |
253 | if (s_pfn_Heap_Set_Information ((PVOID) heap, | |
254 | HeapCompatibilityInformation, | |
255 | &enable_lfh, sizeof(enable_lfh)) == 0) | |
6c572f9a EZ |
256 | DebPrint (("Enabling Low Fragmentation Heap failed: error %ld\n", |
257 | GetLastError ())); | |
62aba0d4 | 258 | #endif |
fab624aa | 259 | |
587fd086 FP |
260 | the_malloc_fn = malloc_after_dump; |
261 | the_realloc_fn = realloc_after_dump; | |
262 | the_free_fn = free_after_dump; | |
263 | } | |
264 | else | |
fab624aa | 265 | { |
587fd086 FP |
266 | /* Find the RtlCreateHeap function. Headers for this function |
267 | are provided with the w32 ddk, but the function is available | |
268 | in ntdll.dll since XP. */ | |
269 | HMODULE hm_ntdll = LoadLibrary ("ntdll.dll"); | |
270 | RtlCreateHeap_Proc s_pfn_Rtl_Create_Heap | |
271 | = (RtlCreateHeap_Proc) GetProcAddress (hm_ntdll, "RtlCreateHeap"); | |
272 | /* Specific parameters for the private heap. */ | |
273 | RTL_HEAP_PARAMETERS params; | |
274 | ZeroMemory(¶ms, sizeof(params)); | |
275 | params.Length = sizeof(RTL_HEAP_PARAMETERS); | |
276 | ||
277 | data_region_base = (unsigned char *)ROUND_UP (dumped_data, 0x1000); | |
278 | data_region_end = bc_limit = dumped_data + DUMPED_HEAP_SIZE; | |
279 | ||
280 | params.InitialCommit = committed = 0x1000; | |
281 | params.InitialReserve = sizeof(dumped_data); | |
282 | /* Use our own routine to commit memory from the dumped_data | |
283 | array. */ | |
284 | params.CommitRoutine = &dumped_data_commit; | |
285 | ||
286 | /* Create the private heap. */ | |
287 | heap = s_pfn_Rtl_Create_Heap (0, data_region_base, 0, 0, NULL, ¶ms); | |
288 | the_malloc_fn = malloc_before_dump; | |
289 | the_realloc_fn = realloc_before_dump; | |
290 | the_free_fn = free_before_dump; | |
fab624aa EZ |
291 | } |
292 | ||
587fd086 FP |
293 | /* Update system version information to match current system. */ |
294 | cache_system_info (); | |
fab624aa | 295 | } |
011db670 | 296 | |
587fd086 FP |
297 | #undef malloc |
298 | #undef realloc | |
299 | #undef calloc | |
300 | #undef free | |
301 | ||
302 | /* FREEABLE_P checks if the block can be safely freed. */ | |
303 | #define FREEABLE_P(addr) \ | |
304 | ((unsigned char *)(addr) < dumped_data \ | |
305 | || (unsigned char *)(addr) >= dumped_data + DUMPED_HEAP_SIZE) | |
011db670 | 306 | |
95ed0025 | 307 | void * |
587fd086 | 308 | malloc_after_dump (size_t size) |
95ed0025 | 309 | { |
587fd086 FP |
310 | /* Use the new private heap. */ |
311 | void *p = HeapAlloc (heap, 0, size); | |
ce20e03e | 312 | |
0dd0ad37 | 313 | /* After dump, keep track of the "brk value" for sbrk(0). */ |
6c572f9a | 314 | if (p) |
0dd0ad37 EZ |
315 | { |
316 | unsigned char *new_brk = (unsigned char *)p + size; | |
317 | ||
318 | if (new_brk > data_region_end) | |
319 | data_region_end = new_brk; | |
320 | } | |
6c572f9a EZ |
321 | else |
322 | errno = ENOMEM; | |
587fd086 FP |
323 | return p; |
324 | } | |
ce20e03e | 325 | |
587fd086 FP |
326 | void * |
327 | malloc_before_dump (size_t size) | |
328 | { | |
329 | void *p; | |
330 | ||
331 | /* Before dumping. The private heap can handle only requests for | |
332 | less than MaxBlockSize. */ | |
333 | if (size < MaxBlockSize) | |
334 | { | |
335 | /* Use the private heap if possible. */ | |
336 | p = HeapAlloc (heap, 0, size); | |
6c572f9a EZ |
337 | if (!p) |
338 | errno = ENOMEM; | |
587fd086 FP |
339 | } |
340 | else | |
95ed0025 | 341 | { |
587fd086 FP |
342 | /* Find the first big chunk that can hold the requested size. */ |
343 | int i = 0; | |
344 | ||
345 | for (i = 0; i < blocks_number; i++) | |
346 | { | |
347 | if (blocks[i].occupied == 0 && blocks[i].size >= size) | |
348 | break; | |
349 | } | |
350 | if (i < blocks_number) | |
3bbabc43 | 351 | { |
587fd086 FP |
352 | /* If found, use it. */ |
353 | p = blocks[i].address; | |
354 | blocks[i].occupied = TRUE; | |
355 | } | |
356 | else | |
357 | { | |
358 | /* Allocate a new big chunk from the end of the dumped_data | |
359 | array. */ | |
360 | if (blocks_number >= MAX_BLOCKS) | |
361 | { | |
362 | fprintf(stderr, | |
363 | "malloc_before_dump: no more big chunks available.\nEnlarge MAX_BLOCKS!\n"); | |
364 | exit (-1); | |
365 | } | |
366 | bc_limit -= size; | |
367 | bc_limit = (unsigned char *)ROUND_DOWN (bc_limit, 0x10); | |
368 | p = bc_limit; | |
369 | blocks[blocks_number].address = p; | |
370 | blocks[blocks_number].size = size; | |
371 | blocks[blocks_number].occupied = TRUE; | |
372 | blocks_number++; | |
373 | if (bc_limit < dumped_data + committed) | |
374 | { | |
375 | /* Check that areas do not overlap. */ | |
376 | fprintf(stderr, | |
377 | "malloc_before_dump: memory exhausted.\nEnlarge dumped_data[]!\n"); | |
378 | exit (-1); | |
379 | } | |
380 | } | |
381 | } | |
382 | return p; | |
383 | } | |
384 | ||
385 | /* Re-allocate the previously allocated block in ptr, making the new | |
386 | block SIZE bytes long. */ | |
387 | void * | |
388 | realloc_after_dump (void *ptr, size_t size) | |
389 | { | |
390 | void *p; | |
95ed0025 | 391 | |
587fd086 FP |
392 | /* After dumping. */ |
393 | if (FREEABLE_P (ptr)) | |
394 | { | |
395 | /* Reallocate the block since it lies in the new heap. */ | |
396 | p = HeapReAlloc (heap, 0, ptr, size); | |
6c572f9a EZ |
397 | if (!p) |
398 | errno = ENOMEM; | |
ce20e03e | 399 | } |
587fd086 | 400 | else |
95ed0025 | 401 | { |
587fd086 FP |
402 | /* If the block lies in the dumped data, do not free it. Only |
403 | allocate a new one. */ | |
404 | p = HeapAlloc (heap, 0, size); | |
6c572f9a EZ |
405 | if (p) |
406 | CopyMemory (p, ptr, size); | |
407 | else | |
408 | errno = ENOMEM; | |
95ed0025 | 409 | } |
0dd0ad37 | 410 | /* After dump, keep track of the "brk value" for sbrk(0). */ |
6c572f9a | 411 | if (p) |
0dd0ad37 EZ |
412 | { |
413 | unsigned char *new_brk = (unsigned char *)p + size; | |
414 | ||
415 | if (new_brk > data_region_end) | |
416 | data_region_end = new_brk; | |
417 | } | |
587fd086 FP |
418 | return p; |
419 | } | |
ce20e03e | 420 | |
587fd086 FP |
421 | void * |
422 | realloc_before_dump (void *ptr, size_t size) | |
423 | { | |
424 | void *p; | |
425 | ||
426 | /* Before dumping. */ | |
427 | if (dumped_data < (unsigned char *)ptr | |
428 | && (unsigned char *)ptr < bc_limit && size <= MaxBlockSize) | |
6c572f9a EZ |
429 | { |
430 | p = HeapReAlloc (heap, 0, ptr, size); | |
431 | if (!p) | |
432 | errno = ENOMEM; | |
433 | } | |
587fd086 FP |
434 | else |
435 | { | |
436 | /* In this case, either the new block is too large for the heap, | |
437 | or the old block was already too large. In both cases, | |
438 | malloc_before_dump() and free_before_dump() will take care of | |
439 | reallocation. */ | |
440 | p = malloc_before_dump (size); | |
be04283a EZ |
441 | /* If SIZE is below MaxBlockSize, malloc_before_dump will try to |
442 | allocate it in the fixed heap. If that fails, we could have | |
443 | kept the block in its original place, above bc_limit, instead | |
444 | of failing the call as below. But this doesn't seem to be | |
445 | worth the added complexity, as loadup allocates only a very | |
446 | small number of large blocks, and never reallocates them. */ | |
6c572f9a EZ |
447 | if (p) |
448 | { | |
449 | CopyMemory (p, ptr, size); | |
450 | free_before_dump (ptr); | |
451 | } | |
587fd086 FP |
452 | } |
453 | return p; | |
95ed0025 RS |
454 | } |
455 | ||
587fd086 | 456 | /* Free a block allocated by `malloc', `realloc' or `calloc'. */ |
95ed0025 | 457 | void |
587fd086 | 458 | free_after_dump (void *ptr) |
95ed0025 | 459 | { |
587fd086 FP |
460 | /* After dumping. */ |
461 | if (FREEABLE_P (ptr)) | |
462 | { | |
463 | /* Free the block if it is in the new private heap. */ | |
464 | HeapFree (heap, 0, ptr); | |
465 | } | |
466 | } | |
30d2b1c2 | 467 | |
587fd086 FP |
468 | void |
469 | free_before_dump (void *ptr) | |
470 | { | |
471 | /* Before dumping. */ | |
472 | if (dumped_data < (unsigned char *)ptr | |
473 | && (unsigned char *)ptr < bc_limit) | |
30d2b1c2 | 474 | { |
587fd086 FP |
475 | /* Free the block if it is allocated in the private heap. */ |
476 | HeapFree (heap, 0, ptr); | |
477 | } | |
478 | else | |
479 | { | |
480 | /* Look for the big chunk. */ | |
481 | int i; | |
30d2b1c2 | 482 | |
587fd086 | 483 | for(i = 0; i < blocks_number; i++) |
30d2b1c2 | 484 | { |
587fd086 FP |
485 | if (blocks[i].address == ptr) |
486 | { | |
487 | /* Reset block occupation if found. */ | |
488 | blocks[i].occupied = 0; | |
489 | break; | |
490 | } | |
491 | /* What if the block is not found? We should trigger an | |
492 | error here. */ | |
493 | eassert (i < blocks_number); | |
30d2b1c2 | 494 | } |
30d2b1c2 | 495 | } |
587fd086 FP |
496 | } |
497 | ||
d2ff520a EZ |
498 | #ifdef ENABLE_CHECKING |
499 | void | |
500 | report_temacs_memory_usage (void) | |
501 | { | |
502 | /* Emulate 'message', which writes to stderr in non-interactive | |
503 | sessions. */ | |
504 | fprintf (stderr, | |
505 | "Dump memory usage: Heap: %" PRIu64 " Large blocks(%lu): %" PRIu64 "\n", | |
506 | (unsigned long long)committed, blocks_number, | |
507 | (unsigned long long)(dumped_data + DUMPED_HEAP_SIZE - bc_limit)); | |
508 | } | |
509 | #endif | |
510 | ||
587fd086 FP |
511 | /* Emulate getpagesize. */ |
512 | int | |
513 | getpagesize (void) | |
514 | { | |
515 | return sysinfo_cache.dwPageSize; | |
516 | } | |
517 | ||
518 | void * | |
519 | sbrk (ptrdiff_t increment) | |
520 | { | |
0dd0ad37 EZ |
521 | /* data_region_end is the address beyond the last allocated byte. |
522 | The sbrk() function is not emulated at all, except for a 0 value | |
523 | of its parameter. This is needed by the Emacs Lisp function | |
524 | `memory-limit'. */ | |
525 | eassert (increment == 0); | |
587fd086 FP |
526 | return data_region_end; |
527 | } | |
528 | ||
529 | #define MAX_BUFFER_SIZE (512 * 1024 * 1024) | |
530 | ||
531 | /* MMAP allocation for buffers. */ | |
532 | void * | |
533 | mmap_alloc (void **var, size_t nbytes) | |
534 | { | |
535 | void *p = NULL; | |
536 | ||
537 | /* We implement amortized allocation. We start by reserving twice | |
538 | the size requested and commit only the size requested. Then | |
539 | realloc could proceed and use the reserved pages, reallocating | |
540 | only if needed. Buffer shrink would happen only so that we stay | |
541 | in the 2x range. This is a big win when visiting compressed | |
542 | files, where the final size of the buffer is not known in | |
543 | advance, and the buffer is enlarged several times as the data is | |
544 | decompressed on the fly. */ | |
545 | if (nbytes < MAX_BUFFER_SIZE) | |
546 | p = VirtualAlloc (NULL, (nbytes * 2), MEM_RESERVE, PAGE_READWRITE); | |
547 | ||
548 | /* If it fails, or if the request is above 512MB, try with the | |
549 | requested size. */ | |
550 | if (p == NULL) | |
551 | p = VirtualAlloc (NULL, nbytes, MEM_RESERVE, PAGE_READWRITE); | |
552 | ||
553 | if (p != NULL) | |
30d2b1c2 | 554 | { |
587fd086 FP |
555 | /* Now, commit pages for NBYTES. */ |
556 | *var = VirtualAlloc (p, nbytes, MEM_COMMIT, PAGE_READWRITE); | |
30d2b1c2 | 557 | } |
801f68b9 | 558 | |
6c572f9a EZ |
559 | if (!p) |
560 | { | |
561 | if (GetLastError () == ERROR_NOT_ENOUGH_MEMORY) | |
562 | errno = ENOMEM; | |
563 | else | |
564 | { | |
565 | DebPrint (("mmap_alloc: error %ld\n", GetLastError ())); | |
566 | errno = EINVAL; | |
567 | } | |
568 | } | |
587fd086 FP |
569 | |
570 | return *var = p; | |
95ed0025 RS |
571 | } |
572 | ||
95ed0025 | 573 | void |
587fd086 | 574 | mmap_free (void **var) |
95ed0025 | 575 | { |
587fd086 FP |
576 | if (*var) |
577 | { | |
578 | if (VirtualFree (*var, 0, MEM_RELEASE) == 0) | |
6c572f9a | 579 | DebPrint (("mmap_free: error %ld\n", GetLastError ())); |
587fd086 FP |
580 | *var = NULL; |
581 | } | |
582 | } | |
ce20e03e | 583 | |
587fd086 FP |
584 | void * |
585 | mmap_realloc (void **var, size_t nbytes) | |
586 | { | |
587 | MEMORY_BASIC_INFORMATION memInfo, m2; | |
588 | ||
589 | if (*var == NULL) | |
590 | return mmap_alloc (var, nbytes); | |
591 | ||
592 | /* This case happens in init_buffer(). */ | |
593 | if (nbytes == 0) | |
594 | { | |
595 | mmap_free (var); | |
596 | return mmap_alloc (var, nbytes); | |
597 | } | |
598 | ||
599 | if (VirtualQuery (*var, &memInfo, sizeof (memInfo)) == 0) | |
6c572f9a | 600 | DebPrint (("mmap_realloc: VirtualQuery error = %ld\n", GetLastError ())); |
587fd086 FP |
601 | |
602 | /* We need to enlarge the block. */ | |
603 | if (memInfo.RegionSize < nbytes) | |
604 | { | |
605 | if (VirtualQuery (*var + memInfo.RegionSize, &m2, sizeof(m2)) == 0) | |
6c572f9a EZ |
606 | DebPrint (("mmap_realloc: VirtualQuery error = %ld\n", |
607 | GetLastError ())); | |
587fd086 FP |
608 | /* If there is enough room in the current reserved area, then |
609 | commit more pages as needed. */ | |
610 | if (m2.State == MEM_RESERVE | |
611 | && nbytes <= memInfo.RegionSize + m2.RegionSize) | |
612 | { | |
613 | void *p; | |
614 | ||
615 | p = VirtualAlloc (*var + memInfo.RegionSize, | |
616 | nbytes - memInfo.RegionSize, | |
617 | MEM_COMMIT, PAGE_READWRITE); | |
618 | if (!p /* && GetLastError() != ERROR_NOT_ENOUGH_MEMORY */) | |
6c572f9a EZ |
619 | { |
620 | DebPrint (("realloc enlarge: VirtualAlloc error %ld\n", | |
621 | GetLastError ())); | |
622 | errno = ENOMEM; | |
623 | } | |
587fd086 FP |
624 | return *var; |
625 | } | |
626 | else | |
627 | { | |
628 | /* Else we must actually enlarge the block by allocating a | |
629 | new one and copying previous contents from the old to the | |
630 | new one. */ | |
631 | void *old_ptr = *var; | |
632 | ||
633 | if (mmap_alloc (var, nbytes)) | |
634 | { | |
635 | CopyMemory (*var, old_ptr, memInfo.RegionSize); | |
636 | mmap_free (&old_ptr); | |
637 | return *var; | |
638 | } | |
639 | else | |
640 | { | |
641 | /* We failed to enlarge the buffer. */ | |
642 | *var = old_ptr; | |
643 | return NULL; | |
644 | } | |
645 | } | |
646 | } | |
647 | ||
648 | /* If we are shrinking by more than one page... */ | |
649 | if (memInfo.RegionSize > nbytes + getpagesize()) | |
650 | { | |
651 | /* If we are shrinking a lot... */ | |
652 | if ((memInfo.RegionSize / 2) > nbytes) | |
653 | { | |
654 | /* Let's give some memory back to the system and release | |
655 | some pages. */ | |
656 | void *old_ptr = *var; | |
657 | ||
658 | if (mmap_alloc (var, nbytes)) | |
659 | { | |
660 | CopyMemory (*var, old_ptr, nbytes); | |
661 | mmap_free (&old_ptr); | |
662 | return *var; | |
663 | } | |
664 | else | |
665 | { | |
666 | /* In case we fail to shrink, try to go on with the old block. | |
667 | But that means there is a lot of memory pressure. | |
668 | We could also decommit pages. */ | |
669 | *var = old_ptr; | |
670 | return *var; | |
671 | } | |
672 | } | |
673 | ||
674 | /* We still can decommit pages. */ | |
675 | if (VirtualFree (*var + nbytes + get_page_size(), | |
676 | memInfo.RegionSize - nbytes - get_page_size(), | |
677 | MEM_DECOMMIT) == 0) | |
6c572f9a | 678 | DebPrint (("mmap_realloc: VirtualFree error %ld\n", GetLastError ())); |
587fd086 FP |
679 | return *var; |
680 | } | |
ce20e03e | 681 | |
587fd086 FP |
682 | /* Not enlarging, not shrinking by more than one page. */ |
683 | return *var; | |
95ed0025 | 684 | } |