Tags: patch

Hi, to reproduce this issue please save the following elisp to proc.el:
       
        (defvar p-stopped nil)
       
        (defun my-sentinel (proc str)
          (when (string-match "^\\(?:finished\n\\|exited abnormally\\|killed\n\\)"
                              str)
            (setq p-stopped t)))
       
        (defun wait-for-process-end (proc)
          (while (not p-stopped)
            (message "process status is: %s" (process-status proc))
            (accept-process-output proc 0.1 nil t)))
       
        (defun f ()
          (with-temp-buffer
          (inotify-add-watch "foo.txt" t #'ignore)
          (let ((proc (start-process "p" (current-buffer) "bash" "-c" "touch foo.txt")))
            (setq p-stopped nil)
            (set-process-sentinel proc 'my-sentinel)
            (wait-for-process-end proc))))

Please also create a file 'foo.txt'

        $ touch foo.txt

Now, invoke the following command

        $ emacs -Q --script proc.el --eval "(f)"
       
This command will never exit, and print "process status is: exit" forever.

The expected behavior is that, because process "p" is exited, its sentinel should be called, so 'wait-for-process-end' will complete and Emacs will exit.

After some investigation, I found that 'wait_reading_process_output' in process.c never calls the sentinel for the process because the pselect invocation in this code (src/process.c) always returns nonzero:
       
        if ((thread_select (pselect, max_desc + 1,
                              &Atemp,
                              (num_pending_connects > 0 ? &Ctemp : NULL),
                              NULL, &timeout, NULL)
                   <= 0))
                {
                  /* It's okay for us to do this and then continue with
                   the loop, since timeout has already been zeroed out.  */
                  clear_waiting_for_input ();
                  got_some_output = status_notify (NULL, wait_proc);
                  if (do_display) redisplay_preserve_echo_area (13);
                }

This pselect invocation always returns nonzero because the inotify fd for "foo.txt" is included in the pselect readfds and is readable. However, the inotify fd is never handled within 'wait_reading_process_output'.

If I understand this code correctly, the intent here is to prioritize handling ready fds before calling 'status_notify', which invokes sentinels. However, it seems inotify fds are not handled in 'wait_reading_process_output', so there is no reason to care about inotify fds here. I believe the correct fix is to filter for process fds in this pselect call. To accomplish this, the included patch filters for process fds in 'compute_input_wait_mask'. It appears to me that all usage of 'compute_input_wait_mask' only cares about process fds, so I believe this is a safe change.


In GNU Emacs 30.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version
 3.24.38, cairo version 1.17.8) of 2023-12-23 built on ostrich
Repository revision: 5c3ff1494b69bf45b99125f2423174222badfa43
Repository branch: master
Windowing system distributor 'The X.Org Foundation', version 11.0.12101011
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-m17n-flt --without-gconf
 --with-native-compilation=yes --with-xinput2 --with-x-toolkit=gtk3
 --without-xaw3d --with-sound=no --with-tree-sitter --without-gpm
 --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'
 LDFLAGS=-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now'