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: Maxim Cournoyer
Subject: Re: Lost input when using suspendable read-line
Date: Mon, 23 Aug 2021 22:03:25 -0400
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/27.2 (gnu/linux)

Hello Leo!

Leo Prikler <leo.prikler@student.tugraz.at> writes:

> 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:
>
>      (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)))))))
>
> 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.

Thank you so much (to flatwhatson on #guile) :-).  I had indeed failed
to carry/refresh the partial continuity in the inner loop.  I had made a
similar mistake in the original problem which was capturing the
continuity at the wrong place in the closure, which was fixed like so:

--8<---------------cut here---------------start------------->8---
1 file changed, 7 insertions(+), 7 deletions(-)
src/mcron/base.scm | 14 +++++++-------

modified   src/mcron/base.scm
@@ -330,8 +330,7 @@ associated <job-data> instance."
     ;; could not be read.
     (let ((name (job-data:name data))
           (pid  (job-data:pid  data))
-          (port (job-data:port data))
-          (cont (job-data:continuation data)))
+          (port (job-data:port data)))
 
       (define (read-line*)
         ;; Return, as a pair, the line and the terminated delimiter or 
end-of-file
@@ -343,11 +342,12 @@ associated <job-data> instance."
             (parameterize ((current-read-waiter
                             (lambda (_)
                               (abort-to-prompt 'continue))))
-              (if cont
-                  (begin
-                    (set-job-data-continuation! data #f) ;reset continuation 
field
-                    (cont))
-                  (read-line port 'split))))
+              (let ((cont (job-data:continuation data)))
+                (if cont
+                    (begin
+                      (set-job-data-continuation! data #f) ;reset continuation
+                      (cont))
+                    (read-line port 'split)))))
           (lambda (partial-continuation)
             (cons 'suspended partial-continuation))))
 --8<---------------cut here---------------end--------------->8---

You've made my day.

Happy hacking!

Maxim



reply via email to

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