unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
* Continuations in custom port: "cannot invoke continuation from this context"
@ 2019-03-28  4:30 Caleb Ristvedt
       [not found] ` <CAGua6m0eTYuXW3tq8o-pR7fgRH5yi8D6kjF1HdLPgC16Cc2=jg@mail.gmail.com>
  0 siblings, 1 reply; 5+ messages in thread
From: Caleb Ristvedt @ 2019-03-28  4:30 UTC (permalink / raw)
  To: guile-user

I have a procedure that writes to an output port, a procedure that reads
from an input port, and some processing that I want to do in between. So
I figured that I'd try my hand at using continuations to write a custom
input/output port that would hook the two procedures together. The idea
is that I would give it two initial procedures, one for writing, one for
reading, and the read! and write! procedures would remember what the
latest read/write parameters were and read from / write to the
corresponding bytevectors, switching to the other procedure if there
wasn't a bytevector associated with that side. From what I've heard,
it's basically coroutining.

But I eventually (turns out exceptions in custom ports get silently
swallowed and turned into an attempt to close the port) discovered that
I was getting the error "cannot invoke continuations from this
context". The control flow went like this:

1. writer
2. write!
3. reader
4. read!
5. write!
6. writer
7. write!
8. exception when trying to go to read!

So clearly invoking the write continuation to get from 4 to 5 worked,
but invoking the read continuation to get from 7 to 8 didn't. That
sounds like something I read while looking at delimited continuations:

"... This is because composing a saved continuation with the current
continuation involves relocating the stack frames that were saved from
the old stack onto a (possibly) new position on the new stack, and Guile
can only do this for stack frames that it created for Scheme code, not
stack frames created by the C compiler"

I was under the impression this restriction only held for delimited
continuations, but in case I was wrong, I ran

(use-modules (ice-9 suspendable-ports))
(install-suspendable-ports!)

and it continued to fail.

Here is the code I've been using, in all its hideous glory:

----------------------------------------------------------------
(use-modules (ice-9 suspendable-ports))
(use-modules (rnrs io ports))
(use-modules (ice-9 textual-ports))
(use-modules (rnrs bytevectors))
(install-suspendable-ports!)

(let* ((the-pipe #f)
       (write-cont (lambda () (format the-pipe "Hello, world!~%")))
       (read-cont (lambda () (write (get-line the-pipe))))
       (read-bv #f)
       (read-len 0)
       (read-off 0)
       (write-bv #f)
       (write-len 0)
       (write-off 0))
  (set! the-pipe (make-custom-binary-input/output-port
		  "scheme-pipe"
		  ;; read!
		  (lambda (bv index len)
		    (catch #t
		      (lambda ()
			(format #t "Reading ~A bytes~%" len)
			(format #t "write-len: ~A write-off: ~A write-bv: ~A~%"
				write-len write-off write-bv)
			;; Stored data?
			(if (> write-len len)
			    ;; Write it, return to reader
			    (begin
			      (format #t "Read ~A bytes, return to reader.~%" len)
			      (bytevector-copy! write-bv write-off bv index len)
			      (set! write-off (+ write-off len))
			      (set! write-len (- write-len len))
			      len)
			    ;; Write it, return to writer
			    (begin
			      (format #t "Read ~A bytes, return to writer.~%"
				      write-len)
			      (when (> write-len 0)
				(bytevector-copy! write-bv write-off bv index
						  write-len))
			      (set! read-bv bv)
			      (set! read-off (+ index write-len))
			      (set! read-len (- len write-len))
			      (call/cc
			       (lambda (c)
				 (set! read-cont c)
				 (write-cont)))
			      (format #t "RETURN FROM READ!~%")
			      len)))
		      (lambda args
			(format #t "EXCEPTION IN READ: ~A~%" args))))
		  ;; write!
		  (lambda (bv index len)
		    (catch #t
		      (lambda ()
			(format #t "Writing ~A bytes~%" len)
			(format #t "read-len: ~A read-off: ~A~%"
				read-len read-off)
			(if (> read-len len)
			    ;; Write it, return to writer
			    (begin
			      (format #t "Wrote ~A bytes, return to writer.~%"
				      len)
			      (bytevector-copy! bv index read-bv read-off len)
			      (set! read-off (+ read-off len))
			      (set! read-len (- read-len len))
			      len)
			    ;; Write it, return to reader
			    (begin
			      (format #t "Wrote ~A bytes, return to reader.~%"
				      read-len)
			      (when (> read-len 0)
				(bytevector-copy! bv index read-bv read-off read-len))
			      (set! write-bv bv)
			      (set! write-off (+ index read-len))
			      (set! write-len (- len read-len))
			      (call/cc
			       (lambda (c)
				 (set! write-cont c)
				 (read-cont)))
			      (format #t "RETURN FROM WRITE!~%")
			      len)))
		      (lambda args
			(format #t "EXCEPTION IN WRITE: ~A~%" args))))
		  ;; get-position
		  (lambda ()
		    (format #t "Tried getting pos~%"))
		  ;; set-position!
		  (lambda (pos)
		    (format #t "Tried setting pos~%"))
		  ;; close
		  (lambda ()
		    (format #t "Tried closing~%"))))
  (setvbuf the-pipe 'none)
  (write-cont)
  (format #t "DONE WRITING!~%")
  (read-cont))
----------------------------------------------------------------

But just now I noticed that if I change the reader from

(get-line the-pipe)

to

(get-bytevector-all)

it gets as far as the "DONE WRITING" message and then produces the same
"cannot invoke continuation from this context" error.

Any idea what's going wrong here?

Thanks,

- reepca



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Continuations in custom port: "cannot invoke continuation from this context"
       [not found] ` <CAGua6m0eTYuXW3tq8o-pR7fgRH5yi8D6kjF1HdLPgC16Cc2=jg@mail.gmail.com>
