Subject: bug#61388: 28.2.50; Race condition heisenbug corrupts internal state when killing many emacsclients

From: [email-masked]
Date: Thu, 09 Feb 2023 14:06:41 +0000

Hi,

This bug is quite weird so please bear with me for a bit.

Context: I noticed that sometimes when I would switch working between
laptops (while SSH-ing into a single remote PC and launching emacsclient
-nw there), Emacs would sometimes enter into a weird state. Some
symptoms of this state were that fontification would not work properly,
delete-selection-mode was broken (typing replaced text but the typed
character became instantly selected again, causing the next typed
character to replace the previous one), and the scratch buffer was
cleared of its usual placeholder comment for some reason. I am not sure
but I think it may be something like command_loop_level being nonzero
when it should not be.

It did not happen with -Q, and manual attempts to reduce my init files
were not fruitful, so I've used an automatic tool (DustMite) to reduce
it. What it produced still had an odd number of parts, and I've been
unable to reduce it further. As far as I can tell, for this bug to
occur, the following needs to happen:

1. An Emacs Lisp file is byte-compiled.

2. The Emacs Lisp file mentioned above must have a "checkdoc-symbol-words"
file-local variable. The value is unimportant.

3. A globalized minor mode must be defined, and activated.

4. Some number of emacsclients are started and then killed
simultaneously.

Here is a full reproducer script:

----------------------------------------------------------------------------
#!/bin/bash
set -xeEuo pipefail

emacs --version

rm -rf bug
mkdir bug
cd bug

# Ensure Emacs and tmux run in our little sandbox and don't interfere with the system
export HOME=$PWD
export TMUX_TMPDIR=$PWD
export XDG_RUNTIME_DIR=$PWD

