XEmacs 21.4.6 "Common Lisp".
[chise/xemacs-chise.git.1] / src / ntheap.c
1 /* Heap management routines for XEmacs on Windows NT.
2    Copyright (C) 1994 Free Software Foundation, Inc.
3
4 This file is part of XEmacs.
5
6 XEmacs is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
9 later version.
10
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with XEmacs; see the file COPYING.  If not, write to the Free
18 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20
21    Geoff Voelker (voelker@cs.washington.edu) 7-29-94 */
22
23 /* Adapted for XEmacs by David Hobley <david@spook-le0.cia.com.au> */
24 /* Synced with FSF Emacs 19.34.6 by Marc Paquette <marcpa@cam.org> */
25
26 #include <config.h>
27 #include "lisp.h"  /* for VALMASK */
28
29 #include <stdlib.h>
30
31 #include "ntheap.h"
32
33 /* This gives us the page size and the size of the allocation unit on NT.  */
34 SYSTEM_INFO sysinfo_cache;
35 unsigned long syspage_mask = 0;
36
37 /* These are defined to get Emacs to compile, but are not used.  */
38 int edata;
39 int etext;
40
41 /* Cache information describing the NT system for later use.  */
42 void
43 cache_system_info (void)
44 {
45   /* Cache page size, allocation unit, processor type, etc.  */
46   GetSystemInfo (&sysinfo_cache);
47   syspage_mask = sysinfo_cache.dwPageSize - 1;
48 }
49
50 /* Round ADDRESS up to be aligned with ALIGN.  */
51 unsigned char *
52 round_to_next (unsigned char *address, unsigned long align)
53 {
54   unsigned long tmp;
55
56   tmp = (unsigned long) address;
57   tmp = (tmp + align - 1) / align;
58
59   return (unsigned char *) (tmp * align);
60 }
61
62 /* Info for keeping track of our heap.  */
63 unsigned char *data_region_base = UNINIT_PTR;
64 unsigned char *data_region_end = UNINIT_PTR;
65 unsigned char *real_data_region_end = UNINIT_PTR;
66 unsigned long  data_region_size = UNINIT_LONG;
67 unsigned long  reserved_heap_size = UNINIT_LONG;
68
69 /* The start of the data segment.  */
70 unsigned char *
71 get_data_start (void)
72 {
73   return data_region_base;
74 }
75
76 /* The end of the data segment.  */
77 unsigned char *
78 get_data_end (void)
79 {
80   return data_region_end;
81 }
82
83 static unsigned char *
84 allocate_heap (void)
85 {
86   /* The base address for our GNU malloc heap is chosen in conjunction
87      with the link settings for temacs.exe which control the stack size,
88      the initial default process heap size and the executable image base
89      address.  The link settings and the malloc heap base below must all
90      correspond; the relationship between these values depends on how NT
91      and Win95 arrange the virtual address space for a process (and on
92      the size of the code and data segments in temacs.exe).
93
94      The most important thing is to make base address for the executable
95      image high enough to leave enough room between it and the 4MB floor
96      of the process address space on Win95 for the primary thread stack,
97      the process default heap, and other assorted odds and ends
98      (eg. environment strings, private system dll memory etc) that are
99      allocated before temacs has a chance to grab its malloc arena.  The
100      malloc heap base can then be set several MB higher than the
101      executable image base, leaving enough room for the code and data
102      segments.
103
104      Because some parts of Emacs can use rather a lot of stack space
105      (for instance, the regular expression routines can potentially
106      allocate several MB of stack space) we allow 8MB for the stack.
107
108      Allowing 1MB for the default process heap, and 1MB for odds and
109      ends, we can base the executable at 16MB and still have a generous
110      safety margin.  At the moment, the executable has about 810KB of
111      code (for x86) and about 550KB of data - on RISC platforms the code
112      size could be roughly double, so if we allow 4MB for the executable
113      we will have plenty of room for expansion.
114
115      Thus we would like to set the malloc heap base to 20MB.  However,
116      Win95 refuses to allocate the heap starting at this address, so we
117      set the base to 27MB to make it happy.  Since Emacs now leaves
118      28 bits available for pointers, this lets us use the remainder of
119      the region below the 256MB line for our malloc arena - 229MB is
120      still a pretty decent arena to play in!  */
121
122   unsigned long base = 0x01B00000;   /*  27MB */
123   /* Temporary hack for the non-starting problem - use 28 (256Mb) rather than VALBITS (1Gb) */
124   unsigned long end  = 1 << 28;      /* 256MB */
125   void *ptr = NULL;
126
127 #define NTHEAP_PROBE_BASE 1
128 #if NTHEAP_PROBE_BASE /* This is never normally defined */
129   /* Try various addresses looking for one the kernel will let us have.  */
130   while (!ptr && (base < end))
131     {
132       reserved_heap_size = end - base;
133       ptr = VirtualAlloc ((void *) base,
134                           get_reserved_heap_size (),
135                           MEM_RESERVE,
136                           PAGE_NOACCESS);
137       base += 0x00100000;  /* 1MB increment */
138     }
139 #else
140   reserved_heap_size = end - base;
141   ptr = VirtualAlloc ((void *) base,
142                       get_reserved_heap_size (),
143                       MEM_RESERVE,
144                       PAGE_NOACCESS);
145 #endif
146
147   return (unsigned char*) ptr;
148 }
149
150
151 /* Emulate Unix sbrk.  */
152 void *
153 sbrk (unsigned long increment)
154 {
155   void *result;
156   long size = (long) increment;
157   
158   /* Allocate our heap if we haven't done so already.  */
159   if (data_region_base == UNINIT_PTR) 
160     {
161       data_region_base = allocate_heap ();
162       if (!data_region_base)
163         return NULL;
164
165       data_region_end = data_region_base;
166       real_data_region_end = data_region_end;
167       data_region_size = get_reserved_heap_size ();
168     }
169   
170   result = data_region_end;
171   
172   /* If size is negative, shrink the heap by decommitting pages.  */
173   if (size < 0) 
174     {
175       int new_size;
176       unsigned char *new_data_region_end;
177
178       size = -size;
179
180       /* Sanity checks.  */
181       if ((data_region_end - size) < data_region_base)
182         return NULL;
183
184       /* We can only decommit full pages, so allow for 
185          partial deallocation [cga].  */
186       new_data_region_end = (data_region_end - size);
187       new_data_region_end = (unsigned char *)
188         ((long) (new_data_region_end + syspage_mask) & ~syspage_mask);
189       new_size = real_data_region_end - new_data_region_end;
190       real_data_region_end = new_data_region_end;
191       if (new_size > 0) 
192         {
193           /* Decommit size bytes from the end of the heap.  */
194           if (!VirtualFree (real_data_region_end, new_size, MEM_DECOMMIT))
195             return NULL;
196         }
197
198       data_region_end -= size;
199     } 
200   /* If size is positive, grow the heap by committing reserved pages.  */
201   else if (size > 0) 
202     {
203       /* Sanity checks.  */
204       if ((data_region_end + size) >
205           (data_region_base + get_reserved_heap_size ()))
206         return NULL;
207
208       /* Commit more of our heap. */
209       if (VirtualAlloc (data_region_end, size, MEM_COMMIT,
210                         PAGE_READWRITE) == NULL)
211         return NULL;
212       data_region_end += size;
213
214       /* We really only commit full pages, so record where
215          the real end of committed memory is [cga].  */
216       real_data_region_end = (unsigned char *)
217           ((long) (data_region_end + syspage_mask) & ~syspage_mask);
218     }
219   
220   return result;
221 }
222
223 #if !defined (CANNOT_DUMP) && !defined(HEAP_IN_DATA) && !defined(PDUMP)
224
225 /* Recreate the heap from the data that was dumped to the executable.
226    EXECUTABLE_PATH tells us where to find the executable.  */
227 void
228 recreate_heap (char *executable_path)
229 {
230   /* First reserve the upper part of our heap.  (We reserve first
231          because there have been problems in the past where doing the
232          mapping first has loaded DLLs into the VA space of our heap.)  */
233
234   /* Query the region at the end of the committed heap */
235   void *tmp;
236   MEMORY_BASIC_INFORMATION info;
237   DWORD size;
238   unsigned char* base = get_heap_end ();
239   unsigned char* end  = base + get_reserved_heap_size () - get_committed_heap_size ();
240   VirtualQuery (base, &info, sizeof info);
241   if (info.State != MEM_FREE)
242         {
243           /* Oops, something has already reserved or commited it, nothing we can do but exit */
244           char buf[256];
245           wsprintf(buf,
246                            "XEmacs cannot start because the memory region required by the heap is not available.\n"
247                            "(BaseAddress = 0x%lx, AllocationBase = 0x%lx, Size = 0x%lx, State = %s, Type = %s)",
248                            info.BaseAddress, info.AllocationBase, info.RegionSize,
249                            info.State == MEM_COMMIT ? "COMMITED" : "RESERVED",
250                            info.Type == MEM_IMAGE ? "IMAGE" : info.Type == MEM_MAPPED ? "MAPPED" : "PRIVATE");
251           MessageBox(NULL, buf, "XEmacs", MB_OK | MB_ICONSTOP);
252           exit(1);
253         }
254
255   /* Now try and reserve as much as possible */
256   size = min (info.RegionSize, end - base);
257   tmp = VirtualAlloc (base, size, MEM_RESERVE, PAGE_NOACCESS);
258   if (!tmp)
259         {
260           /* Can't reserve it, nothing we can do but exit */
261           char buf[256];
262           wsprintf(buf,
263                            "XEmacs cannot start because it couldn't reserve space required for the heap.\n"
264                            "(VirtualAlloc at 0x%lx of 0x%lx failed (%d))", base, size, GetLastError());
265           MessageBox(NULL, buf, "XEmacs", MB_OK | MB_ICONSTOP);
266           exit (1);
267         }
268
269   /* We read in the data for the .bss section from the executable
270      first and map in the heap from the executable second to prevent
271      any funny interactions between file I/O and file mapping.  */
272   read_in_bss (executable_path);
273   map_in_heap (executable_path);
274
275   /* Update system version information to match current system.  */
276   cache_system_info ();
277 }
278 #endif /* CANNOT_DUMP */
279
280 /* Round the heap up to the given alignment.  */
281 void
282 round_heap (unsigned long align)
283 {
284   unsigned long needs_to_be;
285   unsigned long need_to_alloc;
286   
287   needs_to_be = (unsigned long) round_to_next (get_heap_end (), align);
288   need_to_alloc = needs_to_be - (unsigned long) get_heap_end ();
289   
290   if (need_to_alloc) 
291     sbrk (need_to_alloc);
292 }
293
294 #if ((_MSC_VER >= 1000) && (_MSC_VER < 1300))
295
296 /* MSVC 4.2 invokes these functions from mainCRTStartup to initialize
297    a heap via HeapCreate.  They are normally defined by the runtime,
298    but we override them here so that the unnecessary HeapCreate call
299    is not performed.  */
300
301 /* MSVC 7.0 does not allow you to redefine _heap_init or _heap_term. */
302
303 int __cdecl
304 _heap_init (void)
305 {
306   /* Stepping through the assembly indicates that mainCRTStartup is
307      expecting a nonzero success return value.  */
308   return 1;
309 }
310
311 void __cdecl
312 _heap_term (void)
313 {
314   return;
315 }
316
317 #endif