@ 2019-04-01  2:06   ` Caleb Ristvedt
  2019-04-01  9:12     ` Mark H Weaver
  0 siblings, 1 reply; 5+ messages in thread
From: Caleb Ristvedt @ 2019-04-01  2:06 UTC (permalink / raw)
  To: Stefan Israelsson Tampe, guile-user

Any idea why it would still be going to C land even with suspendable ports
installed? The manual says they're implemented in scheme for precisely this
reason.

On Thu, Mar 28, 2019 at 12:58 PM Stefan Israelsson Tampe <
stefan.itampe@gmail.com> wrote:

> I have not looked at this more carefully. But I suspect that the code is
> going to C land and then enters the scheme again
> via the hooks and then continuations won't work as expected.
>
> /Stefan
>
> On Thu, Mar 28, 2019 at 5:42 AM Caleb Ristvedt <caleb.ristvedt@cune.org>
> wrote:
>
>> I have a procedure that writes to an output port, a procedure that reads
>> from an input port, and some processing that I want to do in between. So
>> I figured that I'd try my hand at using continuations to write a custom
>> input/output port that would hook the two procedures together. The idea
>> is that I would give it two initial procedures, one for writing, one for
>> reading, and the read! and write! procedures would remember what the
>> latest read/write parameters were and read from / write to the
>> corresponding bytevectors, switching to the other procedure if there
>> wasn't a bytevector associated with that side. From what I've heard,
>> it's basically coroutining.
>>
>> But I eventually (turns out exceptions in custom ports get silently
>> swallowed and turned into an attempt to close the port) discovered that
>> I was getting the error "cannot invoke continuations from this
>> context". The control flow went like this:
>>
>> 1. writer
>> 2. write!
>> 3. reader
>> 4. read!
>> 5. write!
>> 6. writer
>> 7. write!
>> 8. exception when trying to go to read!
>>
>> So clearly invoking the write continuation to get from 4 to 5 worked,
>> but invoking the read continuation to get from 7 to 8 didn't. That
>> sounds like something I read while looking at delimited continuations:
>>
>> "... This is because composing a saved continuation with the current
>> continuation involves relocating the stack frames that were saved from
>> the old stack onto a (possibly) new position on the new stack, and Guile
>> can only do this for stack frames that it created for Scheme code, not
>> stack frames created by the C compiler"
>>
>> I was under the impression this restriction only held for delimited
>> continuations, but in case I was wrong, I ran
>>
>> (use-modules (ice-9 suspendable-ports))
>> (install-suspendable-ports!)
>>
>> and it continued to fail.
>>
>> Here is the code I've been using, in all its hideous glory:
>>
>> ----------------------------------------------------------------
>> (use-modules (ice-9 suspendable-ports))
>> (use-modules (rnrs io ports))
>> (use-modules (ice-9 textual-ports))
>> (use-modules (rnrs bytevectors))
>> (install-suspendable-ports!)
>>
>> (let* ((the-pipe #f)
>>        (write-cont (lambda () (format the-pipe "Hello, world!~%")))
>>        (read-cont (lambda () (write (get-line the-pipe))))
>>        (read-bv #f)
>>        (read-len 0)
>>        (read-off 0)
>>        (write-bv #f)
>>        (write-len 0)
>>        (write-off 0))
>>   (set! the-pipe (make-custom-binary-input/output-port
>>                   "scheme-pipe"
>>                   ;; read!
>>                   (lambda (bv index len)
>>                     (catch #t
>>                       (lambda ()
>>                         (format #t "Reading ~A bytes~%" len)
>>                         (format #t "write-len: ~A write-off: ~A write-bv:
>> ~A~%"
>>                                 write-len write-off write-bv)
>>                         ;; Stored data?
>>                         (if (> write-len len)
>>                             ;; Write it, return to reader
>>                             (begin
>>                               (format #t "Read ~A bytes, return to
>> reader.~%" len)
>>                               (bytevector-copy! write-bv write-off bv
>> index len)
>>                               (set! write-off (+ write-off len))
>>                               (set! write-len (- write-len len))
>>                               len)
>>                             ;; Write it, return to writer
>>                             (begin
>>                               (format #t "Read ~A bytes, return to
>> writer.~%"
>>                                       write-len)
>>                               (when (> write-len 0)
>>                                 (bytevector-copy! write-bv write-off bv
>> index
>>                                                   write-len))
>>                               (set! read-bv bv)
>>                               (set! read-off (+ index write-len))
>>                               (set! read-len (- len write-len))
>>                               (call/cc
>>                                (lambda (c)
>>                                  (set! read-cont c)
>>                                  (write-cont)))
>>                               (format #t "RETURN FROM READ!~%")
>>                               len)))
>>                       (lambda args
>>                         (format #t "EXCEPTION IN READ: ~A~%" args))))
>>                   ;; write!
>>                   (lambda (bv index len)
>>                     (catch #t
>>                       (lambda ()
>>                         (format #t "Writing ~A bytes~%" len)
>>                         (format #t "read-len: ~A read-off: ~A~%"
>>                                 read-len read-off)
>>                         (if (> read-len len)
>>                             ;; Write it, return to writer
>>                             (begin
>>                               (format #t "Wrote ~A bytes, return to
>> writer.~%"
>>                                       len)
>>                               (bytevector-copy! bv index read-bv read-off
>> len)
>>                               (set! read-off (+ read-off len))
>>                               (set! read-len (- read-len len))
>>                               len)
>>                             ;; Write it, return to reader
>>                             (begin
>>                               (format #t "Wrote ~A bytes, return to
>> reader.~%"
>>                                       read-len)
>>                               (when (> read-len 0)
>>                                 (bytevector-copy! bv index read-bv
>> read-off read-len))
>>                               (set! write-bv bv)
>>                               (set! write-off (+ index read-len))
>>                               (set! write-len (- len read-len))
>>                               (call/cc
>>                                (lambda (c)
>>                                  (set! write-cont c)
>>                                  (read-cont)))
>>                               (format #t "RETURN FROM WRITE!~%")
>>                               len)))
>>                       (lambda args
>>                         (format #t "EXCEPTION IN WRITE: ~A~%" args))))
>>                   ;; get-position
>>                   (lambda ()
>>                     (format #t "Tried getting pos~%"))
>>                   ;; set-position!
>>                   (lambda (pos)
>>                     (format #t "Tried setting pos~%"))
>>                   ;; close
>>                   (lambda ()
>>                     (format #t "Tried closing~%"))))
>>   (setvbuf the-pipe 'none)
>>   (write-cont)
>>   (format #t "DONE WRITING!~%")
>>   (read-cont))
>> ----------------------------------------------------------------
>>
>> But just now I noticed that if I change the reader from
>>
>> (get-line the-pipe)
>>
>> to
>>
>> (get-bytevector-all)
>>
>> it gets as far as the "DONE WRITING" message and then produces the same
>> "cannot invoke continuation from this context" error.
>>
>> Any idea what's going wrong here?
>>
>> Thanks,
>>
>> - reepca
>>
>>


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Continuations in custom port: "cannot invoke continuation from this context"
  2019-04-01  2:06   ` Caleb Ristvedt
@ 2019-04-01  9:12     ` Mark H Weaver
  2019-04-18  0:47       ` Mark H Weaver
  0 siblings, 1 reply; 5+ messages in thread
From: Mark H Weaver @ 2019-04-01  9:12 UTC (permalink / raw)
  To: Caleb Ristvedt; +Cc: guile-user

Hi Caleb,

Caleb Ristvedt <caleb.ristvedt@cune.org> writes:

> On Thu, Mar 28, 2019 at 12:58 PM Stefan Israelsson Tampe <
> stefan.itampe@gmail.com> wrote:
>
>> I have not looked at this more carefully. But I suspect that the code is
>> going to C land and then enters the scheme again
>> via the hooks and then continuations won't work as expected.

> Any idea why it would still be going to C land even with suspendable ports
> installed? The manual says they're implemented in scheme for precisely this
> reason.

'install-suspendable-ports!' replaces only a subset of the C port
functions with Scheme implementations, namely the ones listed in
'port-bindings' in suspendable-ports.scm.

The problem here is the custom port.  Custom ports are implemented in C,
and are not reimplemented by suspendable-ports.scm.  When you perform
I/O on the custom port, it enters C code in r6rs-ports.c which calls
back into Scheme code to call your custom I/O handlers 'read!' and
'write!'.

       Mark



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Continuations in custom port: "cannot invoke continuation from this context"
  2019-04-01  9:12     ` Mark H Weaver
@ 2019-04-18  0:47       ` Mark H Weaver
  2019-04-27 21:04         ` Ludovic Courtès
  0 siblings, 1 reply; 5+ messages in thread
From: Mark H Weaver @ 2019-04-18  0:47 UTC (permalink / raw)
  To: Caleb Ristvedt; +Cc: guile-user

Hi Caleb,

Earlier I wrote:
> Caleb Ristvedt <caleb.ristvedt@cune.org> writes:
>
>> On Thu, Mar 28, 2019 at 12:58 PM Stefan Israelsson Tampe <
>> stefan.itampe@gmail.com> wrote:
>>
>>> I have not looked at this more carefully. But I suspect that the code is
>>> going to C land and then enters the scheme again
>>> via the hooks and then continuations won't work as expected.
>
>> Any idea why it would still be going to C land even with suspendable ports
>> installed? The manual says they're implemented in scheme for precisely this
>> reason.
>
> 'install-suspendable-ports!' replaces only a subset of the C port
> functions with Scheme implementations, namely the ones listed in
> 'port-bindings' in suspendable-ports.scm.
>
> The problem here is the custom port.  Custom ports are implemented in C,
> and are not reimplemented by suspendable-ports.scm.  When you perform
> I/O on the custom port, it enters C code in r6rs-ports.c which calls
> back into Scheme code to call your custom I/O handlers 'read!' and
> 'write!'.

Since writing this, I've written preliminary patches to add suspendable
I/O support for custom ports:

  https://lists.gnu.org/archive/html/guile-devel/2019-04/msg00019.html

I'd be curious to hear how it works in your use case.

      Mark



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Continuations in custom port: "cannot invoke continuation from this context"
  2019-04-18  0:47       ` Mark H Weaver
@ 2019-04-27 21:04         ` Ludovic Courtès
  0 siblings, 0 replies; 5+ messages in thread
From: Ludovic Courtès @ 2019-04-27 21:04 UTC (permalink / raw)
  To: guile-user

Hi Mark,

Mark H Weaver <mhw@netris.org> skribis:

> Since writing this, I've written preliminary patches to add suspendable
> I/O support for custom ports:
>
>   https://lists.gnu.org/archive/html/guile-devel/2019-04/msg00019.html

Really cool.  I’m MIA on Guile but I hope this can land soonish!

Ludo’.




^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2019-04-27 21:04 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-03-28  4:30 Continuations in custom port: "cannot invoke continuation from this context" Caleb Ristvedt
     [not found] ` <CAGua6m0eTYuXW3tq8o-pR7fgRH5yi8D6kjF1HdLPgC16Cc2=jg@mail.gmail.com>
2019-04-01  2:06   ` Caleb Ristvedt
2019-04-01  9:12     ` Mark H Weaver
2019-04-18  0:47       ` Mark H Weaver
2019-04-27 21:04         ` Ludovic Courtès

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).