# Create init files which cause the bug
mkdir .emacs.d
cat <<'EOF' > .emacs.d/init.el
(require 'bytecomp)
(byte-compile-file ".emacs.d/localvars.el")
(define-minor-mode dummy-mode "")
(defun turn-on-dummy-mode () (dummy-mode))
(define-globalized-minor-mode global-dummy-mode
  dummy-mode turn-on-dummy-mode)
(global-dummy-mode)
EOF
cat <<'EOF' > .emacs.d/localvars.el
;; Local Variables:
;; checkdoc-symbol-words: ()
;; End:
EOF

emacs --daemon

# Start ten emacsclients (use tmux to allow them to run concurrently)
for n in $(seq 10) ; do
    tmux new-session -d -s emacsbug-$n 'emacsclient -nw'
done

sleep 1

# Kill all emacsclients simultaneously
tmux kill-server

sleep 1

# Test that the bug occurs. One symptom is that the scratch buffer is cleared.
# This prints 1 if the bug occurs and 146 if the bug did not occur.
emacsclient  --eval '(with-current-buffer (get-buffer "*scratch*") (point-max))'
----------------------------------------------------------------------------

This prints 1 for me (indicating the bug) with Emacs 28.2 when installed
from the Arch Linux repositories as well as when built from source from
the emacs-28 branch. But just in case, here is a Dockerfile which
reproduces the problem as well:

----------------------------------------------------------------------------
FROM docker.io/debian:unstable-20230208@sha256:b2f6fc00701e0d5ee6ef994891a6b4695865b51292fa2fb7a34882c15c7e4b4f
RUN apt-get update
RUN apt-get install -y emacs tmux
COPY repro.sh /
CMD /repro.sh
----------------------------------------------------------------------------

In GNU Emacs 28.2.50 (build 1, x86_64-pc-linux-gnu, X toolkit, Xaw3d scroll bars)
 of 2022-10-03 built on home.thecybershadow.net
Repository revision: 992611b10a2ef4621b5c936d80cf31644ca3653d
Repository branch: makepkg
System Description: Arch Linux

Configured using:
 'configure --prefix=/usr --sysconfdir=/etc --libexecdir=/usr/lib
 --localstatedir=/var --mandir=/usr/share/man --with-gameuser=:games
 --with-modules --without-libotf --without-m17n-flt --without-gconf
 --without-gsettings --enable-link-time-optimization --with-xinput2
 --with-native-compilation --with-x-toolkit=lucid --with-xft
 --with-xaw3d --without-cairo --with-sound=alsa
 --without-compress-install
 '--program-transform-name=s/\([ec]tags\)/\1.emacs/'
 'CFLAGS=-march=x86-64 -mtune=generic -O2 -pipe -fno-plt -fexceptions
 -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security
 -fstack-clash-protection -fcf-protection -fuse-ld=gold -fuse-ld=gold'
 LDFLAGS=-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now'

Configured features:
ACL DBUS FREETYPE GIF GLIB GMP GNUTLS GPM HARFBUZZ JPEG JSON LCMS2
LIBSYSTEMD LIBXML2 MODULES NATIVE_COMP NOTIFY INOTIFY PDUMPER PNG RSVG
SECCOMP SOUND THREADS TIFF TOOLKIT_SCROLL_BARS X11 XAW3D XDBE XFT XIM
XPM LUCID ZLIB

Important settings:
  value of $LC_TIME: en_DK.UTF-8
  value of $LANG: en_US.UTF-8
  locale-coding-system: utf-8-unix

Major mode: Shell-script

Minor modes in effect:
  hi-lock-mode: t
  diff-hl-margin-local-mode: t
  diff-hl-margin-mode: t
  highlight-thing-mode: t
  spell-fu-mode: t
  xterm-mouse-mode: t
  which-key-mode: t
  global-undo-tree-mode: t
  undo-tree-mode: t
  term-title-mode: t
  term-keys-mode: t
  term-cursor-color-mode: t
  smart-tabs-mode: t
  save-place-mode: t
  projectile-mode: t
  delete-selection-mode: t
  cua-mode: t
  helm-mode: t
  helm-minibuffer-history-mode: t
  recentf-mode: t
  async-bytecomp-package-mode: t
  sh-electric-here-document-mode: t
  global-diff-hl-mode: t
  diff-hl-mode: t
  global-company-mode: t
  company-mode: t
  global-git-commit-mode: t
  magit-auto-revert-mode: t
  shell-dirtrack-mode: t
  global-flycheck-mode: t
  flycheck-mode: t
  override-global-mode: t
  straight-use-package-mode: t
  straight-package-neutering-mode: t
  tooltip-mode: t
  global-eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t
  column-number-mode: t
  line-number-mode: t
  indent-tabs-mode: t
  transient-mark-mode: t

Load-path shadows:
/home/vladimir/.emacs.d/straight/build/cmake-mode/cmake-mode hides /usr/share/emacs/site-lisp/cmake-mode
/home/vladimir/work/extern/notmuch/emacs/notmuch hides /usr/share/emacs/site-lisp/notmuch
/home/vladimir/work/extern/notmuch/emacs/notmuch-wash hides /usr/share/emacs/site-lisp/notmuch-wash
/home/vladimir/work/extern/notmuch/emacs/notmuch-version hides /usr/share/emacs/site-lisp/notmuch-version
/home/vladimir/work/extern/notmuch/emacs/notmuch-tree hides /usr/share/emacs/site-lisp/notmuch-tree
/home/vladimir/work/extern/notmuch/emacs/notmuch-tag hides /usr/share/emacs/site-lisp/notmuch-tag
/home/vladimir/work/extern/notmuch/emacs/notmuch-show hides /usr/share/emacs/site-lisp/notmuch-show
/home/vladimir/work/extern/notmuch/emacs/notmuch-query hides /usr/share/emacs/site-lisp/notmuch-query
/home/vladimir/work/extern/notmuch/emacs/notmuch-print hides /usr/share/emacs/site-lisp/notmuch-print
/home/vladimir/work/extern/notmuch/emacs/notmuch-parser hides /usr/share/emacs/site-lisp/notmuch-parser
/home/vladimir/work/extern/notmuch/emacs/notmuch-mua hides /usr/share/emacs/site-lisp/notmuch-mua
/home/vladimir/work/extern/notmuch/emacs/notmuch-message hides /usr/share/emacs/site-lisp/notmuch-message
/home/vladimir/work/extern/notmuch/emacs/notmuch-maildir-fcc hides /usr/share/emacs/site-lisp/notmuch-maildir-fcc
/home/vladimir/work/extern/notmuch/emacs/notmuch-lib hides /usr/share/emacs/site-lisp/notmuch-lib
/home/vladimir/work/extern/notmuch/emacs/notmuch-jump hides /usr/share/emacs/site-lisp/notmuch-jump
/home/vladimir/work/extern/notmuch/emacs/notmuch-hello hides /usr/share/emacs/site-lisp/notmuch-hello
/home/vladimir/work/extern/notmuch/emacs/notmuch-draft hides /usr/share/emacs/site-lisp/notmuch-draft
/home/vladimir/work/extern/notmuch/emacs/notmuch-crypto hides /usr/share/emacs/site-lisp/notmuch-crypto
/home/vladimir/work/extern/notmuch/emacs/notmuch-compat hides /usr/share/emacs/site-lisp/notmuch-compat
/home/vladimir/work/extern/notmuch/emacs/notmuch-company hides /usr/share/emacs/site-lisp/notmuch-company
/home/vladimir/work/extern/notmuch/emacs/notmuch-address hides /usr/share/emacs/site-lisp/notmuch-address
/home/vladimir/work/extern/notmuch/emacs/coolj hides /usr/share/emacs/site-lisp/coolj
/home/vladimir/.emacs.d/straight/build/transient/transient hides /usr/share/emacs/28.2.50/lisp/transient
/home/vladimir/.emacs.d/straight/build/jsonrpc/jsonrpc hides /usr/share/emacs/28.2.50/lisp/jsonrpc
/home/vladimir/.emacs.d/straight/build/xref/xref hides /usr/share/emacs/28.2.50/lisp/progmodes/xref
/home/vladimir/.emacs.d/straight/build/project/project hides /usr/share/emacs/28.2.50/lisp/progmodes/project
/home/vladimir/.emacs.d/straight/build/flymake/flymake hides /usr/share/emacs/28.2.50/lisp/progmodes/flymake
/home/vladimir/.emacs.d/straight/build/let-alist/let-alist hides /usr/share/emacs/28.2.50/lisp/emacs-lisp/let-alist
/home/vladimir/.emacs.d/straight/build/eldoc/eldoc hides /usr/share/emacs/28.2.50/lisp/emacs-lisp/eldoc

Features:
(shadow sort mail-extr emacsbug sendmail cl-print checkdoc image-file
image-converter helm-command helm-elisp helm-eval edebug help-fns
radix-tree magit-patch magit-subtree magit-gitignore magit-ediff ediff
ediff-merg ediff-mult ediff-wind ediff-diff ediff-help ediff-init
ediff-util pc-bufsw conf-mode org-element avl-tree ol-eww ol-rmail
ol-mhe ol-irc ol-info ol-gnus nnselect gnus-search eieio-opt speedbar
ezimage dframe gnus-art mm-uu mml2015 mm-view mml-smime smime dig
gnus-sum gnus-group gnus-undo gnus-start gnus-dbus gnus-cloud nnimap
nnmail mail-source utf7 netrc nnoo gnus-spec gnus-int gnus-range
gnus-win ol-docview doc-view jka-compr ol-bibtex ol-bbdb ol-w3m ol-doi
org-link-doi org ob ob-tangle ob-ref ob-lob ob-table ob-exp org-macro
org-footnote org-src ob-comint org-pcomplete org-list org-faces
org-entities org-version ob-emacs-lisp ob-core ob-eval org-table
oc-basic bibtex ol org-keys oc org-compat org-macs org-loaddefs cal-menu
calendar cal-loaddefs epa-file ffap cs/second-sel dfix git-rebase
helm-git-grep goto-addr dired-k dired-aux mule-util move-text cus-start
cus-load visual-regexp misearch multi-isearch semantic/find
helm-semantic helm-imenu semantic/util-modes semantic/util semantic
semantic/tag semantic/lex semantic/fw mode-local cedet company-dcd ivy
ivy-faces ivy-overlay colir popwin flycheck-dmd-dub d-mode cc-langs
copyright hi-lock winner filecache vc-mtn vc-hg vc-bzr vc-src vc-sccs
vc-svn vc-cvs vc-rcs tramp-archive tramp-gvfs dbus helm-x-files
diff-hl-margin diff-hl-dired term/rxvt term/xterm xterm
highlight-defined highlight-thing spell-fu ispell cs/main
cs/private/main browse-at-remote vc-git cs/private/targets
cs/private/jstools js eglot array jsonrpc ert debug backtrace
flymake-proc flymake cs/private/prefs cs/persistent-selection
cs/packages yaml-mode yaml-mode-autoloads xt-mouse which-key
which-key-autoloads web-mode-autoloads visual-regexp-autoloads undo-tree
queue undo-tree-autoloads queue-autoloads typescript-mode cc-mode
cc-fonts cc-guess cc-menus cc-cmds cc-styles cc-align cc-engine cc-vars
cc-defs typescript-mode-autoloads terraform-mode hcl-mode
terraform-mode-autoloads hcl-mode-autoloads term-title
term-title-autoloads term-keys term-keys-autoloads term-cursor-color
term-cursor-color-autoloads spell-fu-autoloads smart-tabs-mode
smart-tabs-mode-autoloads smart-mode-line-dark-theme smart-mode-line
rich-minority smart-mode-line-autoloads rich-minority-autoloads desktop
frameset sdlang-mode sdlang-mode-autoloads saveplace savehist
rust-mode-autoloads tramp-cache tramp-sh python-black-autoloads
reformatter-autoloads projectile lisp-mnt grep ibuf-ext ibuffer
ibuffer-loaddefs projectile-autoloads pov-mode-autoloads cua-rect rect
php-mode-autoloads cs/keys find-file delsel cua-base pc-bufsw-autoloads
omnisharp-autoloads csharp-mode-autoloads auto-complete-autoloads noflet
cl-indent cl noflet-autoloads move-text-autoloads mediawiki-autoloads
lua-mode-autoloads lsp-mode lsp-protocol yasnippet spinner
network-stream lv inline ht lsp-mode-autoloads lv-autoloads
spinner-autoloads ht-autoloads js2-mode-autoloads jq-mode-autoloads
highlight-thing-autoloads highlight-symbol highlight-symbol-autoloads
highlight-defined-autoloads helm-mode helm-misc recentf tree-widget
helm-for-files helm-bookmark helm-adaptive helm-info magit-bookmark
bookmark pp helm-external helm-net helm-files image-dired image-mode
exif tramp tramp-loaddefs trampver tramp-integration files-x
tramp-compat ls-lisp helm-buffers helm-occur helm-tags helm-locate
helm-grep helm-regexp helm-utils helm-help helm-types helm
helm-global-bindings helm-easymenu helm-core async-bytecomp helm-source
helm-multi-match helm-lib async helm-autoloads popup-autoloads
helm-git-grep-autoloads helm-core-autoloads async-autoloads
haskell-mode-autoloads graphviz-dot-mode-autoloads go-mode-autoloads
align glsl-mode glsl-mode-autoloads gdscript-mode gdscript-hydra
gdscript-godot gdscript-project gdscript-history gdscript-comint
gdscript-debug bindat gdscript-format gdscript-comint-gdformat compile
gdscript-completion gdscript-fill-paragraph gdscript-imenu
gdscript-indent-and-nav gdscript-rx gdscript-utils gdscript-syntax
gdscript-keywords gdscript-docs eww xdg url-queue shr kinsoku svg xml
dom mm-url gnus nnheader wid-edit gdscript-customization
gdscript-mode-autoloads forge-list forge-commands forge-semi
forge-bitbucket buck forge-gogs gogs forge-gitea gtea forge-gitlab glab
forge-github ghub-graphql treepy gsexp ghub url-http url-gw nsm url-auth
let-alist gnutls forge-notify forge-revnote forge-pullreq forge-issue
forge-topic yaml parse-time iso8601 bug-reference forge-post
markdown-mode color thingatpt noutline outline forge-repo forge
forge-core forge-db closql emacsql-sqlite emacsql emacsql-compiler
forge-autoloads yaml-autoloads markdown-mode-autoloads ghub-autoloads
treepy-autoloads emacsql-sqlite-autoloads emacsql-autoloads
closql-autoloads face-remap eterm-256color f f-shortdoc shortdoc term
disp-table ehelp xterm-color eterm-256color-autoloads
xterm-color-autoloads cs/packages/5-elisp-mode eglot-autoloads
project-autoloads xref-autoloads flymake-autoloads eldoc-autoloads
jsonrpc-autoloads help-at-pt dockerfile-mode sh-script smie executable
dockerfile-mode-autoloads dired-k-autoloads diff-hl log-view vc-dir ewoc
vc vc-dispatcher diff-hl-autoloads flycheck-d-dscanner d-mode-autoloads
counsel-css-autoloads counsel-autoloads swiper-autoloads company-oddmuse
company-keywords company-etags etags fileloop generator xref project
company-gtags company-dabbrev-code company-dabbrev company-files
company-clang company-capf company-cmake company-semantic company-bbdb
company-tabnine unicode-escape s company-template company
company-tabnine-autoloads unicode-escape-autoloads names-autoloads
advice company-dcd-autoloads ivy-autoloads popwin-autoloads
yasnippet-autoloads flycheck-dmd-dub-autoloads company-autoloads
cmake-mode-autoloads cmake-ide-autoloads levenshtein-autoloads
cil-mode-autoloads cs/prefs pcase browse-at-remote-autoloads f-autoloads
s-autoloads xr xr-autoloads magit-extras magit-submodule magit-obsolete
magit-blame magit-stash magit-reflog magit-bisect magit-push magit-pull
magit-fetch magit-clone magit-remote magit-commit magit-sequence
magit-notes magit-worktree magit-tag magit-merge magit-branch
magit-reset magit-files magit-refs magit-status magit package browse-url
url url-proxy url-privacy url-expand url-methods url-history url-cookie
url-domsuf url-util mailcap url-handlers url-parse url-vars magit-repos
magit-apply magit-wip magit-log which-func imenu magit-diff smerge-mode
diff diff-mode git-commit log-edit message rmc puny dired dired-loaddefs
rfc822 mml mml-sec epa derived epg rfc6068 epg-config gnus-util rmail
rmail-loaddefs auth-source password-cache text-property-search time-date
mm-decode mm-bodies mm-encode mail-parse rfc2231 rfc2047 rfc2045 mm-util
ietf-drums mail-prsvr mailabbrev mail-utils gmm-utils mailheader
pcvs-util add-log magit-core magit-autorevert autorevert filenotify
magit-margin magit-transient magit-process with-editor shell pcomplete
comint ring server magit-mode transient comp comp-cstr warnings
magit-git magit-base magit-section format-spec crm eieio eieio-core
eieio-loaddefs compat-27 compat-26 compat compat-macs
magit-section-autoloads git-commit-autoloads with-editor-autoloads
transient-autoloads compat-autoloads flycheck ansi-color json map
find-func rx dash flycheck-autoloads let-alist-autoloads
pkg-info-autoloads epl-autoloads finder-inf dash-autoloads use-package
use-package-ensure use-package-delight use-package-diminish
use-package-bind-key bind-key easy-mmode use-package-core
use-package-autoloads info bind-key-autoloads straight-autoloads cl-seq
cl-extra help-mode seq byte-opt straight subr-x cl-macs gv bytecomp
byte-compile cconv cs/local cs/defs edmacro kmacro cl-loaddefs cl-lib
deeper-blue-theme iso-transl tooltip eldoc paren electric uniquify
ediff-hook vc-hooks lisp-float-type elisp-mode mwheel term/x-win x-win
term/common-win x-dnd tool-bar dnd fontset image regexp-opt fringe
tabulated-list replace newcomment text-mode lisp-mode prog-mode register
page tab-bar menu-bar rfn-eshadow isearch easymenu timer select
scroll-bar mouse jit-lock font-lock syntax font-core term/tty-colors
frame minibuffer cl-generic cham georgian utf-8-lang misc-lang
vietnamese tibetan thai tai-viet lao korean japanese eucjp-ms cp51932
hebrew greek romanian slovak czech european ethiopic indian cyrillic
chinese composite emoji-zwj charscript charprop case-table epa-hook
jka-cmpr-hook help simple abbrev obarray cl-preloaded nadvice button
loaddefs faces cus-face macroexp files window text-properties overlay
sha1 md5 base64 format env code-pages mule custom widget
hashtable-print-readable backquote threads dbusbind inotify lcms2
dynamic-setting font-render-setting x-toolkit x multi-tty
make-network-process native-compile emacs)

Memory information:
((conses 16 2397796 890083)
 (symbols 48 418473 20)
 (strings 32 733061 91725)
 (string-bytes 1 18239927)
 (vectors 16 198275)
 (vector-slots 8 5193809 1683234)
 (floats 8 861 9080)
 (intervals 56 58213 37817)
 (buffers 992 119))