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