On Sun, Aug 19, 2012 at 02:54:25PM -0400, Yaron Minsky wrote:
> One quick observation: this code would be a little cleaner if you
> opened Async.Std. Then you'd write:
>
> open Core.Std
> open Async.Std
>
Ah, I missed the Async package's existence and went straight for
Async_unix. I've now updated the dependency to use Async directly and all
the interfaces are much simpler.
> let _ = within' (fun () ->
> try_with make_net_req
> >>= function
> | Error exn ->
> (* TODO: how to dump out top-level errors in a nicer way? *)
> fprintf stderr "err %s.\n%!" (Exn.backtrace ());
> return ()
> | Ok _ -> return ()
> )
> in
> Scheduler.go ()
>
> Further, I don't think the [within'] call is doing you any good, since
> you're not using it to set the block group, the monitor or the
> priority. So you should be able to just drop that.
This was actually related to me experimenting with what all those options
do (particularly the custom monitor). I'll wait for your new update when
Yury's back and see what the interface looks like. Do you normally just
read the s-expr encoded in Error.t, or do you have something fancier (the
former is good enough for me in practise).
> > - Async_streams are deprecated, and I should use Pipes instead. I
> > need to be able to run a background task that will close the I/O
> > channel whenever the Pipe is completed to close the underlying fd.
> > I obtain a Deferred via Pipe.closed, but how should this run in
> > the background? Should I just ignore the return, or register it
> > with the Scheduler?
>
> Sorry, I'm not quite able to decode the question. Do you have a code
> snippet you could share?
Yeah, in:
https://github.com/avsm/ocaml-cohttp/blob/v2-interface/async/cohttp_async.ml
let pipe_of_body read_fn ic =
let rd, wr = Pipe.create () in
(* Consume from the input channel and write to the new pipe *)
let rec write () =
read_fn ic >>= function
|Transfer.Done ->
Pipe.close wr; return ()
|Transfer.Final_chunk c -> begin
Pipe.with_write wr ~f:(fun wrfn -> wrfn c) >>=
function
|`Closed -> return ()
|`Ok _ -> Pipe.close wr; return ()
end
|Transfer.Chunk c -> begin
Pipe.with_write wr ~f:(fun wrfn -> wrfn c) >>=
function
|`Closed -> return ()
|`Ok _ -> write ()
end
in
(* TODO: how to run write () as a background task? *)
let _ = write () in
rd
This function is called to convert a HTTP body into a Pipe pair, such that
the client can lazily consume the (potentially very large) HTTP body, or
pass it onto a proxy process. There is a write() function that reads from
the HTTP body and potentially blocks on the I/O or pushback from the pipe.
We immediately return the reader pipe as a return value from the function.
So my question is what to do with:
(* TODO: how to run write () as a background task? *)
let _ = write () in
rd
Does write() need to be registered with something to be a "proper"
background task for accounting purposes, or to run a cleanup function if
the Reader pipe terminates early?
The equivalent code in Lwt is here and looks like:
https://github.com/avsm/ocaml-cohttp/blob/v2-interface/lwt/cohttp_lwt.ml
let stream_of_body read_fn ic =
let fin = ref false in
Lwt_stream.from (fun () ->
match !fin with
|true -> return None
|false -> begin
match_lwt read_fn ic with
|Transfer.Done ->
return None
|Transfer.Final_chunk c ->
fin := true;
return (Some c);
|Transfer.Chunk c ->
return (Some c)
end
)
The Lwt_stream that is returned has an Lwt.on_terminate handler for when
the consumer hits EOF (e.g. to close the socket), and I think (but am a
little blurry here) cancels the other end with an Lwt.Canceled exception
if the other side stops early.
> > In general, are there any toy network programs written using Async
> > that I could crib good style from?
>
> That's an excellent question. We do have some examples that are
> presently not included in our external tree. I'll see if we can fix
> that.
That would be great!
--
Anil Madhavapeddy
http://anil.recoil.org