Subject: bug#47334: 27.1; Incompatibility between daemon, desktop and highlight-changes-mode

From: Gilles <[email-masked]>
Date: Sun, 28 Mar 2021 03:06:25 +0200

Hello,

I've done some further debugging (analysis below) and can reproduce
the mute Emacs problem just by throwing an error from
`after-init-hook'. I filed a separate bug for that:
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=47439

The incompatibility between highlight-changes-mode, desktop and daemon
startup exists regardless of the daemon-not-starting bug since
highlight-changes-mode is not restored correctly. The
daemon-not-starting bug just made it harder to figure out what was
going on.

----------------------------------------------------------------

I built emacs-27.2 from source with debugging enabled on Linux (Ubuntu
20.04) / x84_64. I observe the same symptoms. Here's a backtrace. I
used src/.gdbinit and do get a Lisp backtrace if I attach to an Emacs
process with a non-empty Lisp call stack.

----------------------------------------------------------------
Breakpoint 1 at 0x55d190f50dbd: file emacs.c, line 379.
Breakpoint 2 at 0x55d190f026f6: file xterm.c, line 10145.
(gdb) bt
#0  0x00007fdb9e854246 in __pselect
    (nfds=8, readfds=0x7fff6656d800, writefds=0x7fff6656d880,
exceptfds=0x0, timeout=<optimized out>, sigmask=<optimized out>)
    at ../sysdeps/unix/sysv/linux/pselect.c:48
#1  0x000055d19120dfbe in really_call_select (arg=0x7fff6656d6f0)
    at thread.c:586
#2  0x000055d1910740ff in flush_stack_call_func
    (func=0x55d19120def3 <really_call_select>, arg=0x7fff6656d6f0)
    at alloc.c:4951
#3  0x000055d19120e0ba in thread_select
    (func=0x7fdb9e854180 <__pselect>, max_fds=8, rfds=0x7fff6656d800,
wfds=0x7fff6656d880, efds=0x0, timeout=0x7fff6656de30, sigmask=0x0) at
thread.c:616
#4  0x000055d191276770 in xg_select
    (fds_lim=8, rfds=0x7fff6656dea0, wfds=0x7fff6656df20, efds=0x0,
timeout=0x7fff6656de30, sigmask=0x0) at xgselect.c:117
#5  0x000055d19117fc7c in wait_reading_process_output
    (time_limit=0, nsecs=0, read_kbd=-1, do_display=true,
wait_for_cell=XIL(0), wait_proc=0x0, just_wait_proc=0) at
process.c:5572
#6  0x000055d190f67260 in kbd_buffer_get_event
    (kbp=0x7fff6656e200, used_mouse_menu=0x7fff6656e805, end_time=0x0)
    at keyboard.c:3866
#7  0x000055d190f6189d in read_event_from_main_queue
    (end_time=0x0, local_getcjmp=0x7fff6656e610,
used_mouse_menu=0x7fff6656e805) at keyboard.c:2156
#8  0x000055d190f61cb3 in read_decoded_event_from_main_queue
    (end_time=0x0, local_getcjmp=0x7fff6656e610, prev_event=XIL(0),
used_mouse_menu=0x7fff6656e805) at keyboard.c:2220
#9  0x000055d190f64030 in read_char
    (commandflag=1, map=XIL(0x55d19260ac23), prev_event=XIL(0),
used_mouse_menu=0x7fff6656e805, end_time=0x0) at keyboard.c:2830
#10 0x000055d190f7673b in read_key_sequence
    (keybuf=0x7fff6656e9f0, prompt=XIL(0), dont_downcase_last=false,
can_return_switch_frame=true, fix_current_buffer=true,
prevent_redisplay=false)
    at keyboard.c:9554
#11 0x000055d190f5ef70 in command_loop_1 () at keyboard.c:1350
#12 0x000055d1910d6339 in internal_condition_case
    (bfun=0x55d190f5ead2 <command_loop_1>, handlers=XIL(0x90),
hfun=0x55d190f5e082 <cmd_error>) at eval.c:1356
#13 0x000055d190f5e693 in command_loop_2 (ignore=XIL(0)) at keyboard.c:1091
#14 0x000055d1910d571c in internal_catch
    (tag=XIL(0xcc60), func=0x55d190f5e662 <command_loop_2>, arg=XIL(0))
    at eval.c:1117
#15 0x000055d190f5e62d in command_loop () at keyboard.c:1070
#16 0x000055d190f5db49 in recursive_edit_1 () at keyboard.c:714
#17 0x000055d190f5dd49 in Frecursive_edit () at keyboard.c:786
#18 0x000055d190f5375d in main (argc=3, argv=0x7fff6656ee78) at emacs.c:2067
(gdb) p *readfds
$1 = {
  fds_bits = {128, 0 <repeats 15 times>}
}
(gdb) p *writefds
$2 = {
  fds_bits = {0 <repeats 16 times>}
}
----------------------------------------------------------------

It appears that Emacs is waiting for input from an event object. I
have no idea what that event object is about, or why the daemon would
be waiting for input before starting the server.

----------------------------------------------------------------
$ ls -log /proc/1372320/fd
total 0
lrwx------ 1 64 Mar 28 00:25 0 -> /dev/pts/6
lrwx------ 1 64 Mar 28 00:25 1 -> /dev/pts/6
lrwx------ 1 64 Mar 28 00:25 2 -> /dev/pts/6
lrwx------ 1 64 Mar 28 00:25 3 -> 'anon_inode:[timerfd]'
l-wx------ 1 64 Mar 28 00:25 4 -> 'pipe:[510996821]'
lr-x------ 1 64 Mar 28 00:25 5 -> /var/lib/sss/mc/passwd
lrwx------ 1 64 Mar 28 00:25 6 -> 'anon_inode:[eventfd]'
lrwx------ 1 64 Mar 28 00:25 7 -> 'anon_inode:[eventfd]'
----------------------------------------------------------------

I added some crude tracing in the init file:
----------------------------------------------------------------
(shell-command "echo >>trace .emacs start")
(eval-after-load 'hilit-chg '(shell-command "echo >>trace hilit-chg loaded"))
(defun my-highlight-changes-mode-hook ()
  (shell-command (format "echo >>trace 'highlight-changes-mode=%s in
%s'" highlight-changes-mode (buffer-name))))
(add-hook 'highlight-changes-mode-hook 'my-highlight-changes-mode-hook)

(setq desktop-restore-frames nil)
(desktop-save-mode)
(require 'hilit-chg)

(shell-command "echo >>trace .emacs end")
----------------------------------------------------------------

The resulting trace is:
----------------------------------------------------------------
.emacs start
hilit-chg loaded
.emacs end
----------------------------------------------------------------

My understanding is that the entry from desktop.el to
`after-init-hook' restores the buffer in highlight-changes-mode, which
calls (x-display-grayscale-p), which errors out because the daemon
doesn't have an X display. And indeed I can reproduce the problem
without session restoration or hilit-chg.el: the problem appears if
after-init-hook throws an error.

Turning on highlight-changes-mode should not depend on the
capabilities of the current terminal or display, since this can change
as new frames are attached.

-- Gilles