aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorStefan Monnier2023-12-25 22:40:02 -0500
committerStefan Monnier2025-07-15 13:49:29 -0400
commitc3f3fe136cefc4c3aed960b175053e7c048aa979 (patch)
tree0ed24837d09d9709f1bd43fba97d1d7bea14ca1a /src
parent4c9b37660771776563cf79bb3a2b6e692aa05ca2 (diff)
downloademacs-c3f3fe136cefc4c3aed960b175053e7c048aa979.tar.gz
emacs-c3f3fe136cefc4c3aed960b175053e7c048aa979.zip
Add `redisplay_counter` to catch nested redisplays and abort outer one
The redisplay code is not re-entrant. To allow running ELisp code from within redisplay, we have some hacks (e.g. `inhibit-redisplay`) that try to avoid the resulting breakage. This commit adds another one of those hacks, which tries to get closer to the core of the problem, thereby making it "safe" to override `inhibit-redisplay`, e.g. to debug jit-lock code. * src/dispextern.h (redisplay_counter): Declare. * src/xdisp.c (redisplay_counter): Define. (redisplay_internal) Increment it. (dsafe__call): Use it, in case `inhibit-redisplay` is overridden. * src/eval.c (call_debugger): Use it as well to refine the test we already had.
Diffstat (limited to 'src')
-rw-r--r--src/dispextern.h1
-rw-r--r--src/eval.c6
-rw-r--r--src/xdisp.c22
3 files changed, 26 insertions, 3 deletions
diff --git a/src/dispextern.h b/src/dispextern.h
index bd48005b83f..19ab104d2e6 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -3559,6 +3559,7 @@ int partial_line_height (struct it *it_origin);
3559bool in_display_vector_p (struct it *); 3559bool in_display_vector_p (struct it *);
3560int frame_mode_line_height (struct frame *); 3560int frame_mode_line_height (struct frame *);
3561extern bool redisplaying_p; 3561extern bool redisplaying_p;
3562extern unsigned int redisplay_counter;
3562extern bool display_working_on_window_p; 3563extern bool display_working_on_window_p;
3563extern void unwind_display_working_on_window (void); 3564extern void unwind_display_working_on_window (void);
3564extern bool help_echo_showing_p; 3565extern bool help_echo_showing_p;
diff --git a/src/eval.c b/src/eval.c
index 5e2b5bff796..204adb62472 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -302,6 +302,7 @@ call_debugger (Lisp_Object arg)
302 /* Resetting redisplaying_p to 0 makes sure that debug output is 302 /* Resetting redisplaying_p to 0 makes sure that debug output is
303 displayed if the debugger is invoked during redisplay. */ 303 displayed if the debugger is invoked during redisplay. */
304 debug_while_redisplaying = redisplaying_p; 304 debug_while_redisplaying = redisplaying_p;
305 int redisplay_counter_before = redisplay_counter;
305 redisplaying_p = 0; 306 redisplaying_p = 0;
306 specbind (Qdebugger_may_continue, 307 specbind (Qdebugger_may_continue,
307 debug_while_redisplaying ? Qnil : Qt); 308 debug_while_redisplaying ? Qnil : Qt);
@@ -323,9 +324,10 @@ call_debugger (Lisp_Object arg)
323 /* Interrupting redisplay and resuming it later is not safe under 324 /* Interrupting redisplay and resuming it later is not safe under
324 all circumstances. So, when the debugger returns, abort the 325 all circumstances. So, when the debugger returns, abort the
325 interrupted redisplay by going back to the top-level. */ 326 interrupted redisplay by going back to the top-level. */
326 /* FIXME: Move this to the redisplay code? */
327 if (debug_while_redisplaying 327 if (debug_while_redisplaying
328 && !EQ (Vdebugger, Qdebug_early)) 328 && redisplay_counter_before != redisplay_counter)
329 /* FIXME: Rather than jump all the way to `top-level`
330 we should exit only the current redisplay. */
329 Ftop_level (); 331 Ftop_level ();
330 332
331 return unbind_to (count, val); 333 return unbind_to (count, val);
diff --git a/src/xdisp.c b/src/xdisp.c
index 992d162f2e3..8eec2757226 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -3054,6 +3054,17 @@ hscrolling_current_line_p (struct window *w)
3054 Lisp form evaluation 3054 Lisp form evaluation
3055 ***********************************************************************/ 3055 ***********************************************************************/
3056 3056
3057/* The redisplay is not re-entrant. But we can run ELisp code during
3058 redisplay, which in turn can trigger redisplay.
3059 We try to make this inner redisplay work correctly, but it messes up
3060 the state of the outer redisplay, so when we return to this outer
3061 redisplay, we need to abort it.
3062 To dect this case, we keep a counter that identifies every call to the
3063 redisplay, so we can detect when a nested redisplay happened by the
3064 fact that the counter has changed. */
3065
3066unsigned int redisplay_counter = 0;
3067
3057/* Error handler for dsafe_eval and dsafe_call. */ 3068/* Error handler for dsafe_eval and dsafe_call. */
3058 3069
3059static Lisp_Object 3070static Lisp_Object
@@ -3085,10 +3096,17 @@ dsafe__call (bool inhibit_quit, Lisp_Object (f) (ptrdiff_t, Lisp_Object *),
3085 specbind (Qinhibit_redisplay, Qt); 3096 specbind (Qinhibit_redisplay, Qt);
3086 if (inhibit_quit) 3097 if (inhibit_quit)
3087 specbind (Qinhibit_quit, Qt); 3098 specbind (Qinhibit_quit, Qt);
3099 int redisplay_counter_before = redisplay_counter;
3088 /* Use Qt to ensure debugger does not run, 3100 /* Use Qt to ensure debugger does not run,
3089 so there is no possibility of wanting to redisplay. */ 3101 to reduce the risk of wanting to redisplay. */
3090 val = internal_condition_case_n (f, nargs, args, Qt, 3102 val = internal_condition_case_n (f, nargs, args, Qt,
3091 dsafe_eval_handler); 3103 dsafe_eval_handler);
3104 if (redisplay_counter_before != redisplay_counter)
3105 /* A nested redisplay happened, abort this one! */
3106 /* FIXME: Rather than jump all the way to `top-level`
3107 we should exit only the current redisplay. */
3108 Ftop_level ();
3109
3092 val = unbind_to (count, val); 3110 val = unbind_to (count, val);
3093 } 3111 }
3094 3112
@@ -17109,6 +17127,8 @@ redisplay_internal (void)
17109 bool polling_stopped_here = false; 17127 bool polling_stopped_here = false;
17110 Lisp_Object tail, frame; 17128 Lisp_Object tail, frame;
17111 17129
17130 redisplay_counter++;
17131
17112 /* Set a limit to the number of retries we perform due to horizontal 17132 /* Set a limit to the number of retries we perform due to horizontal
17113 scrolling, this avoids getting stuck in an uninterruptible 17133 scrolling, this avoids getting stuck in an uninterruptible
17114 infinite loop (Bug #24633). */ 17134 infinite loop (Bug #24633). */