guile-user
[Top][All Lists]
Advanced

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

Re: Lost input when using suspendable read-line


From: Leo Prikler
Subject: Re: Lost input when using suspendable read-line
Date: Mon, 23 Aug 2021 23:45:19 +0200
User-agent: Evolution 3.34.2

Hi,

Am Montag, den 23.08.2021, 00:04 -0400 schrieb Maxim Cournoyer:
> --8<---------------cut here---------------start------------->8---
> (use-modules (ice-9 match)
>              (ice-9 rdelim)
>              (ice-9 suspendable-ports))
> 
> (install-suspendable-ports!)
> 
> (define (read-line* port cont)
>         ;; Return, as a pair, the line and the terminated delimiter
> or end-of-file
>         ;; object.  When a line cannot be read, return the
> '(suspended
>         ;; . partial-continuation) pair, where partial-continuation
> can be
>         ;; evaluated in the future when the port is ready to be read.
>         (call-with-prompt 'continue
>           (lambda ()
>             (parameterize ((current-read-waiter
>                             (lambda (_)
>                               (abort-to-prompt 'continue))))
>               (if cont
>                   (cont)
>                   (read-line port 'split))))
>           (lambda (partial-continuation)
>             (cons 'suspended partial-continuation))))
> 
> (define (main)
>   [...]
>      (let loop ((cont #f))
>        (match (select (list (port->fdes port)) '() '())
>          (((fdes ..1) () ())
>           (let next-line ((line+delim (read-line* port cont)))
>             (match line+delim
>               (('suspended . partial-continuation)
>                (loop partial-continuation))
>               ((line . _)
>                (format #t "~a~%" line)
>                (next-line (read-line* port cont)))))))))))
> 
> (main)
> --8<---------------cut here---------------end--------------->8---
This main loop appears broken.  Look at the value of cont over time. 
On the first few lines, before suspending, you will read all the lines
just fine.  But afterwards it will be set to partial-continuation, even
if said continuation is no longer current.

This appears to be no issue if you need less than one continuation to
finish the line, but if you need two or more, the outer continuation
will cause you to redo the inner over and over again.

I tried to make this loop somewhat more sensible:

--8<---------------cut here---------------start------------->8---
     (define port (car child->parent-pipe))
     (define cont (make-parameter #f))
     (define do-read
       (lambda ()
         (match (select (list (port->fdes port)) '() '())
           (((fdes ..1) () ())
            (match (read-line* port (cont))
              (('suspended . partial-cont)
               partial-cont)
              ((line . _)
               (format #t "~a~%" line)
               #f))))))

     (let loop ((cont? (do-read)))
       (loop (parameterize ((cont cont?)) (do-read)))))))
--8<---------------cut here---------------end--------------->8---
Here, each continuation is used exactly once, and that is to finish the
current line.  With this, I typically get output of the style:

--8<---------------cut here---------------start------------->8---
Line 1
[wait for it...]
Line 2
Line 3
Done!
Line 1
[wait for it...]
--8<----------
-----cut here---------------end--------------->8---
So it's not perfect either, but it's somewhat better than what you have
currently.

I'd hazard a guess that there are simpler implementations that need not
make use of parameters, particularly because I kinda eliminated the
inner loop anyway.  A simpler fix, which retains your structure is to
use 
               (next-line (read-line* port #f)))))))))))
in the case where a line has already been read.  This also seems to
have the desired behaviour of waiting after the "Done!" line.

Regards,
Leo




reply via email to

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