/* Block-relocating memory allocator.
- Copyright (C) 1992 Free Software Foundation, Inc.
+ Copyright (C) 1993 Free Software Foundation, Inc.
This file is part of GNU Emacs.
/* NOTES:
- Only relocate the blocs neccessary for SIZE in r_alloc_sbrk,
+ Only relocate the blocs necessary 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. */
#ifdef emacs
-#include "config.h"
+#include <config.h>
#include "lisp.h" /* Needed for VALBITS. */
#undef NULL
+/* The important properties of this type are that 1) it's a pointer, and
+ 2) arithmetic on it should work as if the size of the object pointed
+ to has a size of 1. */
+#if 0 /* Arithmetic on void* is a GCC extension. */
+#ifdef __STDC__
+typedef void *POINTER;
+#else
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+typedef char *POINTER;
+
+#endif
+#endif /* 0 */
+
+/* Unconditionally use char * for this. */
+typedef char *POINTER;
+
+typedef unsigned long SIZE;
+
/* Declared in dispnew.c, this version doesn't screw up if regions
overlap. */
extern void safe_bcopy ();
typedef size_t SIZE;
typedef void *POINTER;
-#define EXCEEDS_LISP_PTR(x) 0
-
#include <unistd.h>
#include <malloc.h>
#include <string.h>
/* The REAL (i.e., page aligned) break value of the process. */
static POINTER page_break_value;
+/* This is the size of a page. We round memory requests to this boundary. */
+static int page_size;
+
+/* Whenever we get memory from the system, get this many extra bytes. This
+ must be a multiple of page_size. */
+static int extra_bytes;
+
/* 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 - 1) & ~(PAGE - 1))
-#define ROUND_TO_PAGE(addr) (addr & (~(PAGE - 1)))
+#define ALIGNED(addr) (((unsigned long int) (addr) & (page_size - 1)) == 0)
+#define ROUNDUP(size) (((unsigned long int) (size) + page_size - 1) \
+ & ~(page_size - 1))
+#define ROUND_TO_PAGE(addr) (addr & (~(page_size - 1)))
\f
/* Functions to get and return memory from the system. */
if (already_available < size)
{
SIZE get = ROUNDUP (size - already_available);
+ /* Get some extra, so we can come here less often. */
+ get += extra_bytes;
if ((*real_morecore) (get) == 0)
return 0;
SIZE size;
{
POINTER new_page_break;
+ int excess;
break_value -= size;
new_page_break = (POINTER) ROUNDUP (break_value);
+ excess = (char *) page_break_value - (char *) new_page_break;
- if (new_page_break != page_break_value)
+ if (excess > extra_bytes * 2)
{
- if ((*real_morecore) ((char *) new_page_break
- - (char *) page_break_value) == 0)
+ /* Keep extra_bytes worth of empty space.
+ And don't free anything unless we can free at least extra_bytes. */
+ if ((*real_morecore) (extra_bytes - excess) == 0)
abort ();
- page_break_value = new_page_break;
+ page_break_value += extra_bytes - excess;
}
/* Zero the space from the end of the "official" break to the actual
indicated by ADDRESS. Direction of relocation is determined by
the position of ADDRESS relative to BLOC->data.
+ If BLOC is NIL_BLOC, nothing is done.
+
Note that ordering of blocs is not affected by this function. */
static void
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)
+ if (bloc != NIL_BLOC)
{
- data_zone_size += b->size;
- b->data -= offset;
- *b->variable = b->data;
- }
+ register SIZE offset = address - bloc->data;
+ register SIZE data_size = 0;
+ register bloc_ptr b;
+
+ for (b = bloc; b != NIL_BLOC; b = b->next)
+ {
+ data_size += b->size;
+ b->data += offset;
+ *b->variable = b->data;
+ }
- safe_bcopy (data_zone, new_data_zone, data_zone_size);
+ safe_bcopy (address - offset, address, data_size);
+ }
}
+
/* Free BLOC from the chain of blocs, relocating any blocs above it
and returning BLOC->size bytes to the free area. */
{
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);
}
+ relocate_some_blocs (bloc->next, bloc->data);
relinquish (bloc->size);
free (bloc);
}
them. This function gets plugged into the GNU malloc's __morecore
hook.
+ We provide hysteresis, never relocating by less than extra_bytes.
+
If we're out of memory, we should return zero, to imitate the other
__morecore hook values - in particular, __default_morecore in the
GNU malloc package. */
r_alloc_sbrk (size)
long size;
{
+ /* This is the first address not currently available for the heap. */
+ POINTER top;
+ /* Amount of empty space below that. */
+ /* It is not correct to use SIZE here, because that is usually unsigned.
+ ptrdiff_t would be okay, but is not always available.
+ `long' will work in all cases, in practice. */
+ long already_available;
POINTER ptr;
if (! use_relocatable_buffers)
return (*real_morecore) (size);
- if (size > 0)
+ top = first_bloc ? first_bloc->data : page_break_value;
+ already_available = (char *) top - (char *) virtual_break_value;
+
+ /* Do we not have enough gap already? */
+ if (size > 0 && already_available < size)
{
- if (! obtain (size))
+ /* Get what we need, plus some extra so we can come here less often. */
+ SIZE get = size - already_available + extra_bytes;
+
+ if (! obtain (get))
return 0;
if (first_bloc)
- {
- relocate_some_blocs (first_bloc, first_bloc->data + size);
+ relocate_some_blocs (first_bloc, first_bloc->data + get);
- /* Zero out the space we just allocated, to help catch bugs
- quickly. */
- bzero (virtual_break_value, size);
- }
+ /* Zero out the space we just allocated, to help catch bugs
+ quickly. */
+ bzero (virtual_break_value, get);
}
- else if (size < 0)
+ /* Can we keep extra_bytes of gap while freeing at least extra_bytes? */
+ else if (size < 0 && already_available - size > 2 * extra_bytes)
{
+ /* Ok, do so. This is how many to free. */
+ SIZE give_back = already_available - size - extra_bytes;
+
if (first_bloc)
- relocate_some_blocs (first_bloc, first_bloc->data + size);
- relinquish (- size);
+ relocate_some_blocs (first_bloc, first_bloc->data - give_back);
+ relinquish (give_back);
}
ptr = virtual_break_value;
virtual_break_value += size;
+
return ptr;
}
from the system. */
extern POINTER (*__morecore) ();
-/* Intialize various things for memory allocation. */
+/* Initialize various things for memory allocation. */
static void
r_alloc_init ()
if (break_value == NIL)
abort ();
+ page_size = PAGE;
+ extra_bytes = ROUNDUP (50000);
+
page_break_value = (POINTER) ROUNDUP (break_value);
+
+ /* The extra call to real_morecore guarantees that the end of the
+ address space is a multiple of page_size, even if page_size is
+ not really the page size of the system running the binary in
+ which page_size is stored. This allows a binary to be built on a
+ system with one page size and run on a system with a smaller page
+ size. */
+ (*real_morecore) (page_break_value - break_value);
+
/* Clear the rest of the last page; this memory is in our address space
even though it is after the sbrk value. */
+ /* Doubly true, with the additional call that explicitly adds the
+ rest of that page to the address space. */
bzero (break_value, (page_break_value - break_value));
+ virtual_break_value = break_value = page_break_value;
use_relocatable_buffers = 1;
}