Subject: 26.0.50; Interacting with user from threads other than the primary

From: Eli Zaretskii <[email-masked]>
Date: Fri, 16 Dec 2016 17:17:02 +0200

Here's some fun with threads:

 emacs -Q

Evaluate:

  (defun my-question ()
    (let (use-dialog-box) ;; in case this is a GUI frame
      (if (y-or-n-p "Yes or no? ")
	  (message "You said YES")
	(message "You said NO"))))

Evaluate:

  (my-question)

You get a question asked in the minibuffer, answer it with Y or N, and
get the corresponding echo.  All's well.

Now evaluate this:

  (make-thread #'my-question "question")

You again get the question asked in the minibuffer, but typing
anything at the prompt causes Emacs to complain that whatever you
typed is "undefined".  Your only fire escape is to close this session
with some mouse gesture, or kill it.

What happens here is that, by the time the new thread runs, the main
(a.k.a. "primary") thread is already idle, i.e. it already called
read_char, which called wait_reading_process_output, which called
thread_select.  (In fact, it's _because_ the primary thread called
thread_select that the new thread was allowed to run at all, since it
has to acquire the global lock for that, and that is only possible
when the running thread releases the lock inside thread_select.)  Now,
when wait_reading_process_output calls thread_select, it prepares the
mask for file descriptors it will wait for to become ready for
reading, in this case there's only one descriptor: the keyboard
descriptor.  So the primary thread is waiting for input from the
keyboard.

Now the new thread starts running and eventually calls y-or-n-p, which
calls read_char, which calls wait_reading_process_output.  But when
this call prepares the mask for the thread_select call, it skips the
keyboard descriptor, because we only allow a single thread to wait on
each descriptor, and the keyboard descriptor is already watched by the
primary thread.  So the new thread ends up not waiting on the keyboard
input descriptor.

The thread_select call then switches back to the primary thread, and
the primary thread receives the Y or N character you type.  But it
doesn't know what to do with it, so it becomes confused.

IOW, user interaction from non-primary threads is currently inherently
unsafe.

And then, of course, there's the use case where two threads ask the
user something via the minibuffer.

Thoughts?

In GNU Emacs 26.0.50.118 (i686-pc-mingw32)
 of 2016-12-16 built on HOME-C4E4A596F7
Repository revision: fb2fdb1435d2520c1cbf2a3d6a53128512a38458
Windowing system distributor 'Microsoft Corp.', version 5.1.2600
Recent messages:
For information about GNU Emacs and the GNU system, type C-h C-a.

Configured using:
 'configure --prefix=/d/usr --enable-checking=yes,glyphs --with-wide-int
 --with-modules 'CFLAGS=-O0 -gdwarf-4 -g3''

Configured features:
XPM JPEG TIFF GIF PNG RSVG SOUND NOTIFY ACL GNUTLS LIBXML2 ZLIB
TOOLKIT_SCROLL_BARS MODULES

Important settings:
  value of $LANG: ENU
  locale-coding-system: cp1255

Major mode: Lisp Interaction

Minor modes in effect:
  tooltip-mode: t
  global-eldoc-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  tool-bar-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t
  line-number-mode: t
  transient-mark-mode: t

Load-path shadows:
None found.

Features:
(shadow sort mail-extr emacsbug message subr-x puny seq byte-opt gv
bytecomp byte-compile cl-extra help-mode cconv cl-loaddefs pcase cl-lib
dired dired-loaddefs format-spec rfc822 mml easymenu mml-sec
password-cache epa derived epg epg-config gnus-util rmail rmail-loaddefs
mm-decode mm-bodies mm-encode mail-parse rfc2231 mailabbrev gmm-utils
mailheader sendmail rfc2047 rfc2045 ietf-drums mm-util mail-prsvr
mail-utils time-date mule-util tooltip eldoc electric uniquify
ediff-hook vc-hooks lisp-float-type mwheel dos-w32 ls-lisp disp-table
term/w32-win w32-win w32-vars term/common-win tool-bar dnd fontset image
regexp-opt fringe tabulated-list replace newcomment text-mode elisp-mode
lisp-mode prog-mode register page menu-bar rfn-eshadow isearch timer
select scroll-bar mouse jit-lock font-lock syntax facemenu font-core
term/tty-colors frame 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 charscript case-table epa-hook jka-cmpr-hook help
simple abbrev obarray minibuffer cl-preloaded nadvice loaddefs button
faces cus-face macroexp files text-properties overlay sha1 md5 base64
format env code-pages mule custom widget hashtable-print-readable
backquote w32notify w32 multi-tty make-network-process emacs)

Memory information:
((conses 16 102990 12105)
 (symbols 56 21101 0)
 (miscs 48 36 129)
 (strings 16 21030 4737)
 (string-bytes 1 593607)
 (vectors 16 14398)
 (vector-slots 8 444812 4412)
 (floats 8 179 129)
 (intervals 40 269 104)
 (buffers 864 11))