Considering the following code:
(in-package #:cl-user)
(defconstant +block-size+ 5)
(let ((buffer (make-string +block-size+)))
(defun read->echo (in out)
;; Can I make use (MAKE-ECHO-STREAM IN OUT) with READ-N-BYTES ?
;; Is there any benefit to using READ-N-BYTES instead of
;; READ-SEQUENCE ?
(let ((bytes-read (sys:read-n-bytes in buffer 0 +block-size+ nil)))
(format t "buffer => ~S~%bytes-read => ~A~%" buffer bytes-read)
(write-string buffer out :start 0 :end bytes-read)
bytes-read)))
(defvar *shelf3*)
(defvar *expect-buffer*
(make-array '(0) :element-type 'base-char :fill-pointer 0
:adjustable t))
(defun shelf3--make ()
(when (and (boundp '*shelf3*) *shelf3*)
(error "*shelf3* is already bound: ~A" *shelf3*))
(setf *shelf3*
(ext:run-program "telnet" '("127.0.0.1")
:wait nil :pty t :input t :output t :error t)))
(defun shelf3--add-fd-handler ()
(sys:add-fd-handler (sys:fd-stream-fd (ext:process-pty *shelf3*))
:input
;; Why is the handler being called before one
;; even calls SYS:SERVE-EVENT ? I want to be
;; able to set this handler and then call
;; SYS:SERVE-EVENT to have the handler invoked.
(with-output-to-string (s *expect-buffer*)
(lambda (fd)
(declare (ignorable fd))
(read->echo (ext:process-pty *shelf3*) s)
(format t "*expect-buffer* => ~S~%"
*expect-buffer*)))))
(defun shelf3--read-5 ()
(dotimes (n 5)
(format t "~A" (read-char (process-pty *shelf3*))))
(format t "~%"))
I would expect to be able to use it as follows (hypothetical):
(shelf3--make) => #<process pid :RUNNING>
(shelf3--add-fd-handler) => #<Handler for INPUT on ...>
(sys:serve-event 1) => t
buffer => "Tryin"
bytes-read => 5
*expect-buffer* => "Tryin"
(sys:serve-event 1) => t
buffer => "g 127"
bytes-read => 5
*expect-buffer* => "Trying 127"
(sys:serve-event 1) => t
buffer => ".0.0."
bytes-read => 5
*expect-buffer* => "Trying 127.0.0."
(sys:serve-event 1) => t
buffer => "1..."
bytes-read => 5
*expect-buffer* => "Trying 127.0.0.1..."
But I'm not getting that. Is that not the way that ADD-FD-HANDLER and
SERVE-EVENT are meant to be used? I'm getting something like this...
% cmucl -eval '(progn (load "new-expect"))'
#| ... |#
CMU Common Lisp 18e, running on gsdapp04
With core: #| ... |#
Dumped on: Tue, 2003-04-08 13:23:10-05:00 on achat
See <http://www.cons.org/cmucl/> for support information.
Loaded subsystems:
Python 1.1, target SPARCstation/Solaris 2
CLOS 18e (based on PCL September 16 92 PCL (f))
* (shelf3--make)
#<process 28098 :RUNNING>
* (shelf3--add-fd-handler)
#<Handler for INPUT on descriptor 9: #<Closure Over Function "DEFUN
SHELF3--ADD-FD-HANDLER"
{404113B1}>>
* buffer => "Tryin"
bytes-read => 5
*expect-buffer* => "Tryin"
Why does registering the handler cause it to be invoked?
* (sys:serve-event 1)
NIL
* (sys:serve-event 1)
NIL
I would have expected SERVE-EVENT to notice that there is input
available on the descriptor and cause the registered handler to be
invoked. But I know that the descriptor *is* ready for INPUT because
I can explicitly read from the stream.
* (shelf3--read-5)
g 127
NIL
* (shelf3--read-5)
.0.0.
NIL
* (quit)
%
However, sometimes, the registered handler is suddenly called a bunch,
as if some clot broke free and suddenly it keeps getting called over
and over again. Frequently, this degenerates to the following:
Error in function COMMON-LISP::FD-STREAM-READ-N-BYTES:
Error reading #<Stream for descriptor 9>: I/O error
Error flushed ...
0]
Error in function COMMON-LISP::FD-STREAM-READ-N-BYTES:
Error reading #<Stream for descriptor 9>: I/O error
Error flushed ...
0]
Error in function COMMON-LISP::FD-STREAM-READ-N-BYTES:
Error reading #<Stream for descriptor 9>: I/O error
Error flushed ...
0]
^Z
% kill %1
The only recourse at this point is to kill the process.