diff options
| author | Jim Blandy | 1990-11-12 20:20:45 +0000 |
|---|---|---|
| committer | Jim Blandy | 1990-11-12 20:20:45 +0000 |
| commit | dcfdbac7bb0fd364ddf542ed10b9ff2271c37096 (patch) | |
| tree | ddf67a3f258cffea86f4359b430a7171f97babb9 /src/ralloc.c | |
| parent | 8a281f86e1a71be3a15402fef758bbd19837007e (diff) | |
| download | emacs-dcfdbac7bb0fd364ddf542ed10b9ff2271c37096.tar.gz emacs-dcfdbac7bb0fd364ddf542ed10b9ff2271c37096.zip | |
Initial revision
Diffstat (limited to 'src/ralloc.c')
| -rw-r--r-- | src/ralloc.c | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/src/ralloc.c b/src/ralloc.c new file mode 100644 index 00000000000..1f92b51be88 --- /dev/null +++ b/src/ralloc.c | |||
| @@ -0,0 +1,426 @@ | |||
| 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 | } | ||