vm-limit.c 5.77 KB
Newer Older
Joseph Arceneaux's avatar
Joseph Arceneaux committed
1
/* Functions for memory limit warnings.
Paul Eggert's avatar
Paul Eggert committed
2
   Copyright (C) 1990, 1992, 2001-2020 Free Software Foundation, Inc.
Joseph Arceneaux's avatar
Joseph Arceneaux committed
3 4 5

This file is part of GNU Emacs.

6
GNU Emacs is free software: you can redistribute it and/or modify
Joseph Arceneaux's avatar
Joseph Arceneaux committed
7
it under the terms of the GNU General Public License as published by
8 9
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
Joseph Arceneaux's avatar
Joseph Arceneaux committed
10 11 12 13 14 15 16

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
17
along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
Joseph Arceneaux's avatar
Joseph Arceneaux committed
18

19
#include <config.h>
20
#include <unistd.h> /* for 'environ', on AIX */
Joseph Arceneaux's avatar
Joseph Arceneaux committed
21
#include "lisp.h"
22 23

#ifdef MSDOS
24
#include "dosfns.h"
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
extern int etext;
#endif

/* Some systems need this before <sys/resource.h>.  */
#include <sys/types.h>

#ifdef HAVE_SYS_RESOURCE_H
# include <sys/time.h>
# include <sys/resource.h>
#else
# if HAVE_SYS_VLIMIT_H
#  include <sys/vlimit.h>	/* Obsolete, says glibc */
# endif
#endif

/* Start of data.  It is OK if this is approximate; it's used only as
   a heuristic.  */
#ifdef DATA_START
# define data_start ((char *) DATA_START)
#else
extern char data_start[];
# ifndef HAVE_DATA_START
/* Initialize to nonzero, so that it's put into data and not bss.
   Link this file's object code first, so that this symbol is near the
   start of data.  */
char data_start[1] = { 1 };
# endif
#endif
Joseph Arceneaux's avatar
Joseph Arceneaux committed
53

54 55 56
#ifdef HAVE_MALLOC_H
# include <malloc.h>
#endif
57
#ifndef DOUG_LEA_MALLOC
58 59 60
# ifndef __MALLOC_HOOK_VOLATILE
#  define __MALLOC_HOOK_VOLATILE volatile
# endif
Paul Eggert's avatar
Paul Eggert committed
61
extern void *(*__morecore) (ptrdiff_t);
62 63
extern void (*__MALLOC_HOOK_VOLATILE __after_morecore_hook) (void);
#endif
Paul Eggert's avatar
Paul Eggert committed
64 65 66 67 68 69

/* From ralloc.c.  */
#ifdef REL_ALLOC
extern void *(*real_morecore) (ptrdiff_t);
#endif

Joseph Arceneaux's avatar
Joseph Arceneaux committed
70 71 72 73 74
/*
  Level number of warnings already issued.
  0 -- no warnings issued.
  1 -- 75% warning already issued.
  2 -- 85% warning already issued.
75
  3 -- 95% warning issued; keep warning frequently.
Joseph Arceneaux's avatar
Joseph Arceneaux committed
76
*/
77 78
enum warnlevel { not_warned, warned_75, warned_85, warned_95 };
static enum warnlevel warnlevel;
Joseph Arceneaux's avatar
Joseph Arceneaux committed
79 80 81

/* Function to call to issue a warning;
   0 means don't issue them.  */
82
static void (*warn_function) (const char *);
Joseph Arceneaux's avatar
Joseph Arceneaux committed
83

84 85
/* Start of data space; can be changed by calling memory_warnings.  */
static char *data_space_start;
86 87

/* Number of bytes of writable memory we can expect to be able to get.  */
88
static size_t lim_data;
89

Paul Eggert's avatar
Paul Eggert committed
90
#ifdef HAVE_GETRLIMIT
91

Paul Eggert's avatar
Paul Eggert committed
92 93 94
# ifndef RLIMIT_AS
#  define RLIMIT_AS RLIMIT_DATA
# endif
95 96

static void
97
get_lim_data (void)
98
{
Paul Eggert's avatar
Paul Eggert committed
99 100 101 102 103 104 105 106
  /* Set LIM_DATA to the minimum of the maximum object size and the
     maximum address space.  Don't bother to check for values like
     RLIM_INFINITY since in practice they are not much less than SIZE_MAX.  */
  struct rlimit rlimit;
  lim_data
    = (getrlimit (RLIMIT_AS, &rlimit) == 0 && rlimit.rlim_cur <= SIZE_MAX
       ? rlimit.rlim_cur
       : SIZE_MAX);
107 108
}

