* configure: Start with a blank line; this keeps some old CSH's
[bpt/emacs.git] / src / ralloc.c
CommitLineData
dcfdbac7 1/* Block-relocating memory allocator.
956ace37 2 Copyright (C) 1992 Free Software Foundation, Inc.
dcfdbac7
JB
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 1, or (at your option)
9any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
18the 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
2c46d29f 26#ifdef emacs
aef4d570 27
dcfdbac7 28#include "config.h"
956ace37 29#include "lisp.h" /* Needed for VALBITS. */
2c46d29f 30
aef4d570
RM
31#undef NULL
32
f275fd9a
RS
33/* The important properties of this type are that 1) it's a pointer, and
34 2) arithmetic on it should work as if the size of the object pointed
35 to has a size of 1. */
36#ifdef __STDC__
37typedef void *POINTER;
38#else
39typedef char *POINTER;
40#endif
41
42typedef unsigned long SIZE;
43
2c46d29f
RS
44/* Declared in dispnew.c, this version doesn't screw up if regions
45 overlap. */
46extern void safe_bcopy ();
2c46d29f 47
aef4d570
RM
48#include "getpagesize.h"
49
50#else /* Not emacs. */
51
2c46d29f 52#include <stddef.h>
aef4d570 53
2c46d29f
RS
54typedef size_t SIZE;
55typedef void *POINTER;
aef4d570 56
aef4d570
RM
57#include <unistd.h>
58#include <malloc.h>
59#include <string.h>
60
2c46d29f 61#define safe_bcopy(x, y, z) memmove (y, x, z)
2c46d29f 62
aef4d570 63#endif /* emacs. */
dcfdbac7
JB
64
65#define NIL ((POINTER) 0)
66
2c46d29f
RS
67/* A flag to indicate whether we have initialized ralloc yet. For
68 Emacs's sake, please do not make this local to malloc_init; on some
69 machines, the dumping procedure makes all static variables
70 read-only. On these machines, the word static is #defined to be
71 the empty string, meaning that r_alloc_initialized becomes an
72 automatic variable, and loses its value each time Emacs is started up. */
73static int r_alloc_initialized = 0;
74
75static void r_alloc_init ();
dcfdbac7 76\f
956ace37
JB
77/* Declarations for working with the malloc, ralloc, and system breaks. */
78
bbc60227
RM
79/* Function to set the real break value. */
80static POINTER (*real_morecore) ();
dcfdbac7
JB
81
82/* The break value, as seen by malloc (). */
83static POINTER virtual_break_value;
84
85/* The break value, viewed by the relocatable blocs. */
86static POINTER break_value;
87
88/* The REAL (i.e., page aligned) break value of the process. */
89static POINTER page_break_value;
90
7516b7d5
RS
91/* This is the size of a page. We round memory requests to this boundary. */
92static int page_size;
93
ad3bb3d2
JB
94/* Whenever we get memory from the system, get this many extra bytes. This
95 must be a multiple of page_size. */
7516b7d5
RS
96static int extra_bytes;
97
dcfdbac7
JB
98/* Macros for rounding. Note that rounding to any value is possible
99 by changing the definition of PAGE. */
100#define PAGE (getpagesize ())
7516b7d5
RS
101#define ALIGNED(addr) (((unsigned int) (addr) & (page_size - 1)) == 0)
102#define ROUNDUP(size) (((unsigned int) (size) + page_size - 1) & ~(page_size - 1))
103#define ROUND_TO_PAGE(addr) (addr & (~(page_size - 1)))
dcfdbac7 104\f
956ace37
JB
105/* Functions to get and return memory from the system. */
106
dcfdbac7
JB
107/* Obtain SIZE bytes of space. If enough space is not presently available
108 in our process reserve, (i.e., (page_break_value - break_value)),
98b7fe02 109 this means getting more page-aligned space from the system.
dcfdbac7 110
98b7fe02
JB
111 Return non-zero if all went well, or zero if we couldn't allocate
112 the memory. */
113static int
dcfdbac7
JB
114obtain (size)
115 SIZE size;
116{
117 SIZE already_available = page_break_value - break_value;
118
119 if (already_available < size)
120 {
956ace37 121 SIZE get = ROUNDUP (size - already_available);
7516b7d5
RS
122 /* Get some extra, so we can come here less often. */
123 get += extra_bytes;
dcfdbac7 124
bbc60227 125 if ((*real_morecore) (get) == 0)
98b7fe02 126 return 0;
dcfdbac7
JB
127
128 page_break_value += get;
129 }
130
131 break_value += size;
98b7fe02
JB
132
133 return 1;
dcfdbac7
JB
134}
135
98b7fe02
JB
136/* Obtain SIZE bytes of space and return a pointer to the new area.
137 If we could not allocate the space, return zero. */
dcfdbac7
JB
138
139static POINTER
140get_more_space (size)
141 SIZE size;
142{
143 POINTER ptr = break_value;
98b7fe02
JB
144 if (obtain (size))
145 return ptr;
146 else
147 return 0;
dcfdbac7
JB
148}
149
150/* Note that SIZE bytes of space have been relinquished by the process.
956ace37 151 If SIZE is more than a page, return the space to the system. */
dcfdbac7
JB
152
153static void
154relinquish (size)
155 SIZE size;
156{
956ace37 157 POINTER new_page_break;
7516b7d5 158 int excess;
dcfdbac7 159
956ace37
JB
160 break_value -= size;
161 new_page_break = (POINTER) ROUNDUP (break_value);
7516b7d5 162 excess = (char *) page_break_value - (char *) new_page_break;
956ace37 163
7516b7d5 164 if (excess > extra_bytes * 2)
dcfdbac7 165 {
7516b7d5
RS
166 /* Keep extra_bytes worth of empty space.
167 And don't free anything unless we can free at least extra_bytes. */
168 if ((*real_morecore) (extra_bytes - excess) == 0)
dcfdbac7
JB
169 abort ();
170
7516b7d5 171 page_break_value += extra_bytes - excess;
dcfdbac7
JB
172 }
173
956ace37
JB
174 /* Zero the space from the end of the "official" break to the actual
175 break, so that bugs show up faster. */
176 bzero (break_value, ((char *) page_break_value - (char *) break_value));
dcfdbac7
JB
177}
178\f
956ace37
JB
179/* The meat - allocating, freeing, and relocating blocs. */
180
181/* These structures are allocated in the malloc arena.
182 The linked list is kept in order of increasing '.data' members.
183 The data blocks abut each other; if b->next is non-nil, then
184 b->data + b->size == b->next->data. */
dcfdbac7
JB
185typedef struct bp
186{
187 struct bp *next;
188 struct bp *prev;
189 POINTER *variable;
190 POINTER data;
191 SIZE size;
192} *bloc_ptr;
193
194#define NIL_BLOC ((bloc_ptr) 0)
195#define BLOC_PTR_SIZE (sizeof (struct bp))
196
197/* Head and tail of the list of relocatable blocs. */
198static bloc_ptr first_bloc, last_bloc;
199
956ace37 200/* Find the bloc referenced by the address in PTR. Returns a pointer
dcfdbac7
JB
201 to that block. */
202
203static bloc_ptr
204find_bloc (ptr)
205 POINTER *ptr;
206{
207 register bloc_ptr p = first_bloc;
208
209 while (p != NIL_BLOC)
210 {
211 if (p->variable == ptr && p->data == *ptr)
212 return p;
213
214 p = p->next;
215 }
216
217 return p;
218}
219
220/* Allocate a bloc of SIZE bytes and append it to the chain of blocs.
98b7fe02
JB
221 Returns a pointer to the new bloc, or zero if we couldn't allocate
222 memory for the new block. */
dcfdbac7
JB
223
224static bloc_ptr
225get_bloc (size)
226 SIZE size;
227{
98b7fe02
JB
228 register bloc_ptr new_bloc;
229
230 if (! (new_bloc = (bloc_ptr) malloc (BLOC_PTR_SIZE))
231 || ! (new_bloc->data = get_more_space (size)))
232 {
233 if (new_bloc)
234 free (new_bloc);
235
236 return 0;
237 }
dcfdbac7 238
dcfdbac7
JB
239 new_bloc->size = size;
240 new_bloc->next = NIL_BLOC;
8c7f1e35 241 new_bloc->variable = (POINTER *) NIL;
dcfdbac7
JB
242
243 if (first_bloc)
244 {
245 new_bloc->prev = last_bloc;
246 last_bloc->next = new_bloc;
247 last_bloc = new_bloc;
248 }
249 else
250 {
251 first_bloc = last_bloc = new_bloc;
252 new_bloc->prev = NIL_BLOC;
253 }
254
255 return new_bloc;
256}
257
258/* Relocate all blocs from BLOC on upward in the list to the zone
259 indicated by ADDRESS. Direction of relocation is determined by
260 the position of ADDRESS relative to BLOC->data.
261
ad3bb3d2
JB
262 If BLOC is NIL_BLOC, nothing is done.
263
dcfdbac7
JB
264 Note that ordering of blocs is not affected by this function. */
265
266static void
267relocate_some_blocs (bloc, address)
268 bloc_ptr bloc;
269 POINTER address;
270{
ad3bb3d2 271 if (bloc != NIL_BLOC)
dcfdbac7 272 {
ad3bb3d2
JB
273 register SIZE offset = address - bloc->data;
274 register SIZE data_size = 0;
275 register bloc_ptr b;
276
277 for (b = bloc; b != NIL_BLOC; b = b->next)
278 {
279 data_size += b->size;
280 b->data += offset;
281 *b->variable = b->data;
282 }
dcfdbac7 283
ad3bb3d2
JB
284 safe_bcopy (address - offset, address, data_size);
285 }
dcfdbac7
JB
286}
287
ad3bb3d2 288
dcfdbac7
JB
289/* Free BLOC from the chain of blocs, relocating any blocs above it
290 and returning BLOC->size bytes to the free area. */
291
292static void
293free_bloc (bloc)
294 bloc_ptr bloc;
295{
296 if (bloc == first_bloc && bloc == last_bloc)
297 {
298 first_bloc = last_bloc = NIL_BLOC;
299 }
300 else if (bloc == last_bloc)
301 {
302 last_bloc = bloc->prev;
303 last_bloc->next = NIL_BLOC;
304 }
305 else if (bloc == first_bloc)
306 {
307 first_bloc = bloc->next;
308 first_bloc->prev = NIL_BLOC;
dcfdbac7
JB
309 }
310 else
311 {
312 bloc->next->prev = bloc->prev;
313 bloc->prev->next = bloc->next;
dcfdbac7
JB
314 }
315
ad3bb3d2 316 relocate_some_blocs (bloc->next, bloc->data);
dcfdbac7
JB
317 relinquish (bloc->size);
318 free (bloc);
319}
320\f
956ace37
JB
321/* Interface routines. */
322
dcfdbac7
JB
323static int use_relocatable_buffers;
324
98b7fe02 325/* Obtain SIZE bytes of storage from the free pool, or the system, as
2c46d29f 326 necessary. If relocatable blocs are in use, this means relocating
98b7fe02
JB
327 them. This function gets plugged into the GNU malloc's __morecore
328 hook.
329
7516b7d5
RS
330 We provide hysteresis, never relocating by less than extra_bytes.
331
98b7fe02
JB
332 If we're out of memory, we should return zero, to imitate the other
333 __morecore hook values - in particular, __default_morecore in the
334 GNU malloc package. */
dcfdbac7
JB
335
336POINTER
337r_alloc_sbrk (size)
338 long size;
339{
7516b7d5
RS
340 /* This is the first address not currently available for the heap. */
341 POINTER top;
342 /* Amount of empty space below that. */
343 SIZE already_available;
dcfdbac7
JB
344 POINTER ptr;
345
346 if (! use_relocatable_buffers)
bbc60227 347 return (*real_morecore) (size);
dcfdbac7 348
7516b7d5
RS
349 top = first_bloc ? first_bloc->data : page_break_value;
350 already_available = (char *) top - (char *) virtual_break_value;
351
352 /* Do we not have enough gap already? */
353 if (size > 0 && already_available < size)
dcfdbac7 354 {
7516b7d5
RS
355 /* Get what we need, plus some extra so we can come here less often. */
356 SIZE get = size - already_available + extra_bytes;
357
358 if (! obtain (get))
98b7fe02
JB
359 return 0;
360
dcfdbac7 361 if (first_bloc)
ad3bb3d2 362 relocate_some_blocs (first_bloc, first_bloc->data + get);
956ace37 363
ad3bb3d2
JB
364 /* Zero out the space we just allocated, to help catch bugs
365 quickly. */
366 bzero (virtual_break_value, get);
dcfdbac7 367 }
7516b7d5
RS
368 /* Can we keep extra_bytes of gap while freeing at least extra_bytes? */
369 else if (size < 0 && already_available - size > 2 * extra_bytes)
dcfdbac7 370 {
7516b7d5
RS
371 /* Ok, do so. This is how many to free. */
372 SIZE give_back = already_available - size - extra_bytes;
373
dcfdbac7 374 if (first_bloc)
7516b7d5
RS
375 relocate_some_blocs (first_bloc, first_bloc->data - give_back);
376 relinquish (give_back);
dcfdbac7
JB
377 }
378
379 ptr = virtual_break_value;
380 virtual_break_value += size;
7516b7d5 381
dcfdbac7
JB
382 return ptr;
383}
384
385/* Allocate a relocatable bloc of storage of size SIZE. A pointer to
386 the data is returned in *PTR. PTR is thus the address of some variable
98b7fe02
JB
387 which will use the data area.
388
389 If we can't allocate the necessary memory, set *PTR to zero, and
390 return zero. */
dcfdbac7
JB
391
392POINTER
393r_alloc (ptr, size)
394 POINTER *ptr;
395 SIZE size;
396{
397 register bloc_ptr new_bloc;
398
2c46d29f
RS
399 if (! r_alloc_initialized)
400 r_alloc_init ();
401
dcfdbac7 402 new_bloc = get_bloc (size);
98b7fe02
JB
403 if (new_bloc)
404 {
405 new_bloc->variable = ptr;
406 *ptr = new_bloc->data;
407 }
408 else
409 *ptr = 0;
dcfdbac7
JB
410
411 return *ptr;
412}
413
2c46d29f
RS
414/* Free a bloc of relocatable storage whose data is pointed to by PTR.
415 Store 0 in *PTR to show there's no block allocated. */
dcfdbac7
JB
416
417void
418r_alloc_free (ptr)
419 register POINTER *ptr;
420{
421 register bloc_ptr dead_bloc;
422
dcfdbac7
JB
423 dead_bloc = find_bloc (ptr);
424 if (dead_bloc == NIL_BLOC)
425 abort ();
426
427 free_bloc (dead_bloc);
2c46d29f 428 *ptr = 0;
dcfdbac7
JB
429}
430
16a5c729 431/* Given a pointer at address PTR to relocatable data, resize it to SIZE.
98b7fe02
JB
432 Do this by shifting all blocks above this one up in memory, unless
433 SIZE is less than or equal to the current bloc size, in which case
434 do nothing.
dcfdbac7 435
98b7fe02
JB
436 Change *PTR to reflect the new bloc, and return this value.
437
438 If more memory cannot be allocated, then leave *PTR unchanged, and
439 return zero. */
dcfdbac7
JB
440
441POINTER
442r_re_alloc (ptr, size)
443 POINTER *ptr;
444 SIZE size;
445{
16a5c729 446 register bloc_ptr bloc;
dcfdbac7 447
16a5c729
JB
448 bloc = find_bloc (ptr);
449 if (bloc == NIL_BLOC)
dcfdbac7
JB
450 abort ();
451
16a5c729 452 if (size <= bloc->size)
956ace37 453 /* Wouldn't it be useful to actually resize the bloc here? */
dcfdbac7
JB
454 return *ptr;
455
98b7fe02
JB
456 if (! obtain (size - bloc->size))
457 return 0;
458
16a5c729 459 relocate_some_blocs (bloc->next, bloc->data + size);
dcfdbac7 460
16a5c729
JB
461 /* Zero out the new space in the bloc, to help catch bugs faster. */
462 bzero (bloc->data + bloc->size, size - bloc->size);
0abf54e3 463
16a5c729
JB
464 /* Indicate that this block has a new size. */
465 bloc->size = size;
dcfdbac7
JB
466
467 return *ptr;
468}
469\f
470/* The hook `malloc' uses for the function which gets more space
471 from the system. */
472extern POINTER (*__morecore) ();
473
474/* Intialize various things for memory allocation. */
475
2c46d29f
RS
476static void
477r_alloc_init ()
dcfdbac7 478{
2c46d29f 479 if (r_alloc_initialized)
dcfdbac7
JB
480 return;
481
2c46d29f 482 r_alloc_initialized = 1;
bbc60227 483 real_morecore = __morecore;
dcfdbac7 484 __morecore = r_alloc_sbrk;
8c7f1e35 485
bbc60227 486 virtual_break_value = break_value = (*real_morecore) (0);
aef4d570 487 if (break_value == NIL)
2c46d29f 488 abort ();
8c7f1e35 489
7516b7d5
RS
490 page_size = PAGE;
491 extra_bytes = ROUNDUP (50000);
492
dcfdbac7 493 page_break_value = (POINTER) ROUNDUP (break_value);
2c46d29f
RS
494 /* Clear the rest of the last page; this memory is in our address space
495 even though it is after the sbrk value. */
dcfdbac7
JB
496 bzero (break_value, (page_break_value - break_value));
497 use_relocatable_buffers = 1;
2c46d29f 498}