Mercurial > emacs
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ralloc.c Mon Nov 12 20:20:45 1990 +0000 @@ -0,0 +1,426 @@ +/* Block-relocating memory allocator. + Copyright (C) 1990 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* NOTES: + + Only relocate the blocs neccessary for SIZE in r_alloc_sbrk, + rather than all of them. This means allowing for a possible + hole between the first bloc and the end of malloc storage. */ + +#include "config.h" +#include "lisp.h" /* Needed for xterm.h */ +#undef NULL +#include "mem_limits.h" +#include "xterm.h" /* Needed for BLOCK_INPUT */ + +#define NIL ((POINTER) 0) + + +/* System call to set the break value. */ +extern POINTER sbrk (); + +/* The break value, as seen by malloc (). */ +static POINTER virtual_break_value; + +/* The break value, viewed by the relocatable blocs. */ +static POINTER break_value; + +/* The REAL (i.e., page aligned) break value of the process. */ +static POINTER page_break_value; + +/* Macros for rounding. Note that rounding to any value is possible + by changing the definition of PAGE. */ +#define PAGE (getpagesize ()) +#define ALIGNED(addr) (((unsigned int) (addr) & (PAGE - 1)) == 0) +#define ROUNDUP(size) (((unsigned int) (size) + PAGE) & ~(PAGE - 1)) +#define ROUND_TO_PAGE(addr) (addr & (~(PAGE - 1))) +#define EXCEEDS_ELISP_PTR(ptr) ((unsigned int) (ptr) >> VALBITS) + +/* Level of warnings issued. */ +static int warnlevel; + +/* Function to call to issue a warning; + 0 means don't issue them. */ +static void (*warnfunction) (); + +static void +check_memory_limits (address) + POINTER address; +{ + SIZE data_size = address - data_space_start; + + switch (warnlevel) + { + case 0: + if (data_size > (lim_data / 4) * 3) + { + warnlevel++; + (*warnfunction) ("Warning: past 75% of memory limit"); + } + break; + + case 1: + if (data_size > (lim_data / 20) * 17) + { + warnlevel++; + (*warnfunction) ("Warning: past 85% of memory limit"); + } + break; + + case 2: + if (data_size > (lim_data / 20) * 19) + { + warnlevel++; + (*warnfunction) ("Warning: past 95% of memory limit"); + } + break; + + default: + (*warnfunction) ("Warning: past acceptable memory limits"); + break; + } + + if (EXCEEDS_ELISP_PTR (address)) + (*warnfunction) ("Warning: memory in use exceeds lisp pointer size"); +} + +/* Obtain SIZE bytes of space. If enough space is not presently available + in our process reserve, (i.e., (page_break_value - break_value)), + this means getting more page-aligned space from the system. */ + +static void +obtain (size) + SIZE size; +{ + SIZE already_available = page_break_value - break_value; + + if (already_available < size) + { + SIZE get = ROUNDUP (size); + + if (warnfunction) + check_memory_limits (page_break_value); + + if (((int) sbrk (get)) < 0) + abort (); + + page_break_value += get; + } + + break_value += size; +} + +/* Obtain SIZE bytes of space and return a pointer to the new area. */ + +static POINTER +get_more_space (size) + SIZE size; +{ + POINTER ptr = break_value; + obtain (size); + return ptr; +} + +/* Note that SIZE bytes of space have been relinquished by the process. + If SIZE is more than a page, return the space the system. */ + +static void +relinquish (size) + SIZE size; +{ + SIZE page_part = ROUND_TO_PAGE (size); + + if (page_part) + { + if (((int) (sbrk (- page_part))) < 0) + abort (); + + page_break_value -= page_part; + } + + break_value -= size; + bzero (break_value, (size - page_part)); +} + +typedef struct bp +{ + struct bp *next; + struct bp *prev; + POINTER *variable; + POINTER data; + SIZE size; +} *bloc_ptr; + +#define NIL_BLOC ((bloc_ptr) 0) +#define BLOC_PTR_SIZE (sizeof (struct bp)) + +/* Head and tail of the list of relocatable blocs. */ +static bloc_ptr first_bloc, last_bloc; + +/* Declared in dispnew.c, this version dosen't fuck up if regions overlap. */ +extern void safe_bcopy (); + +/* Find the bloc reference by the address in PTR. Returns a pointer + to that block. */ + +static bloc_ptr +find_bloc (ptr) + POINTER *ptr; +{ + register bloc_ptr p = first_bloc; + + while (p != NIL_BLOC) + { + if (p->variable == ptr && p->data == *ptr) + return p; + + p = p->next; + } + + return p; +} + +/* Allocate a bloc of SIZE bytes and append it to the chain of blocs. + Returns a pointer to the new bloc. */ + +static bloc_ptr +get_bloc (size) + SIZE size; +{ + register bloc_ptr new_bloc = (bloc_ptr) malloc (BLOC_PTR_SIZE); + + new_bloc->data = get_more_space (size); + new_bloc->size = size; + new_bloc->next = NIL_BLOC; + new_bloc->variable = NIL; + + if (first_bloc) + { + new_bloc->prev = last_bloc; + last_bloc->next = new_bloc; + last_bloc = new_bloc; + } + else + { + first_bloc = last_bloc = new_bloc; + new_bloc->prev = NIL_BLOC; + } + + return new_bloc; +} + +/* Relocate all blocs from BLOC on upward in the list to the zone + indicated by ADDRESS. Direction of relocation is determined by + the position of ADDRESS relative to BLOC->data. + + Note that ordering of blocs is not affected by this function. */ + +static void +relocate_some_blocs (bloc, address) + bloc_ptr bloc; + POINTER address; +{ + register bloc_ptr b; + POINTER data_zone = bloc->data; + register SIZE data_zone_size = 0; + register SIZE offset = bloc->data - address; + POINTER new_data_zone = data_zone - offset; + + for (b = bloc; b != NIL_BLOC; b = b->next) + { + data_zone_size += b->size; + b->data -= offset; + *b->variable = b->data; + } + + safe_bcopy (data_zone, new_data_zone, data_zone_size); +} + +/* Free BLOC from the chain of blocs, relocating any blocs above it + and returning BLOC->size bytes to the free area. */ + +static void +free_bloc (bloc) + bloc_ptr bloc; +{ + if (bloc == first_bloc && bloc == last_bloc) + { + first_bloc = last_bloc = NIL_BLOC; + } + else if (bloc == last_bloc) + { + last_bloc = bloc->prev; + last_bloc->next = NIL_BLOC; + } + else if (bloc == first_bloc) + { + first_bloc = bloc->next; + first_bloc->prev = NIL_BLOC; + relocate_some_blocs (bloc->next, bloc->data); + } + else + { + bloc->next->prev = bloc->prev; + bloc->prev->next = bloc->next; + relocate_some_blocs (bloc->next, bloc->data); + } + + relinquish (bloc->size); + free (bloc); +} + +static int use_relocatable_buffers; + +/* Obtain SIZE bytes of storage from the free pool, or the system, + as neccessary. If relocatable blocs are in use, this means + relocating them. */ + +POINTER +r_alloc_sbrk (size) + long size; +{ + POINTER ptr; + + if (! use_relocatable_buffers) + return sbrk (size); + + if (size > 0) + { + obtain (size); + if (first_bloc) + { + relocate_some_blocs (first_bloc, first_bloc->data + size); + bzero (virtual_break_value, size); + } + } + else if (size < 0) + { + if (first_bloc) + relocate_some_blocs (first_bloc, first_bloc->data + size); + relinquish (- size); + } + + ptr = virtual_break_value; + virtual_break_value += size; + return ptr; +} + +/* Allocate a relocatable bloc of storage of size SIZE. A pointer to + the data is returned in *PTR. PTR is thus the address of some variable + which will use the data area. */ + +POINTER +r_alloc (ptr, size) + POINTER *ptr; + SIZE size; +{ + register bloc_ptr new_bloc; + + BLOCK_INPUT; + new_bloc = get_bloc (size); + new_bloc->variable = ptr; + *ptr = new_bloc->data; + UNBLOCK_INPUT; + + return *ptr; +} + +/* Free a bloc of relocatable storage whose data is pointed to by PTR. */ + +void +r_alloc_free (ptr) + register POINTER *ptr; +{ + register bloc_ptr dead_bloc; + + BLOCK_INPUT; + dead_bloc = find_bloc (ptr); + if (dead_bloc == NIL_BLOC) + abort (); + + free_bloc (dead_bloc); + UNBLOCK_INPUT; +} + +/* Given a pointer at address PTR to relocatable data, resize it + to SIZE. This is done by obtaining a new block and freeing the + old, unless SIZE is less than or equal to the current bloc size, + in which case nothing happens and the current value is returned. + + The contents of PTR is changed to reflect the new bloc, and this + value is returned. */ + +POINTER +r_re_alloc (ptr, size) + POINTER *ptr; + SIZE size; +{ + register bloc_ptr old_bloc, new_bloc; + + BLOCK_INPUT; + old_bloc = find_bloc (ptr); + if (old_bloc == NIL_BLOC) + abort (); + + if (size <= old_bloc->size) + return *ptr; + + new_bloc = get_bloc (size); + new_bloc->variable = ptr; + safe_bcopy (old_bloc->data, new_bloc->data, old_bloc->size); + *ptr = new_bloc->data; + + free_bloc (old_bloc); + UNBLOCK_INPUT; + + return *ptr; +} + +/* The hook `malloc' uses for the function which gets more space + from the system. */ +extern POINTER (*__morecore) (); + +/* Intialize various things for memory allocation. */ + +void +malloc_init (start, warn_func) + POINTER start; + void (*warn_func) (); +{ + static int malloc_initialized = 0; + + if (start) + data_space_start = start; + + if (malloc_initialized) + return; + + malloc_initialized = 1; + __morecore = r_alloc_sbrk; + virtual_break_value = break_value = sbrk (0); + page_break_value = (POINTER) ROUNDUP (break_value); + bzero (break_value, (page_break_value - break_value)); + use_relocatable_buffers = 1; + + lim_data = 0; + warnlevel = 0; + warnfunction = warn_func; + + get_lim_data (); +}