As to the interactions with dynamic-wind and continuations: l did, as you figured out, intend for you to use only the post-thunk in the dynamic-wind (to close the pipes). In principle, you could use the pre-thurnk to try to restore the pipe, but there really isn't enough information to do this correctly in all cases.
I see that you've protected things a little bit by using the port-closed? predicate as a guard, but if you did that to protect against possible continuation re-entry, then probably you're better off adding something to the pre-thunk that explicitly raises an error saying that it isn't allowed to re-enter. Something like this: #lang racket (define (only-once thunk) (define already-in-once? #f) (dynamic-wind (λ () (when already-in-once? (error 'only-once "no no"))) (λ () (set! already-in-once? #t) (thunk)) void)) (only-once (λ () "hi")) (let ([saved-k #f]) (only-once (λ () (let/cc k (set! saved-k k)))) (saved-k 11)) On Tue, Aug 6, 2013 at 11:16 AM, JP Verkamp <rac...@jverkamp.com> wrote: > I've never actually used dynamic-wind, although it does look interesting > / like what I need. A few questions / caveats though: > > - Should the pipe be created in the pre-thunk or before the > dynamic-windentirely? The thunks don't seem to share scope, so I'm guessing > the latter, > but that seems a bit odd. I'm guessing the pre-thunk is for an entirely > different use case though when you are actually dealing with closing and > reopening resources are the like as control gets passed around. > > - Doesn't dynamic-wind break if the user messes with continuations during > the value-thunk? So far as I understand, when control passes out, > post-thunk is called and then pre-thunk on the way back in, but that > means that when control returns the port will be closed. I don't know how > often this will come up, but it seems to break if I nest a thread inside of > the with-gzip call. Granted, my version did as well because of the > close-input-port call. Is this just expected behavior? > > (And yes, it works fine in the more likely / sensible case of wrapping the > entire with-gzip in a thread in both cases.) > > - So far as error rather than raise, raise was my original guess. But > that added another layer of indirection to the stack trace which I didn't > at first notice (I thought I wasn't even catching the error). It makes > sense to have that though in the long run. > > That all being said, how does this version look? > > (define (with-gunzip thunk) > (define-values (pipe-from pipe-to) (make-pipe)) > (dynamic-wind > void > (λ () > (gunzip-through-ports (current-input-port) pipe-to) > (close-output-port pipe-to) > (parameterize ([current-input-port pipe-from]) > (thunk))) > (λ () > (unless (port-closed? pipe-to) (close-output-port pipe-to)) > (unless (port-closed? pipe-from) (close-input-port pipe-from))))) > > > On Tue, Aug 6, 2013 at 11:47 AM, Robby Findler < > ro...@eecs.northwestern.edu> wrote: > >> You might consider using dynamic-wind instead of that with-handlers. Or, >> instead of (error 'with-gunzip ...) just do (raise exn). That way you won't >> lose the stack information in the original exception (which is likely the >> one a user would want). >> >> Robby >> >> >> On Tue, Aug 6, 2013 at 10:40 AM, JP Verkamp <rac...@jverkamp.com> wrote: >> >>> Figured it out and cleaned it up. It turns out that I was using >>> with-handlers oddly, but reading further though the documentation it >>> works as expected. Here's a new version (generalized to any input-port): >>> >>> (define (with-gunzip thunk) >>> (define-values (pipe-from pipe-to) (make-pipe)) >>> (with-handlers ([exn:fail? >>> (λ (err) >>> (close-output-port pipe-to) >>> (close-input-port pipe-from) >>> (error 'with-gunzip (exn-message err)))]) >>> (gunzip-through-ports (current-input-port) pipe-to) >>> (close-output-port pipe-to) >>> (parameterize ([current-input-port pipe-from]) >>> (thunk)) >>> (close-input-port pipe-from))) >>> >>> If anyone's interested in a more in depth write up / source code for >>> this and with-gzip: >>> - writeup: >>> http://blog.jverkamp.com/2013/08/06/adventures-in-racket-gzip/ >>> - source: >>> https://github.com/jpverkamp/small-projects/tree/master/blog/with-gzip.rkt >>> >>> >>> On Mon, Aug 5, 2013 at 5:36 PM, JP Verkamp <rac...@jverkamp.com> wrote: >>> >>>> Thanks! make-pipe isn't something that I've had to use otherwise, so I >>>> missed the optional parameter. That does certainly seem to help. >>>> >>>> Here's my first take of with-input-from-gzipped-file: >>>> >>>> (define (with-input-from-gzipped-file filename thunk #:buffer-size >>>> [buffer-size #f]) >>>> (call-with-input-file filename >>>> (lambda (file-from) >>>> (define-values (pipe-from pipe-to) (make-pipe buffer-size)) >>>> >>>> (thread >>>> (λ () >>>> (gunzip-through-ports file-from pipe-to) >>>> (close-output-port pipe-to))) >>>> >>>> (current-input-port pipe-from) >>>> (thunk) >>>> (close-input-port pipe-from)))) >>>> >>>> The main thing missing is that there's no error handling (where the >>>> pipe should still be closed). At the very least, if I try to call this on a >>>> non-gzipped file, it breaks on the gunzip-through-ports line. >>>> Theoretically, some variation of with-handlers should work (errorshould >>>> raise an >>>> exn:fail?, yes?), but it doesn't seem to be helping. >>>> >>>> Any help with that? >>>> >>>> Alternatively, I've now found this: >>>> http://planet.racket-lang.org/display.ss?package=gzip.plt&owner=soegaard >>>> >>>> It seems to do exactly what I need, albeit without the call-with-* >>>> forms, but that's easy enough to wrap. With some very basic testing, it >>>> does seem to be buffering though, although it is a bit slower than the >>>> above. Not enough to cause trouble though. >>>> >>>> >>>> On Mon, Aug 5, 2013 at 4:51 PM, Ryan Culpepper <ry...@ccs.neu.edu>wrote: >>>> >>>>> On 08/05/2013 04:29 PM, JP Verkamp wrote: >>>>> >>>>>> Is there a nice / idiomatic way to work with gzipped data in a >>>>>> streaming >>>>>> manner (to avoid loading the rather large files into memory at once). >>>>>> So >>>>>> far as I can tell, my code isn't doing that. It hangs for a while on >>>>>> the >>>>>> call to gunzip-through-ports, long enough to uncompress the entire >>>>>> file, >>>>>> then reads are pretty quick afterwords. >>>>>> >>>>>> Here's what I have thus far: >>>>>> >>>>>> #lang racket >>>>>> >>>>>> (require file/gunzip) >>>>>> >>>>>> (define-values (pipe-from pipe-to) (make-pipe)) >>>>>> (with-input-from-file "test.rkt.gz" >>>>>> (lambda () >>>>>> (gunzip-through-ports (current-input-port) pipe-to) >>>>>> (for ([line (in-lines pipe-from)]) >>>>>> (displayln line)))) >>>>>> >>>>> >>>>> You should probably 1) limit the size of the pipe (to stop it from >>>>> inflating the whole file at once) and 2) put the gunzip-through-ports call >>>>> in a separate thread. The gunzip thread will block when the pipe is full; >>>>> when your program reads some data out of the pipe, the gunzip thread will >>>>> be able to make some more progress. Something like this: >>>>> >>>>> (define-values (pipe-from pipe-to) (make-pipe 4000)) >>>>> (with-input-from-file "test.rkt.gz" >>>>> (lambda () >>>>> (thread >>>>> >>>>> (lambda () >>>>> (gunzip-through-ports (current-input-port) pipe-to) >>>>> (close-output-port pipe-to))) >>>>> >>>>> (for ([line (in-lines pipe-from)]) >>>>> (displayln line)))) >>>>> >>>>> As an additional problem, that code doesn't actually work. >>>>>> in-lines seems to be waiting for an eof-object? that >>>>>> gunzip-through-ports isn't sending. Am I missing something? It ends up >>>>>> just hanging after reading and printing the file. >>>>>> >>>>> >>>>> The docs don't say anything about closing the port, so you'll probably >>>>> have to do that yourself. In the code above, I added a call to >>>>> close-output-port. >>>>> >>>>> Ryan >>>>> >>>>> >>>> >>> >>> ____________________ >>> Racket Users list: >>> http://lists.racket-lang.org/users >>> >>> >> > > ____________________ > Racket Users list: > http://lists.racket-lang.org/users > >
____________________ Racket Users list: http://lists.racket-lang.org/users