mit-scheme-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[MIT-Scheme-devel] race in subprocess status and exit reason queries


From: Taylor R Campbell
Subject: [MIT-Scheme-devel] race in subprocess status and exit reason queries
Date: Sat, 9 Oct 2010 08:01:39 +0000
User-agent: IMAIL/1.21; Edwin/3.116; MIT-Scheme/9.0.1

Suppose I want an operation (SUBPROCESS-WAIT* <subprocess>) that
behaves as it seems that this definition should behave:

(define (subprocess-wait* subprocess)
  (let* ((status (subprocess-wait subprocess))
         (exit-reason (subprocess-exit-reason subprocess)))
    (cons status exit-reason)))

More precisely, SUBPROCESS-WAIT* (1) waits until the subprocess's
status is not RUNNING, and then (2) returns both the status and the
exit reason associated with that status.  This idiom doesn't work as
written, though.

Consider a process that stops, is continued, and then exits normally.
But before it exits, another Scheme thread reads its status, thereby
updating its exit reason.  Then although we may see its status as
STOPPED when calling SUBPROCESS-WAIT*, SUBPROCESS-EXIT-REASON may
yield its return value -- not the signal by which it was stopped.

(let ((subprocess
       (start-pipe-subprocess "/bin/sh"
                              '#("/bin/sh" "-c" "kill -STOP $$; exit 1")
                              '#())))
  (sleep-current-thread 1000)
  (let ((status (subprocess-wait subprocess)))
    (pp (subprocess-id subprocess))
    ;; ***
    (create-thread #f
      (lambda ()
        (sleep-current-thread 5000)
        (subprocess-status subprocess)))
    (sleep-current-thread 10000)
    (cons status (subprocess-exit-reason subprocess))))

If, at the line marked `;; ***', I open up a shell and `kill -CONT'
the subprocess (within the five second window), this reliably yields
(STOPPED . 1), but in this case, SUBPROCESS-WAIT* should return only
either (EXITED . 1) or (STOPPED . n), where n is the value of SIGSTOP
on your favourite Unix system (usually not 1).

The closest I can get with the built-in operations is the following,
but it has the problem described in the comment marked `;; ***'.

(define (subprocess-wait* subprocess)
  (let wait-loop ()
    (subprocess-wait subprocess)
    (let tick-loop ((tick (subprocess-status-tick subprocess)))
      (let ((status (subprocess-status subprocess)))
        (case status
          ((RUNNING)
           ;; *** We know we have lost information: SUBPROCESS-WAIT
           ;; told us, by returning, that the process had a status
           ;; other than RUNNING, together with an exit reason which we
           ;; can no longer know.
           (wait-loop))
          ((EXITED SIGNALLED)
           ;; No race condition for these cases: the process's exit
           ;; reason will never change.
           (cons status (subprocess-exit-reason subprocess)))
          ((STOPPED)
           (let ((exit-reason (subprocess-exit-reason subprocess)))
             (let ((tick* (subprocess-status-tick subprocess)))
               (if (eq? tick* tick)
                   (cons 'STOPPED exit-reason)
                   (tick-loop tick*)))))
          (else
           (error "Invalid subprocess status:" status)))))))

One straightforward answer is `don't operate on subprocesses in
another thread', but I don't think that's a very good answer,
particularly in the context surrounding this problem.  However, that
context would require another mail as long as this one.

Perhaps SUBPROCESS-WAIT* should be built-in to runtime/process.scm,
and hooked into the logic inside %SUBPROCESS-STATUS.  I'd also like it
if SIGNAL-IO-THREAD-EVENTS delivered all I/O thread events registered
for process status changes, rather than delivering only the first one
-- especially since one cannot register an I/O thread event for a
particular process's status changes.  But maybe it would be better
just to rewrite the I/O thread event registry altogether.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]