Skip to content

Sleeping process not reported as sleeping

Turning my email into an issue

I’m trying to find out how different lisp compilers handle external processes(*). As a part of that, I’ve created a short piece of code that launches an external process, sends it SIGSTOP, asks its about its well-being, and allows its to go about its business again by sending SIGCONT.

I would expect that ext:external-process-status consequently reports

  1. :running before SIGSTOP is sent
  2. then :stopped
  3. then :running or :resumed again once SIGCONT was received
  4. and finally :exited.

Indeed, that is what ps would do if you ran it on the command line (so I’ve added that to the example for comparison). Here’s the output of my script:

internal status: RUNNING (expected: running)
internal status: RUNNING (expected: stopped)
internal status: RUNNING (expected: running/resumed)
internal status: EXITED (expected: exited)

My expectations aren’t met in (2): Even though according to the ecl manual the status of a process can be :stopped, and the process is clearly stopped, it is not reported as such.

So I’d like to ask: Is this a bug or intentional?

(*) I’ve already sent very similar messages to openmcl-devel and sbcl-devel.

Here’s the updated script that I used (which now sends SIGSTOP/SIGCONT repeatedly:

;; CMUCL: make sure to use a version >21b (not yet released as of 2017-94-04)
;;        because of https://gitlab.common-lisp.net/cmucl/cmucl/issues/26
;; ECL: make sure to use a version >16.1.3 (not yet released as of 2017-09-04)
;;      because of https://gitlab.com/embeddable-common-lisp/ecl/issues/273
#+clozure (use-package :ccl)
#+sbcl (use-package :sb-ext)
#+cmu (use-package :ext)
#+mkcl (use-package :mk-ext)

#+clozure (setf (fdefinition 'process-output) #'external-process-output-stream)
#+ecl (setf (fdefinition 'process-output) #'ext:external-process-output)

(defun my-run-program (&rest rest)
  #+ecl (nth-value 2 (apply #'ext:run-program rest))
  #+mkcl (nth-value 1 (apply #'mk-ext:run-program rest))
  #-(or ecl mkcl) (apply #'run-program rest))

(defconstant +sigstop+
  #+clozure (symbol-value (read-from-string "#$SIGSTOP"))
  #+cmu unix:sigstop
  #+ecl ext:+sigstop+
  #+sbcl (progn (require :sb-posix)
                (symbol-value (find-symbol (symbol-name :sigstop)
                                           (find-package :sb-posix))))
  #-(or clozure cmu ecl sbcl) (or #+darwin 17 #-darwin 19)) ; FIXME
(defconstant +sigtstp+
  #+clozure (symbol-value (read-from-string "#$SIGTSTP"))
  #+cmu unix:sigtstp
  #+ecl ext:+sigtstp+
  #+sbcl (progn (require :sb-posix)
                (symbol-value (find-symbol (symbol-name :sigtstp)
                                           (find-package :sb-posix))))
  #-(or clozure cmu ecl sbcl) (or #+darwin 18 #-darwin 20)) ; FIXME
(defconstant +sigcont+
  #+clozure (symbol-value (read-from-string "#$SIGCONT"))
  #+cmu unix:sigcont
  #+ecl ext:+sigcont+
  #+sbcl (progn (require :sb-posix)
                (symbol-value (find-symbol (symbol-name :sigcont)
                                           (find-package :sb-posix))))
  #-(or clozure cmu ecl sbcl) (or #+darwin 19 #-darwin 18)) ; FIXME

(defun internal-status (process)
  #+clozure (external-process-status process)
  #+(or sbcl cmu) (process-status process)
  #+ecl (ext:external-process-status process)
  #+mkcl (mk-ext:process-status process))

(defun external-kill (pid signal)
  (my-run-program "/usr/bin/env" (list "kill"
                                    (format nil "-~a" signal)
                                    (format nil "~a" pid))))

(defun get-pid (process)
  #+clozure (ccl::external-process-pid process)
  #+ecl (ext:external-process-pid process)
  #+(or sbcl cmu) (process-pid process)
  #+mkcl (mkcl:process-id process))

(let* ((p (my-run-program "/bin/sleep" '("5") :wait nil))
       (pid (get-pid p)))
  (format t "internal status: ~a (expected: running)~%" (internal-status p))

  (external-kill pid +sigstop+)
  (sleep 1)
  (format t "internal status: ~a (expected: stopped)~%" (internal-status p))

  (external-kill pid +sigcont+)
  (sleep 1)
  (format t "internal status: ~a (expected: running/resumed)~%" (internal-status p))

  (external-kill pid +sigstop+)
  (sleep 1)
  (format t "internal status: ~a (expected: stopped)~%" (internal-status p))

  (external-kill pid +sigcont+)
  (sleep 1)
  (format t "internal status: ~a (expected: running/resumed)~%" (internal-status p))
  
  (sleep 5)
  (format t "internal status: ~a (expected: exited)~%" (internal-status p)))

Update:

The fact that sleeping processes are not reported at all appears to be down to the fact that the line

waitpid(ecl_to_fix(pid), &code_int, Null(wait)? WNOHANG : 0)

from ecl_waitpid defined at src/c/unixsys.d:242 does not contain WUNTRACED. The fix is not as easy as adding WUNTRACED, however. Such a change turns running/running/running/exited into running/stopped/stopped/stopped which is an improvement but still not correct.

Edited by Elias Pipping