Subject: Re: bug#25214: 26.0.50; Interacting with user from threads other than the primary

From: Clément Pit--Claudel <[email-masked]>
Date: Sun, 18 Dec 2016 23:31:28 -0500

Hi Eli,

> Thoughts?

I don't have that much concurrency experience, but this sounds like an issue that other languages have had to deal with in the context of GUIs.  In Microsoft's Windows Forms, for example, applications start with a single thread (the UI thread) running a message pump, and other threads that want to interact with the user may do so only by posting a message (using Control.Invoke) to the UI thread.

If I understand your description of the problem correctly, what happens is essentially that the main thread and the background thread are competing for keyboard access.  If so, could the solution be to route all UI requests through the main thread?  Concretely, this would mean that in the context of a secondary thread, calling "read-from-minibuffer" really means pushing a message (tagged with a thread id) into a queue of "pending-reads-from-minibuffer", then repeatedly `thread-yield`ing until the UI thread has pushed the user's response into a map of "processed-reads-from-minibuffer".  The user's response would be collected by the main thread.

Alternatively, could we simply disallow this type of interaction until we have a good story about it? That would bring us closer to the way web workers work in Javascript (they need to post a message to interact with the UI/DOM).

Cheers,
Clément.

On 2016-12-16 10:17, Eli Zaretskii wrote:
> 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))
> 
> 
> 
>