1

While the following works:

(let* ((i (make-string-input-stream "foo bar baz"))
       (p (sb-ext:run-program "/bin/cat" '() 
              :input i :output *trace-output* :wait t)))
  (sb-ext:process-close p))

the code below does not - it will stop after writing "001":

(let* ((_1 (format t "001~%"))
       (p (sb-ext:run-program "/bin/cat" '() 
              :input :stream :output *trace-output* :wait t))
       (_2 (format t "010~s~%" p))
       (s (sb-ext:process-input p)))
  (declare (ignore _1 _2))
  (format s "foo bar baz~%")
  (finish-output s)
  (sb-ext:process-close p))

So it seems to silently leave execution in sb-ext:run-program.

This is with SBCL 1.3.6 on Ubuntu 16.04.1.

Any ideas? Thanks in advance, Frank

Frank Ruben
  • 105
  • 7
  • 2
    `:WAIT T` means wait until the process exits. – jkiiski Aug 08 '16 at 06:48
  • The `:wait t` is there in both cases, also in the working case - so this cannot be the explanation. Also the issue is not that `sb:ext-run-program` is leaving its own execution, the whole `let` blog is left without running displaying any log output - and this would not be expected even with `:wait nil`. Thanks anyway, – Frank Ruben Aug 09 '16 at 05:56
  • 1
    In the first case you're giving it a string input stream. `cat` reads the input until it EOFs and then returns. In the second example you're not writing anything to the stream, so there will not be EOF and `cat` will keep waiting forever for more input. – jkiiski Aug 09 '16 at 07:25

2 Answers2

3

As I mentioned in the comments, the problem is the :WAIT T argument. It causes the call to SB-EXT:RUN-PROGRAM to not return until the child process exits.

In the first example you passed a string input stream to the child process. cat will read input from the stream, and when the input ends there will be a End of File, so cat exits. In the second example there is no input available for the program, so it's effectively an infinite loop (just like if you run cat on the command line, and don't give any input to it; it will never exit).

The solution is to use :WAIT NIL. You will also have to close the input stream with CLOSE, because otherwise there will be no EOF and cat keeps listening for more input. You'll also want to use SB-EXT:PROCESS-WAIT after closing the stream to wait for cat to exit itself.

(let* ((p (sb-ext:run-program "/bin/cat" '() 
                              :input :stream
                              :output *standard-output*
                              :wait nil))
       (s (sb-ext:process-input p)))
  (format s "foo bar baz~%")
  (finish-output s)
  (close s)
  (sb-ext:process-wait p)
  (sb-ext:process-close p))

I'm not sure why you used *TRACE-OUTPUT* for the child output, so I changed it to *STANDARD-OUTPUT*.

Also, using FORMAT for debugging like that is kind of ugly. Common Lisp provides actual debugging tools. In this case you could use STEP:

(step (let* ((p (sb-ext:run-program "/bin/cat" '() 
                                    :input :stream
                                    :output *standard-output*
                                    :wait nil))
             (s (sb-ext:process-input p)))
        (format s "foo bar baz~%")
        (finish-output s)
        (close s)
        (sb-ext:process-wait p)
        (sb-ext:process-close p)))

This will put you in the debugger, showing the call being evaluated next. You can invoke the STEP-NEXT-restart to continue to the next call.

jkiiski
  • 8,206
  • 2
  • 28
  • 44
  • OK - thanks again. I get your points - and will concentrate on the main issue now. What I don't understand here: also for the second example I do provide input for the process and I finish/flush that input stream - both actions done through stream `s`. I also got confused, since Emacs/Slime don't block the waiting process, the REPL is still responsive. When testing in the SBCL REPL, the waiting process really blocks. Anyway I tried the `wait nil`as you proposed and ... it worked ;) So I still don't get it completely, but I have a solution - thanks again. – Frank Ruben Aug 09 '16 at 18:49
  • @FrankRuben Emacs/Slime makes use of different threads. The REPL uses its own thread, whereas others tasks are executed using "worker" threads. Use slime-list-threads to list them. When you run SBCL on your own, you are only interacting with a single thread. You could use sb-thread:make-thread to start another one and the result would be similar to what you observe within Emacs. – coredump Aug 10 '16 at 23:40
0

This is what works, as suggested by jkiiski:

(let* ((p (sb-ext:run-program "/bin/cat" '() 
                              :input :stream 
                              :output *standard-output* 
                              :wait nil))
       (s (sb-ext:process-input p)))
  (format s "foo bar baz~%")
  (finish-output s)
  (sb-ext:process-wait p)
  (sb-ext:process-close p))
Community
  • 1
  • 1
Frank Ruben
  • 105
  • 7