Mercurial > emacs
comparison src/ralloc.c @ 118:49342840ba00
Initial revision
author | Jim Blandy <jimb@redhat.com> |
---|---|
date | Mon, 12 Nov 1990 20:20:45 +0000 |
parents | |
children | 8c615e453683 |
comparison
equal
deleted
inserted
replaced
117:08356dc1077c | 118:49342840ba00 |
---|---|
1 /* Block-relocating memory allocator. | |
2 Copyright (C) 1990 Free Software Foundation, Inc. | |
3 | |
4 This file is part of GNU Emacs. | |
5 | |
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 1, or (at your option) | |
9 any later version. | |
10 | |
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. | |
15 | |
16 You should have received a copy of the GNU General Public License | |
17 along with GNU Emacs; see the file COPYING. If not, write to | |
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
19 | |
20 /* NOTES: | |
21 | |
22 Only relocate the blocs neccessary for SIZE in r_alloc_sbrk, | |
23 rather than all of them. This means allowing for a possible | |
24 hole between the first bloc and the end of malloc storage. */ | |
25 | |
26 #include "config.h" | |
27 #include "lisp.h" /* Needed for xterm.h */ | |
28 #undef NULL | |
29 #include "mem_limits.h" | |
30 #include "xterm.h" /* Needed for BLOCK_INPUT */ | |
31 | |
32 #define NIL ((POINTER) 0) | |
33 | |
34 | |
35 /* System call to set the break value. */ | |
36 extern POINTER sbrk (); | |
37 | |
38 /* The break value, as seen by malloc (). */ | |
39 static POINTER virtual_break_value; | |
40 | |
41 /* The break value, viewed by the relocatable blocs. */ | |
42 static POINTER break_value; | |
43 | |
44 /* The REAL (i.e., page aligned) break value of the process. */ | |
45 static POINTER page_break_value; | |
46 | |
47 /* Macros for rounding. Note that rounding to any value is possible | |
48 by changing the definition of PAGE. */ | |
49 #define PAGE (getpagesize ()) | |
50 #define ALIGNED(addr) (((unsigned int) (addr) & (PAGE - 1)) == 0) | |
51 #define ROUNDUP(size) (((unsigned int) (size) + PAGE) & ~(PAGE - 1)) | |
52 #define ROUND_TO_PAGE(addr) (addr & (~(PAGE - 1))) | |
53 #define EXCEEDS_ELISP_PTR(ptr) ((unsigned int) (ptr) >> VALBITS) | |
54 | |
55 /* Level of warnings issued. */ | |
56 static int warnlevel; | |
57 | |
58 /* Function to call to issue a warning; | |
59 0 means don't issue them. */ | |
60 static void (*warnfunction) (); | |
61 | |
62 static void | |
63 check_memory_limits (address) | |
64 POINTER address; | |
65 { | |
66 SIZE data_size = address - data_space_start; | |
67 | |
68 switch (warnlevel) | |
69 { | |
70 case 0: | |
71 if (data_size > (lim_data / 4) * 3) | |
72 { | |
73 warnlevel++; | |
74 (*warnfunction) ("Warning: past 75% of memory limit"); | |
75 } | |
76 break; | |
77 | |
78 case 1: | |
79 if (data_size > (lim_data / 20) * 17) | |
80 { | |
81 warnlevel++; | |
82 (*warnfunction) ("Warning: past 85% of memory limit"); | |
83 } | |
84 break; | |
85 | |
86 case 2: | |
87 if (data_size > (lim_data / 20) * 19) | |
88 { | |
89 warnlevel++; | |
90 (*warnfunction) ("Warning: past 95% of memory limit"); | |
91 } | |
92 break; | |
93 | |
94 default: | |
95 (*warnfunction) ("Warning: past acceptable memory limits"); | |
96 break; | |
97 } | |
98 | |
99 if (EXCEEDS_ELISP_PTR (address)) | |
100 (*warnfunction) ("Warning: memory in use exceeds lisp pointer size"); | |
101 } | |
102 | |
103 /* Obtain SIZE bytes of space. If enough space is not presently available | |
104 in our process reserve, (i.e., (page_break_value - break_value)), | |
105 this means getting more page-aligned space from the system. */ | |
106 | |
107 static void | |
108 obtain (size) | |
109 SIZE size; | |
110 { | |
111 SIZE already_available = page_break_value - break_value; | |
112 | |
113 if (already_available < size) | |
114 { | |
115 SIZE get = ROUNDUP (size); | |
116 | |
117 if (warnfunction) | |
118 check_memory_limits (page_break_value); | |
119 | |
120 if (((int) sbrk (get)) < 0) | |
121 abort (); | |
122 | |
123 page_break_value += get; | |
124 } | |
125 | |
126 break_value += size; | |
127 } | |
128 | |
129 /* Obtain SIZE bytes of space and return a pointer to the new area. */ | |
130 | |
131 static POINTER | |
132 get_more_space (size) | |
133 SIZE size; | |
134 { | |
135 POINTER ptr = break_value; | |
136 obtain (size); | |
137 return ptr; | |
138 } | |
139 | |
140 /* Note that SIZE bytes of space have been relinquished by the process. | |
141 If SIZE is more than a page, return the space the system. */ | |
142 | |
143 static void | |
144 relinquish (size) | |
145 SIZE size; | |
146 { | |
147 SIZE page_part = ROUND_TO_PAGE (size); | |
148 | |
149 if (page_part) | |
150 { | |
151 if (((int) (sbrk (- page_part))) < 0) | |
152 abort (); | |
153 | |
154 page_break_value -= page_part; | |
155 } | |
156 | |
157 break_value -= size; | |
158 bzero (break_value, (size - page_part)); | |
159 } | |
160 | |
161 typedef struct bp | |
162 { | |
163 struct bp *next; | |
164 struct bp *prev; | |
165 POINTER *variable; | |
166 POINTER data; | |
167 SIZE size; | |
168 } *bloc_ptr; | |
169 | |
170 #define NIL_BLOC ((bloc_ptr) 0) | |
171 #define BLOC_PTR_SIZE (sizeof (struct bp)) | |
172 | |
173 /* Head and tail of the list of relocatable blocs. */ | |
174 static bloc_ptr first_bloc, last_bloc; | |
175 | |
176 /* Declared in dispnew.c, this version dosen't fuck up if regions overlap. */ | |
177 extern void safe_bcopy (); | |
178 | |
179 /* Find the bloc reference by the address in PTR. Returns a pointer | |
180 to that block. */ | |
181 | |
182 static bloc_ptr | |
183 find_bloc (ptr) | |
184 POINTER *ptr; | |
185 { | |
186 register bloc_ptr p = first_bloc; | |
187 | |
188 while (p != NIL_BLOC) | |
189 { | |
190 if (p->variable == ptr && p->data == *ptr) | |
191 return p; | |
192 | |
193 p = p->next; | |
194 } | |
195 | |
196 return p; | |
197 } | |
198 | |
199 /* Allocate a bloc of SIZE bytes and append it to the chain of blocs. | |
200 Returns a pointer to the new bloc. */ | |
201 | |
202 static bloc_ptr | |
203 get_bloc (size) | |
204 SIZE size; | |
205 { | |
206 register bloc_ptr new_bloc = (bloc_ptr) malloc (BLOC_PTR_SIZE); | |
207 | |
208 new_bloc->data = get_more_space (size); | |
209 new_bloc->size = size; | |
210 new_bloc->next = NIL_BLOC; | |
211 new_bloc->variable = NIL; | |
212 | |
213 if (first_bloc) | |
214 { | |
215 new_bloc->prev = last_bloc; | |
216 last_bloc->next = new_bloc; | |
217 last_bloc = new_bloc; | |
218 } | |
219 else | |
220 { | |
221 first_bloc = last_bloc = new_bloc; | |
222 new_bloc->prev = NIL_BLOC; | |
223 } | |
224 | |
225 return new_bloc; | |
226 } | |
227 | |
228 /* Relocate all blocs from BLOC on upward in the list to the zone | |
229 indicated by ADDRESS. Direction of relocation is determined by | |
230 the position of ADDRESS relative to BLOC->data. | |
231 | |
232 Note that ordering of blocs is not affected by this function. */ | |
233 | |
234 static void | |
235 relocate_some_blocs (bloc, address) | |
236 bloc_ptr bloc; | |
237 POINTER address; | |
238 { | |
239 register bloc_ptr b; | |
240 POINTER data_zone = bloc->data; | |
241 register SIZE data_zone_size = 0; | |
242 register SIZE offset = bloc->data - address; | |
243 POINTER new_data_zone = data_zone - offset; | |
244 | |
245 for (b = bloc; b != NIL_BLOC; b = b->next) | |
246 { | |
247 data_zone_size += b->size; | |
248 b->data -= offset; | |
249 *b->variable = b->data; | |
250 } | |
251 | |
252 safe_bcopy (data_zone, new_data_zone, data_zone_size); | |
253 } | |
254 | |
255 /* Free BLOC from the chain of blocs, relocating any blocs above it | |
256 and returning BLOC->size bytes to the free area. */ | |
257 | |
258 static void | |
259 free_bloc (bloc) | |
260 bloc_ptr bloc; | |
261 { | |
262 if (bloc == first_bloc && bloc == last_bloc) | |
263 { | |
264 first_bloc = last_bloc = NIL_BLOC; | |
265 } | |
266 else if (bloc == last_bloc) | |
267 { | |
268 last_bloc = bloc->prev; | |
269 last_bloc->next = NIL_BLOC; | |
270 } | |
271 else if (bloc == first_bloc) | |
272 { | |
273 first_bloc = bloc->next; | |
274 first_bloc->prev = NIL_BLOC; | |
275 relocate_some_blocs (bloc->next, bloc->data); | |
276 } | |
277 else | |
278 { | |
279 bloc->next->prev = bloc->prev; | |
280 bloc->prev->next = bloc->next; | |
281 relocate_some_blocs (bloc->next, bloc->data); | |
282 } | |
283 | |
284 relinquish (bloc->size); | |
285 free (bloc); | |
286 } | |
287 | |
288 static int use_relocatable_buffers; | |
289 | |
290 /* Obtain SIZE bytes of storage from the free pool, or the system, | |
291 as neccessary. If relocatable blocs are in use, this means | |
292 relocating them. */ | |
293 | |
294 POINTER | |
295 r_alloc_sbrk (size) | |
296 long size; | |
297 { | |
298 POINTER ptr; | |
299 | |
300 if (! use_relocatable_buffers) | |
301 return sbrk (size); | |
302 | |
303 if (size > 0) | |
304 { | |
305 obtain (size); | |
306 if (first_bloc) | |
307 { | |
308 relocate_some_blocs (first_bloc, first_bloc->data + size); | |
309 bzero (virtual_break_value, size); | |
310 } | |
311 } | |
312 else if (size < 0) | |
313 { | |
314 if (first_bloc) | |
315 relocate_some_blocs (first_bloc, first_bloc->data + size); | |
316 relinquish (- size); | |
317 } | |
318 | |
319 ptr = virtual_break_value; | |
320 virtual_break_value += size; | |
321 return ptr; | |
322 } | |
323 | |
324 /* Allocate a relocatable bloc of storage of size SIZE. A pointer to | |
325 the data is returned in *PTR. PTR is thus the address of some variable | |
326 which will use the data area. */ | |
327 | |
328 POINTER | |
329 r_alloc (ptr, size) | |
330 POINTER *ptr; | |
331 SIZE size; | |
332 { | |
333 register bloc_ptr new_bloc; | |
334 | |
335 BLOCK_INPUT; | |
336 new_bloc = get_bloc (size); | |
337 new_bloc->variable = ptr; | |
338 *ptr = new_bloc->data; | |
339 UNBLOCK_INPUT; | |
340 | |
341 return *ptr; | |
342 } | |
343 | |
344 /* Free a bloc of relocatable storage whose data is pointed to by PTR. */ | |
345 | |
346 void | |
347 r_alloc_free (ptr) | |
348 register POINTER *ptr; | |
349 { | |
350 register bloc_ptr dead_bloc; | |
351 | |
352 BLOCK_INPUT; | |
353 dead_bloc = find_bloc (ptr); | |
354 if (dead_bloc == NIL_BLOC) | |
355 abort (); | |
356 | |
357 free_bloc (dead_bloc); | |
358 UNBLOCK_INPUT; | |
359 } | |
360 | |
361 /* Given a pointer at address PTR to relocatable data, resize it | |
362 to SIZE. This is done by obtaining a new block and freeing the | |
363 old, unless SIZE is less than or equal to the current bloc size, | |
364 in which case nothing happens and the current value is returned. | |
365 | |
366 The contents of PTR is changed to reflect the new bloc, and this | |
367 value is returned. */ | |
368 | |
369 POINTER | |
370 r_re_alloc (ptr, size) | |
371 POINTER *ptr; | |
372 SIZE size; | |
373 { | |
374 register bloc_ptr old_bloc, new_bloc; | |
375 | |
376 BLOCK_INPUT; | |
377 old_bloc = find_bloc (ptr); | |
378 if (old_bloc == NIL_BLOC) | |
379 abort (); | |
380 | |
381 if (size <= old_bloc->size) | |
382 return *ptr; | |
383 | |
384 new_bloc = get_bloc (size); | |
385 new_bloc->variable = ptr; | |
386 safe_bcopy (old_bloc->data, new_bloc->data, old_bloc->size); | |
387 *ptr = new_bloc->data; | |
388 | |
389 free_bloc (old_bloc); | |
390 UNBLOCK_INPUT; | |
391 | |
392 return *ptr; | |
393 } | |
394 | |
395 /* The hook `malloc' uses for the function which gets more space | |
396 from the system. */ | |
397 extern POINTER (*__morecore) (); | |
398 | |
399 /* Intialize various things for memory allocation. */ | |
400 | |
401 void | |
402 malloc_init (start, warn_func) | |
403 POINTER start; | |
404 void (*warn_func) (); | |
405 { | |
406 static int malloc_initialized = 0; | |
407 | |
408 if (start) | |
409 data_space_start = start; | |
410 | |
411 if (malloc_initialized) | |
412 return; | |
413 | |
414 malloc_initialized = 1; | |
415 __morecore = r_alloc_sbrk; | |
416 virtual_break_value = break_value = sbrk (0); | |
417 page_break_value = (POINTER) ROUNDUP (break_value); | |
418 bzero (break_value, (page_break_value - break_value)); | |
419 use_relocatable_buffers = 1; | |
420 | |
421 lim_data = 0; | |
422 warnlevel = 0; | |
423 warnfunction = warn_func; | |
424 | |
425 get_lim_data (); | |
426 } |