* 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
[parent not found: <CAGua6m0eTYuXW3tq8o-pR7fgRH5yi8D6kjF1HdLPgC16Cc2=jg@mail.gmail.com>]
* 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).