summaryrefslogtreecommitdiff
path: root/src/alloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/alloc.c')
-rw-r--r--src/alloc.c200
1 files changed, 93 insertions, 107 deletions
diff --git a/src/alloc.c b/src/alloc.c
index 2d490f3bb75..9fbd0d05739 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -224,7 +224,7 @@ struct emacs_globals globals;
/* maybe_gc collects garbage if this goes negative. */
-intmax_t consing_until_gc;
+EMACS_INT consing_until_gc;
#ifdef HAVE_PDUMPER
/* Number of finalizers run: used to loop over GC until we stop
@@ -238,10 +238,17 @@ bool gc_in_progress;
/* System byte and object counts reported by GC. */
+/* Assume byte counts fit in uintptr_t and object counts fit into
+ intptr_t. */
typedef uintptr_t byte_ct;
typedef intptr_t object_ct;
-/* Number of live and free conses etc. */
+/* Large-magnitude value for a threshold count, which fits in EMACS_INT.
+ Using only half the EMACS_INT range avoids overflow hassles.
+ There is no need to fit these counts into fixnums. */
+#define HI_THRESHOLD (EMACS_INT_MAX / 2)
+
+/* Number of live and free conses etc. counted by the most-recent GC. */
static struct gcstat
{
@@ -299,7 +306,7 @@ static intptr_t garbage_collection_inhibited;
/* The GC threshold in bytes, the last time it was calculated
from gc-cons-threshold and gc-cons-percentage. */
-static intmax_t gc_threshold;
+static EMACS_INT gc_threshold;
/* If nonzero, this is a warning delivered by malloc and not yet
displayed. */
@@ -536,6 +543,15 @@ XFLOAT_INIT (Lisp_Object f, double n)
XFLOAT (f)->u.data = n;
}
+/* Account for allocation of NBYTES in the heap. This is a separate
+ function to avoid hassles with implementation-defined conversion
+ from unsigned to signed types. */
+static void
+tally_consing (ptrdiff_t nbytes)
+{
+ consing_until_gc -= nbytes;
+}
+
#ifdef DOUG_LEA_MALLOC
static bool
pointers_fit_in_lispobj_p (void)
@@ -560,7 +576,7 @@ struct Lisp_Finalizer finalizers;
/* Head of a circularly-linked list of finalizers that must be invoked
because we deemed them unreachable. This list must be global, and
- not a local inside garbage_collect_1, in case we GC again while
+ not a local inside garbage_collect, in case we GC again while
running finalizers. */
struct Lisp_Finalizer doomed_finalizers;
@@ -1366,16 +1382,14 @@ make_interval (void)
newi->next = interval_block;
interval_block = newi;
interval_block_index = 0;
- gcstat.total_free_intervals += INTERVAL_BLOCK_SIZE;
}
val = &interval_block->intervals[interval_block_index++];
}
MALLOC_UNBLOCK_INPUT;
- consing_until_gc -= sizeof (struct interval);
+ tally_consing (sizeof (struct interval));
intervals_consed++;
- gcstat.total_free_intervals--;
RESET_INTERVAL (val);
val->gcmarkbit = 0;
return val;
@@ -1730,8 +1744,6 @@ allocate_string (void)
NEXT_FREE_LISP_STRING (s) = string_free_list;
string_free_list = ptr_bounds_clip (s, sizeof *s);
}
-
- gcstat.total_free_strings += STRING_BLOCK_SIZE;
}
check_string_free_list ();
@@ -1742,10 +1754,8 @@ allocate_string (void)
MALLOC_UNBLOCK_INPUT;
- gcstat.total_free_strings--;
- gcstat.total_strings++;
++strings_consed;
- consing_until_gc -= sizeof *s;
+ tally_consing (sizeof *s);
#ifdef GC_CHECK_STRING_BYTES
if (!noninteractive)
@@ -1865,7 +1875,7 @@ allocate_string_data (struct Lisp_String *s,
old_data->string = NULL;
}
- consing_until_gc -= needed;
+ tally_consing (needed);
}
@@ -2461,7 +2471,6 @@ make_float (double float_value)
memset (new->gcmarkbits, 0, sizeof new->gcmarkbits);
float_block = new;
float_block_index = 0;
- gcstat.total_free_floats += FLOAT_BLOCK_SIZE;
}
XSETFLOAT (val, &float_block->floats[float_block_index]);
float_block_index++;
@@ -2471,9 +2480,8 @@ make_float (double float_value)
XFLOAT_INIT (val, float_value);
eassert (!XFLOAT_MARKED_P (XFLOAT (val)));
- consing_until_gc -= sizeof (struct Lisp_Float);
+ tally_consing (sizeof (struct Lisp_Float));
floats_consed++;
- gcstat.total_free_floats--;
return val;
}
@@ -2543,9 +2551,8 @@ free_cons (struct Lisp_Cons *ptr)
ptr->u.s.u.chain = cons_free_list;
ptr->u.s.car = dead_object ();
cons_free_list = ptr;
- if (INT_ADD_WRAPV (consing_until_gc, sizeof *ptr, &consing_until_gc))
- consing_until_gc = INTMAX_MAX;
- gcstat.total_free_conses++;
+ ptrdiff_t nbytes = sizeof *ptr;
+ tally_consing (-nbytes);
}
DEFUN ("cons", Fcons, Scons, 2, 2, 0,
@@ -2565,26 +2572,12 @@ DEFUN ("cons", Fcons, Scons, 2, 2, 0,
{
if (cons_block_index == CONS_BLOCK_SIZE)
{
- /* Maximum number of conses that should be active at any
- given time, so that list lengths fit into a ptrdiff_t and
- into a fixnum. */
- ptrdiff_t max_conses = min (PTRDIFF_MAX, MOST_POSITIVE_FIXNUM);
-
- /* This check is typically optimized away, as a runtime
- check is needed only on weird platforms where a count of
- distinct conses might not fit. */
- if (max_conses < INTPTR_MAX / sizeof (struct Lisp_Cons)
- && (max_conses - CONS_BLOCK_SIZE
- < gcstat.total_free_conses + gcstat.total_conses))
- memory_full (sizeof (struct cons_block));
-
struct cons_block *new
= lisp_align_malloc (sizeof *new, MEM_TYPE_CONS);
memset (new->gcmarkbits, 0, sizeof new->gcmarkbits);
new->next = cons_block;
cons_block = new;
cons_block_index = 0;
- gcstat.total_free_conses += CONS_BLOCK_SIZE;
}
XSETCONS (val, &cons_block->conses[cons_block_index]);
cons_block_index++;
@@ -2596,7 +2589,6 @@ DEFUN ("cons", Fcons, Scons, 2, 2, 0,
XSETCDR (val, cdr);
eassert (!XCONS_MARKED_P (XCONS (val)));
consing_until_gc -= sizeof (struct Lisp_Cons);
- gcstat.total_free_conses--;
cons_cells_consed++;
return val;
}
@@ -2855,7 +2847,6 @@ setup_on_free_list (struct Lisp_Vector *v, ptrdiff_t nbytes)
eassert (vindex < VECTOR_MAX_FREE_LIST_INDEX);
set_next_vector (v, vector_free_lists[vindex]);
vector_free_lists[vindex] = v;
- gcstat.total_free_vector_slots += nbytes / word_size;
}
/* Get a new vector block. */
@@ -2903,7 +2894,6 @@ allocate_vector_from_block (ptrdiff_t nbytes)
{
vector = vector_free_lists[index];
vector_free_lists[index] = next_vector (vector);
- gcstat.total_free_vector_slots -= nbytes / word_size;
return vector;
}
@@ -2917,7 +2907,6 @@ allocate_vector_from_block (ptrdiff_t nbytes)
/* This vector is larger than requested. */
vector = vector_free_lists[index];
vector_free_lists[index] = next_vector (vector);
- gcstat.total_free_vector_slots -= nbytes / word_size;
/* Excess bytes are used for the smaller vector,
which should be set on an appropriate free list. */
@@ -3092,7 +3081,10 @@ sweep_vectors (void)
space was coalesced into the only free vector. */
free_this_block = true;
else
- setup_on_free_list (vector, total_bytes);
+ {
+ setup_on_free_list (vector, total_bytes);
+ gcstat.total_free_vector_slots += total_bytes / word_size;
+ }
}
}
@@ -3177,7 +3169,7 @@ allocate_vectorlike (ptrdiff_t len)
if (find_suspicious_object_in_range (p, (char *) p + nbytes))
emacs_abort ();
- consing_until_gc -= nbytes;
+ tally_consing (nbytes);
vector_cells_consed += len;
MALLOC_UNBLOCK_INPUT;
@@ -3454,7 +3446,6 @@ Its value is void, and its function definition and property list are nil. */)
new->next = symbol_block;
symbol_block = new;
symbol_block_index = 0;
- gcstat.total_free_symbols += SYMBOL_BLOCK_SIZE;
}
XSETSYMBOL (val, &symbol_block->symbols[symbol_block_index]);
symbol_block_index++;
@@ -3463,9 +3454,8 @@ Its value is void, and its function definition and property list are nil. */)
MALLOC_UNBLOCK_INPUT;
init_symbol (val, name);
- consing_until_gc -= sizeof (struct Lisp_Symbol);
+ tally_consing (sizeof (struct Lisp_Symbol));
symbols_consed++;
- gcstat.total_free_symbols--;
return val;
}
@@ -5503,7 +5493,7 @@ staticpro (Lisp_Object const *varaddress)
static void
allow_garbage_collection (intmax_t consing)
{
- consing_until_gc = consing - (INTMAX_MAX - consing_until_gc);
+ consing_until_gc = consing - (HI_THRESHOLD - consing_until_gc);
garbage_collection_inhibited--;
}
@@ -5513,7 +5503,7 @@ inhibit_garbage_collection (void)
ptrdiff_t count = SPECPDL_INDEX ();
record_unwind_protect_intmax (allow_garbage_collection, consing_until_gc);
garbage_collection_inhibited++;
- consing_until_gc = INTMAX_MAX;
+ consing_until_gc = HI_THRESHOLD;
return count;
}
@@ -5723,7 +5713,7 @@ visit_buffer_root (struct gc_root_visitor visitor,
There are other GC roots of course, but these roots are dynamic
runtime data structures that pdump doesn't care about and so we can
- continue to mark those directly in garbage_collect_1. */
+ continue to mark those directly in garbage_collect. */
void
visit_static_gc_roots (struct gc_root_visitor visitor)
{
@@ -5753,8 +5743,7 @@ mark_object_root_visitor (Lisp_Object const *root_ptr,
}
/* List of weak hash tables we found during marking the Lisp heap.
- Will be NULL on entry to garbage_collect_1 and after it
- returns. */
+ NULL on entry to garbage_collect and after it returns. */
static struct Lisp_Hash_Table *weak_hash_tables;
NO_INLINE /* For better stack traces */
@@ -5788,11 +5777,13 @@ mark_and_sweep_weak_table_contents (void)
}
}
-/* Return the number of bytes to cons between GCs, assuming
- gc-cons-threshold is THRESHOLD and gc-cons-percentage is
- PERCENTAGE. */
-static intmax_t
-consing_threshold (intmax_t threshold, Lisp_Object percentage)
+/* Return the number of bytes to cons between GCs, given THRESHOLD and
+ PERCENTAGE. When calculating a threshold based on PERCENTAGE,
+ assume SINCE_GC bytes have been allocated since the most recent GC.
+ The returned value is positive and no greater than HI_THRESHOLD. */
+static EMACS_INT
+consing_threshold (intmax_t threshold, Lisp_Object percentage,
+ intmax_t since_gc)
{
if (!NILP (Vmemory_full))
return memory_full_cons_threshold;
@@ -5802,42 +5793,33 @@ consing_threshold (intmax_t threshold, Lisp_Object percentage)
if (FLOATP (percentage))
{
double tot = (XFLOAT_DATA (percentage)
- * total_bytes_of_live_objects ());
+ * (total_bytes_of_live_objects () + since_gc));
if (threshold < tot)
{
- if (tot < INTMAX_MAX)
- threshold = tot;
+ if (tot < HI_THRESHOLD)
+ return tot;
else
- threshold = INTMAX_MAX;
+ return HI_THRESHOLD;
}
}
- return threshold;
+ return min (threshold, HI_THRESHOLD);
}
}
-/* Adjust consing_until_gc, assuming gc-cons-threshold is THRESHOLD and
- gc-cons-percentage is PERCENTAGE. */
-static Lisp_Object
+/* Adjust consing_until_gc and gc_threshold, given THRESHOLD and PERCENTAGE.
+ Return the updated consing_until_gc. */
+
+static EMACS_INT
bump_consing_until_gc (intmax_t threshold, Lisp_Object percentage)
{
- /* If consing_until_gc is negative leave it alone, since this prevents
- negative integer overflow and a GC would have been done soon anyway. */
- if (0 <= consing_until_gc)
- {
- threshold = consing_threshold (threshold, percentage);
- intmax_t sum;
- if (INT_ADD_WRAPV (consing_until_gc, threshold - gc_threshold, &sum))
- {
- /* Scale the threshold down so that consing_until_gc does
- not overflow. */
- sum = INTMAX_MAX;
- threshold = INTMAX_MAX - consing_until_gc + gc_threshold;
- }
- consing_until_gc = sum;
- gc_threshold = threshold;
- }
-
- return Qnil;
+ /* Guesstimate that half the bytes allocated since the most
+ recent GC are still in use. */
+ EMACS_INT since_gc = (gc_threshold - consing_until_gc) >> 1;
+ EMACS_INT new_gc_threshold = consing_threshold (threshold, percentage,
+ since_gc);
+ consing_until_gc += new_gc_threshold - gc_threshold;
+ gc_threshold = new_gc_threshold;
+ return consing_until_gc;
}
/* Watch changes to gc-cons-threshold. */
@@ -5848,7 +5830,8 @@ watch_gc_cons_threshold (Lisp_Object symbol, Lisp_Object newval,
intmax_t threshold;
if (! (INTEGERP (newval) && integer_to_intmax (newval, &threshold)))
return Qnil;
- return bump_consing_until_gc (threshold, Vgc_cons_percentage);
+ bump_consing_until_gc (threshold, Vgc_cons_percentage);
+ return Qnil;
}
/* Watch changes to gc-cons-percentage. */
@@ -5856,24 +5839,34 @@ static Lisp_Object
watch_gc_cons_percentage (Lisp_Object symbol, Lisp_Object newval,
Lisp_Object operation, Lisp_Object where)
{
- return bump_consing_until_gc (gc_cons_threshold, newval);
+ bump_consing_until_gc (gc_cons_threshold, newval);
+ return Qnil;
+}
+
+/* It may be time to collect garbage. Recalculate consing_until_gc,
+ since it might depend on current usage, and do the garbage
+ collection if the recalculation says so. */
+void
+maybe_garbage_collect (void)
+{
+ if (bump_consing_until_gc (gc_cons_threshold, Vgc_cons_percentage) < 0)
+ garbage_collect ();
}
/* Subroutine of Fgarbage_collect that does most of the work. */
-static bool
-garbage_collect_1 (struct gcstat *gcst)
+void
+garbage_collect (void)
{
struct buffer *nextb;
char stack_top_variable;
bool message_p;
ptrdiff_t count = SPECPDL_INDEX ();
struct timespec start;
- byte_ct tot_before = 0;
eassert (weak_hash_tables == NULL);
if (garbage_collection_inhibited)
- return false;
+ return;
/* Record this function, so it appears on the profiler's backtraces. */
record_in_backtrace (QAutomatic_GC, 0, 0);
@@ -5883,14 +5876,15 @@ garbage_collect_1 (struct gcstat *gcst)
FOR_EACH_BUFFER (nextb)
compact_buffer (nextb);
- if (profiler_memory_running)
- tot_before = total_bytes_of_live_objects ();
+ byte_ct tot_before = (profiler_memory_running
+ ? total_bytes_of_live_objects ()
+ : (byte_ct) -1);
start = current_timespec ();
/* In case user calls debug_print during GC,
don't let that cause a recursive GC. */
- consing_until_gc = INTMAX_MAX;
+ consing_until_gc = HI_THRESHOLD;
/* Save what's currently displayed in the echo area. Don't do that
if we are GC'ing because we've run out of memory, since
@@ -6002,7 +5996,7 @@ garbage_collect_1 (struct gcstat *gcst)
unblock_input ();
consing_until_gc = gc_threshold
- = consing_threshold (gc_cons_threshold, Vgc_cons_percentage);
+ = consing_threshold (gc_cons_threshold, Vgc_cons_percentage, 0);
if (garbage_collection_messages && NILP (Vmemory_full))
{
@@ -6014,8 +6008,6 @@ garbage_collect_1 (struct gcstat *gcst)
unbind_to (count, Qnil);
- *gcst = gcstat;
-
/* GC is complete: now we can run our finalizer callbacks. */
run_finalizers (&doomed_finalizers);
@@ -6029,29 +6021,21 @@ garbage_collect_1 (struct gcstat *gcst)
/* Accumulate statistics. */
if (FLOATP (Vgc_elapsed))
{
- struct timespec since_start = timespec_sub (current_timespec (), start);
- Vgc_elapsed = make_float (XFLOAT_DATA (Vgc_elapsed)
- + timespectod (since_start));
+ static struct timespec gc_elapsed;
+ gc_elapsed = timespec_add (gc_elapsed,
+ timespec_sub (current_timespec (), start));
+ Vgc_elapsed = make_float (timespectod (gc_elapsed));
}
gcs_done++;
/* Collect profiling data. */
- if (profiler_memory_running)
+ if (tot_before != (byte_ct) -1)
{
byte_ct tot_after = total_bytes_of_live_objects ();
- byte_ct swept = tot_before <= tot_after ? 0 : tot_before - tot_after;
- malloc_probe (min (swept, SIZE_MAX));
+ if (tot_after < tot_before)
+ malloc_probe (min (tot_before - tot_after, SIZE_MAX));
}
-
- return true;
-}
-
-void
-garbage_collect (void)
-{
- struct gcstat gcst;
- garbage_collect_1 (&gcst);
}
DEFUN ("garbage-collect", Fgarbage_collect, Sgarbage_collect, 0, 0, "",
@@ -6071,10 +6055,12 @@ returns nil, because real GC can't be done.
See Info node `(elisp)Garbage Collection'. */)
(void)
{
- struct gcstat gcst;
- if (!garbage_collect_1 (&gcst))
+ if (garbage_collection_inhibited)
return Qnil;
+ garbage_collect ();
+ struct gcstat gcst = gcstat;
+
Lisp_Object total[] = {
list4 (Qconses, make_fixnum (sizeof (struct Lisp_Cons)),
make_int (gcst.total_conses),