Paul Eggert's avatar
Paul Eggert committed
109
#elif defined WINDOWSNT
110

111 112
#include "w32heap.h"

113
static void
114
get_lim_data (void)
115
{
116
  extern size_t reserved_heap_size;
117 118 119
  lim_data = reserved_heap_size;
}

Paul Eggert's avatar
Paul Eggert committed
120
#elif defined MSDOS
121 122

void
123
get_lim_data (void)
124
{
125 126 127 128
  unsigned long totalram, freeram, totalswap, freeswap;

  dos_memory_info (&totalram, &freeram, &totalswap, &freeswap);
  lim_data = freeram;
129
  /* Don't believe they will give us more that 0.5 GB.   */
130 131
  if (lim_data > 512U * 1024U * 1024U)
    lim_data = 512U * 1024U * 1024U;
132
}
133 134

unsigned long
135
ret_lim_data (void)
136 137 138 139
{
  get_lim_data ();
  return lim_data;
}
140
#else
Paul Eggert's avatar
Paul Eggert committed
141
# error "get_lim_data not implemented on this machine"
142 143 144
#endif

/* Verify amount of memory available, complaining if we're near the end. */
Joseph Arceneaux's avatar
Joseph Arceneaux committed
145

146
static void
147
check_memory_limits (void)
Joseph Arceneaux's avatar
Joseph Arceneaux committed
148
{
Paul Eggert's avatar
Paul Eggert committed
149
#ifndef REL_ALLOC
150
  void *(*real_morecore) (ptrdiff_t) = 0;
151
#endif
152

153
  char *cp;
154 155
  size_t five_percent;
  size_t data_size;
156
  enum warnlevel new_warnlevel;
Joseph Arceneaux's avatar
Joseph Arceneaux committed
157 158 159

  if (lim_data == 0)
    get_lim_data ();
160
  five_percent = lim_data / 20;
Joseph Arceneaux's avatar
Joseph Arceneaux committed
161 162

  /* Find current end of memory and issue warning if getting near max */
163 164
  cp = (real_morecore ? real_morecore : __morecore) (0);
  data_size = cp - data_space_start;
Joseph Arceneaux's avatar
Joseph Arceneaux committed
165

166 167 168 169
  if (!warn_function)
    return;

  /* What level of warning does current memory usage demand?  */
170 171 172 173 174
  new_warnlevel
    = (data_size > five_percent * 19) ? warned_95
    : (data_size > five_percent * 17) ? warned_85
    : (data_size > five_percent * 15) ? warned_75
    : not_warned;
175 176 177 178 179

  /* If we have gone up a level, give the appropriate warning.  */
  if (new_warnlevel > warnlevel || new_warnlevel == warned_95)
    {
      warnlevel = new_warnlevel;
180
      static char const *const warn_diagnostic[] =
181
	{
182 183 184 185 186
	  "Warning: past 75% of memory limit",
	  "Warning: past 85% of memory limit",
	  "Warning: past 95% of memory limit"
	};
      warn_function (warn_diagnostic[warnlevel - 1]);
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
    }
  /* Handle going down in usage levels, with some hysteresis.  */
  else
    {
      /* If we go down below 70% full, issue another 75% warning
	 when we go up again.  */
      if (data_size < five_percent * 14)
	warnlevel = not_warned;
      /* If we go down below 80% full, issue another 85% warning
	 when we go up again.  */
      else if (warnlevel > warned_75 && data_size < five_percent * 16)
	warnlevel = warned_75;
      /* If we go down below 90% full, issue another 95% warning
	 when we go up again.  */
      else if (warnlevel > warned_85 && data_size < five_percent * 18)
	warnlevel = warned_85;
    }
Joseph Arceneaux's avatar
Joseph Arceneaux committed
204
}
205 206 207 208

/* Enable memory usage warnings.
   START says where the end of pure storage is.
   WARNFUN specifies the function to call to issue a warning.  */
Joseph Arceneaux's avatar
Joseph Arceneaux committed
209 210

void
Paul Eggert's avatar
Paul Eggert committed
211
memory_warnings (void *start, void (*warnfun) (const char *))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
212
{
213
  data_space_start = start ? start : data_start;
214

215
  warn_function = warnfun;
216
  __after_morecore_hook = check_memory_limits;
217 218 219

  /* Force data limit to be recalculated on each run.  */
  lim_data = 0;
Joseph Arceneaux's avatar
Joseph Arceneaux committed
220
}