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 ();
+}