XEmacs 21.2.27 "Hera".
[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 /* The major and minor versions of NT.  */
42 int nt_major_version;
43 int nt_minor_version;
44
45 /* Distinguish between Windows NT and Windows 95.  */
46 int os_subtype;
47
48 /* Cache information describing the NT system for later use.  */
49 void
50 cache_system_info (void)
51 {
52   union 
53     {
54       struct info 
55         {
56           char  major;
57           char  minor;
58           short platform;
59         } info;
60       DWORD data;
61     } version;
62
63   /* Cache the version of the operating system.  */
64   version.data = GetVersion ();
65   nt_major_version = version.info.major;
66   nt_minor_version = version.info.minor;
67
68   if (version.info.platform & 0x8000)
69     os_subtype = OS_WIN95;
70   else
71     os_subtype = OS_NT;
72
73   /* Cache page size, allocation unit, processor type, etc.  */
74   GetSystemInfo (&sysinfo_cache);
75   syspage_mask = sysinfo_cache.dwPageSize - 1;
76 }
77
78 /* Round ADDRESS up to be aligned with ALIGN.  */
79 unsigned char *
80 round_to_next (unsigned char *address, unsigned long align)
81 {
82   unsigned long tmp;
83
84   tmp = (unsigned long) address;
85   tmp = (tmp + align - 1) / align;
86
87   return (unsigned char *) (tmp * align);
88 }
89
90 /* Info for keeping track of our heap.  */
91 unsigned char *data_region_base = UNINIT_PTR;
92 unsigned char *data_region_end = UNINIT_PTR;
93 unsigned char *real_data_region_end = UNINIT_PTR;
94 unsigned long  data_region_size = UNINIT_LONG;
95 unsigned long  reserved_heap_size = UNINIT_LONG;
96
97 /* The start of the data segment.  */
98 unsigned char *
99 get_data_start (void)
100 {
101   return data_region_base;
102 }
103
104 /* The end of the data segment.  */
105 unsigned char *
106 get_data_end (void)
107 {
108   return data_region_end;
109 }
110
111 static char *
112 allocate_heap (void)
113 {
114   /* The base address for our GNU malloc heap is chosen in conjunction
115      with the link settings for temacs.exe which control the stack size,
116      the initial default process heap size and the executable image base
117      address.  The link settings and the malloc heap base below must all
118      correspond; the relationship between these values depends on how NT
119      and Win95 arrange the virtual address space for a process (and on
120      the size of the code and data segments in temacs.exe).
121
122      The most important thing is to make base address for the executable
123      image high enough to leave enough room between it and the 4MB floor
124      of the process address space on Win95 for the primary thread stack,
125      the process default heap, and other assorted odds and ends
126      (eg. environment strings, private system dll memory etc) that are
127      allocated before temacs has a chance to grab its malloc arena.  The
128      malloc heap base can then be set several MB higher than the
129      executable image base, leaving enough room for the code and data
130      segments.
131
132      Because some parts of Emacs can use rather a lot of stack space
133      (for instance, the regular expression routines can potentially
134      allocate several MB of stack space) we allow 8MB for the stack.
135
136      Allowing 1MB for the default process heap, and 1MB for odds and
137      ends, we can base the executable at 16MB and still have a generous
138      safety margin.  At the moment, the executable has about 810KB of
139      code (for x86) and about 550KB of data - on RISC platforms the code
140      size could be roughly double, so if we allow 4MB for the executable
141      we will have plenty of room for expansion.
142
143      Thus we would like to set the malloc heap base to 20MB.  However,
144      Win95 refuses to allocate the heap starting at this address, so we
145      set the base to 27MB to make it happy.  Since Emacs now leaves
146      28 bits available for pointers, this lets us use the remainder of
147      the region below the 256MB line for our malloc arena - 229MB is
148      still a pretty decent arena to play in!  */
149
150   unsigned long base = 0x01B00000;   /*  27MB */
151   /* Temporary hack for the non-starting problem - use 28 (256Mb) rather than VALBITS (1Gb) */
152   unsigned long end  = 1 << 28;      /* 256MB */
153   void *ptr = NULL;
154
155 #define NTHEAP_PROBE_BASE 1
156 #if NTHEAP_PROBE_BASE /* This is never normally defined */
157   /* Try various addresses looking for one the kernel will let us have.  */
158   while (!ptr && (base < end))
159     {
160       reserved_heap_size = end - base;
161       ptr = VirtualAlloc ((void *) base,
162                           get_reserved_heap_size (),
163                           MEM_RESERVE,
164                           PAGE_NOACCESS);
165       base += 0x00100000;  /* 1MB increment */
166     }
167 #else
168   reserved_heap_size = end - base;
169   ptr = VirtualAlloc ((void *) base,
170                       get_reserved_heap_size (),
171                       MEM_RESERVE,
172                       PAGE_NOACCESS);
173 #endif
174
175   return ptr;
176 }
177
178
179 /* Emulate Unix sbrk.  */
180 void *
181 sbrk (unsigned long increment)
182 {
183   void *result;
184   long size = (long) increment;
185   
186   /* Allocate our heap if we haven't done so already.  */
187   if (data_region_base == UNINIT_PTR) 
188     {
189       data_region_base = allocate_heap ();
190       if (!data_region_base)
191         return NULL;
192
193       data_region_end = data_region_base;
194       real_data_region_end = data_region_end;
195       data_region_size = get_reserved_heap_size ();
196     }
197   
198   result = data_region_end;
199   
200   /* If size is negative, shrink the heap by decommitting pages.  */
201   if (size < 0) 
202     {
203       int new_size;
204       unsigned char *new_data_region_end;
205
206       size = -size;
207
208       /* Sanity checks.  */
209       if ((data_region_end - size) < data_region_base)
210         return NULL;
211
212       /* We can only decommit full pages, so allow for 
213          partial deallocation [cga].  */
214       new_data_region_end = (data_region_end - size);
215       new_data_region_end = (unsigned char *)
216         ((long) (new_data_region_end + syspage_mask) & ~syspage_mask);
217       new_size = real_data_region_end - new_data_region_end;
218       real_data_region_end = new_data_region_end;
219       if (new_size > 0) 
220         {
221           /* Decommit size bytes from the end of the heap.  */
222           if (!VirtualFree (real_data_region_end, new_size, MEM_DECOMMIT))
223             return NULL;
224         }
225
226       data_region_end -= size;
227     } 
228   /* If size is positive, grow the heap by committing reserved pages.  */
229   else if (size > 0) 
230     {
231       /* Sanity checks.  */
232       if ((data_region_end + size) >
233           (data_region_base + get_reserved_heap_size ()))
234         return NULL;
235
236       /* Commit more of our heap. */
237       if (VirtualAlloc (data_region_end, size, MEM_COMMIT,
238                         PAGE_READWRITE) == NULL)
239         return NULL;
240       data_region_end += size;
241
242       /* We really only commit full pages, so record where
243          the real end of committed memory is [cga].  */
244       real_data_region_end = (unsigned char *)
245           ((long) (data_region_end + syspage_mask) & ~syspage_mask);
246     }
247   
248   return result;
249 }
250
251 #if !defined (CANNOT_DUMP) && !defined(HEAP_IN_DATA)
252
253 /* Recreate the heap from the data that was dumped to the executable.
254    EXECUTABLE_PATH tells us where to find the executable.  */
255 void
256 recreate_heap (char *executable_path)
257 {
258   unsigned char *tmp;
259
260   /* First reserve the upper part of our heap.  (We reserve first
261      because there have been problems in the past where doing the
262      mapping first has loaded DLLs into the VA space of our heap.)  */
263   tmp = VirtualAlloc ((void *) get_heap_end (),
264                       get_reserved_heap_size () - get_committed_heap_size (),
265                       MEM_RESERVE,
266                       PAGE_NOACCESS);
267   if (!tmp)
268     exit (1);
269
270   /* We read in the data for the .bss section from the executable
271      first and map in the heap from the executable second to prevent
272      any funny interactions between file I/O and file mapping.  */
273   read_in_bss (executable_path);
274   map_in_heap (executable_path);
275
276   /* Update system version information to match current system.  */
277   cache_system_info ();
278 }
279 #endif /* CANNOT_DUMP */
280
281 /* Round the heap up to the given alignment.  */
282 void
283 round_heap (unsigned long align)
284 {
285   unsigned long needs_to_be;
286   unsigned long need_to_alloc;
287   
288   needs_to_be = (unsigned long) round_to_next (get_heap_end (), align);
289   need_to_alloc = needs_to_be - (unsigned long) get_heap_end ();
290   
291   if (need_to_alloc) 
292     sbrk (need_to_alloc);
293 }
294
295 #if (_MSC_VER >= 1000)
296
297 /* MSVC 4.2 invokes these functions from mainCRTStartup to initialize
298    a heap via HeapCreate.  They are normally defined by the runtime,
299    but we override them here so that the unnecessary HeapCreate call
300    is not performed.  */
301
302 int __cdecl
303 _heap_init (void)
304 {
305   /* Stepping through the assembly indicates that mainCRTStartup is
306      expecting a nonzero success return value.  */
307   return 1;
308 }
309
310 void __cdecl
311 _heap_term (void)
312 {
313   return;
314 }
315
316 #endif