diff --git a/dev/index.html b/dev/index.html index aaac8143..fbf4d185 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,2 +1,2 @@ -
Browse by name, by tag, the standard library and the OCaml manual (online, latest version).
Generated for /home/runner/work/moonpool/moonpool/_opam/lib
Browse by name, by tag, the standard library and the OCaml manual (online, latest version).
Generated for /home/runner/work/moonpool/moonpool/_opam/lib
Lwt.Exception_filterAn Exception_filter.t is a value which indicates to Lwt what exceptions to catch and what exceptions to let bubble up all the way out of the main loop immediately.
val handle_all : thandle_all is the default filter. With it the all the exceptions (including Out_of_memory and Stack_overflow) can be handled: caught and transformed into rejected promises.
val handle_all_except_runtime : thandle_all_except_runtime is a filter which lets the OCaml runtime exceptions (Out_of_memory and Stack_overflow) go through all the Lwt abstractions and bubble all the way out of the call to Lwt_main.run.
Note that if you set this handler, then the runtime exceptions leave the Lwt internal state inconsistent. For this reason, you will not be able to call Lwt_main.run again after such an exception has escaped Lwt_main.run.
val set : t -> unitset sets the given exception filter globally. You should call this function at most once during the start of your program, before the first call to Lwt_main.run.
Let_syntax.Open_on_rhsInfix.Let_syntaxThis module provides support for ppx_let.
val return : 'a -> 'a tSee Lwt.return.
module Open_on_rhs : sig ... endLwt.InfixThis module provides several infix operators for making programming with Lwt more convenient.
To use it, open Lwt.Infix.
Of the operators declared in this module, only >|= is recommended for new code. The only other commonly-used operator is >>=.
p >>= f is the same as Lwt.bind p f. It requires Lwt.Infix to be opened in scope:
open Lwt.Infix
+
+let () =
+ Lwt_main.run
+ (Lwt_io.(read_line stdin) >>= Lwt_io.printl)
+
+(* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)It is recommended to use the PPX let%lwt syntax instead. This operator is the next-best choice. It is frequently found while reading existing Lwt code.
p >|= f is the same as Lwt.map f p. It requires Lwt.Infix to be opened in scope.
open Lwt.Infix
+
+let () =
+ Lwt_main.run
+ (Lwt_io.(read_line stdin) >|= ignore)
+
+(* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)p1 <?> p2 is the same as Lwt.choose [p1; p2]. It requires Lwt.Infix to be opened in scope.
Unlike with Lwt.bind and Lwt.map, there are no problems with explicit Lwt.choose syntax, so using this operator is not recommended.
Furthermore, most users actually need Lwt.pick instead of Lwt.choose.
f =<< p is the same as Lwt.bind p f. It requires Lwt.Infix to be opened in scope.
This operator is obscure and its use is discouraged. It is the same as p >>= f.
f =|< p is the same as Lwt.map f p. It requires Lwt.Infix to be opened in scope.
This operator is obscure and its use is discouraged. It is the same as p >|= f.
module Let_syntax : sig ... endThis module provides support for ppx_let.
Let_syntax.Open_on_rhsLet_syntax.Let_syntaxval return : 'a -> 'a tSee Lwt.return.
module Open_on_rhs : sig ... endLwt.Let_syntaxmodule Let_syntax : sig ... endLwt.SyntaxLwtAsynchronous programming with promises.
A promise is a placeholder for a single value which might take a long time to compute. Speaking roughly, a promise is a ref that can be filled in later. To make that precise, here is how promises differ from refs:
So, promises are optional, write-once references, and when they don't yet have a value, they store a list of callbacks that are waiting for the value.
The waiting callbacks make promises a natural data type for asynchronous programming. For example, you can ask Lwt to read a file. Lwt immediately returns you only a promise for the data.
You can neglect this promise for a while. You can do some other computation, request more I/O, etc. At some point, you might decide to attach a callback to the read promise, maybe several callbacks.
In the meantime, the read operation is running in the background. Once it finishes, Lwt resolves the read promise by putting the data into it. Lwt then runs the callbacks you attached.
One of those might take the data, and ask Lwt to write it to STDOUT. Lwt gives you a promise for that, too, and the process repeats.
Lwt has a small amount of syntactic sugar to make this look as natural as possible:
let () =
+ Lwt_main.run begin
+ let%lwt data = Lwt_io.(read_line stdin) in
+ let%lwt () = Lwt_io.printl data in
+ Lwt.return ()
+ end
+
+(* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix echo.ml && ./a.out *)This is all explained in the next sections:
After that is the reference proper, which goes into painful levels of detail on every single type and value in this module, Lwt. Please be safe, and read only what you need from it :)
Happy asynchronous programming!
All of Lwt is variations on:
'a Lwt.t are placeholders for values of type 'a.Lwt.bind attaches callbacks to promises. When a promise gets a value, its callbacks are called.'a Lwt.u are used to write values into promises, through Lwt.wakeup_later.Lwt.wait. Lwt I/O functions call Lwt.wait internally, but return only the promise.Lwt_main.run is used to wait on one “top-level” promise. When that promise gets a value, the program terminates.Let's read from STDIN. The first version is written using ordinary values from the OCaml standard library. This makes the program block until the user enters a line:
let () =
+ let line : string = read_line () in
+ print_endline "Now unblocked!";
+ ignore line
+
+(* ocamlfind opt -linkpkg code.ml && ./a.out *)If we use a promise instead, execution continues immediately:
let () =
+ let line_promise : string Lwt.t =
+ Lwt_io.(read_line stdin) in
+ print_endline "Execution just continues...";
+ ignore line_promise
+
+(* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)Indeed, this program is a little too asynchronous – it exits right away! Let's force it to wait for line_promise at the end by calling Lwt_main.run:
let () =
+ let line_promise : string Lwt.t =
+ Lwt_io.(read_line stdin) in
+ print_endline "Execution just continues...";
+
+ let line : string =
+ Lwt_main.run line_promise in
+ ignore line
+
+(* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)Lwt_main.run should only be called once, on one promise, at the top level of your program. Most of the time, waiting for promises is done using let%lwt. That is the recommended syntactic sugar for Lwt.bind, and is pronounced “bind”:
let () =
+ let p : unit Lwt.t =
+ let%lwt line_1 = Lwt_io.(read_line stdin) in
+ let%lwt line_2 = Lwt_io.(read_line stdin) in
+ Lwt_io.printf "%s and %s\n" line_1 line_2
+ in
+
+ Lwt_main.run p
+
+(* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)The way that works is everything in scope after the “in” in “let%lwt x = ... in ...” goes into a callback, and “x” is that callback's argument. So, we could have been very explicit, and written the code like this:
let () =
+ let p : unit Lwt.t =
+ let line_1_promise : string Lwt.t = Lwt_io.(read_line stdin) in
+ Lwt.bind line_1_promise (fun (line_1 : string) ->
+
+ let line_2_promise : string Lwt.t = Lwt_io.(read_line stdin) in
+ Lwt.bind line_2_promise (fun (line_2 : string) ->
+
+ Lwt_io.printf "%s and %s\n" line_1 line_2))
+ in
+
+ Lwt_main.run p
+
+(* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)But, as you can see, this is verbose, and the indentation gets a bit crazy. So, we will always use let%lwt.
The code above reads two lines in sequence, because we ask Lwt to wait for line_1, before calling the second Lwt_io.read_line in the callback, to start the second I/O.
We could also run I/O concurrently. All we have to do is not start the second I/O in a callback of the first. Because it doesn't make sense to read two lines from STDIN concurrently, let's start two waits instead:
let () =
+ Lwt_main.run begin
+ let three_seconds : unit Lwt.t = Lwt_unix.sleep 3. in
+ let five_seconds : unit Lwt.t = Lwt_unix.sleep 5. in
+ let%lwt () = three_seconds in
+ let%lwt () = Lwt_io.printl "3 seconds passed" in
+ let%lwt () = five_seconds in
+ Lwt_io.printl "Only 2 more seconds passed"
+ end
+
+(* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)This program takes about five seconds to run. We are still new to let%lwt, so let's desugar it:
let () =
+ Lwt_main.run begin
+ let three_seconds : unit Lwt.t = Lwt_unix.sleep 3. in
+ let five_seconds : unit Lwt.t = Lwt_unix.sleep 5. in
+
+ (* Both waits have already been started at this point! *)
+
+ Lwt.bind three_seconds (fun () ->
+ (* This is 3 seconds later. *)
+ Lwt.bind (Lwt_io.printl "3 seconds passed") (fun () ->
+ Lwt.bind five_seconds (fun () ->
+ (* Only 2 seconds were left in the 5-second wait, so
+ this callback runs 2 seconds after the first callback. *)
+ Lwt_io.printl "Only 2 more seconds passed")))
+ end
+
+(* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)And that's it! Concurrency in Lwt is simply a matter of whether you start an operation in the callback of another one or not. As a convenience, Lwt provides a few helpers for common concurrency patterns.
It's important to understand that promises are a pure-OCaml data type. They don't do any fancy scheduling or I/O. They are just lists of callbacks (if pending), or containers for one value (if resolved).
The interesting function is Lwt_main.run. It's a wrapper around select(2), epoll(7), kqueue(2), or whatever asynchronous I/O API your system provides. On browsers, the work of Lwt_main.run is done by the surrounding JavaScript engine, so you don't call Lwt_main.run from inside your program. But the execution model is still the same, and the description below applies!
To avoid writing out “underlying asynchronous I/O API,” we'll assume, in this section, that the API is select(2). That's just for the sake of abbreviation. It doesn't actually matter, for most purposes, what the underlying I/O API is.
Let's use the program from the tutorial that reads two lines as an example. Here it is, again, in its desugared form:
let () =
+ let p : unit Lwt.t =
+ let line_1_promise : string Lwt.t = Lwt_io.(read_line stdin) in
+ Lwt.bind line_1_promise (fun (line_1 : string) ->
+
+ let line_2_promise : string Lwt.t = Lwt_io.(read_line stdin) in
+ Lwt.bind line_2_promise (fun (line_2 : string) ->
+
+ Lwt_io.printf "%s and %s\n" line_1 line_2))
+ in
+
+ Lwt_main.run p
+
+(* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)Lwt_main.run is your program's main I/O loop. You pass it a single promise, and it:
select(2) to put your process to sleep until the next I/O completes.line_1. Lwt_main.run knows that I/O is supposed to resolve line_1_promise, so it puts line_1 into the promise and resolves it.line_1_promise to run, one after another. Each callback is also ordinary OCaml code. In our case, there is only one callback, but in general, there might be several, and they might also resolve additional promises. So, promise resolution triggers a “cascade” of callbacks. Eventually, however, we should run out of callbacks, and control will return to Lwt_main.run.Lwt_main.run – the one that will read line_2. There are no callbacks left to run after that, so control returns to Lwt_main.run.Lwt_main.run goes back to sleep again by calling select(2), now waiting for the second I/O that we just registered. The loop repeats itself from step 1.This has two major implications, one good and one bad. Let's start with the bad one.
(1) If one of your callbacks enters an infinite loop, calls an Lwt-unfriendly blocking I/O, or just runs for a really long time, it won't return control to Lwt_main.run anytime soon. That means Lwt_main.run won't get a chance to resolve any other Lwt I/O promises, even if the underlying I/O operations complete.
In case your callback is just using the CPU for a really long time, you can insert a few calls to Lwt.pause into it, and resume your computation in callbacks of pause. This is basically the same as Lwt_unix.sleep 0. – it's a promise that will be resolved by Lwt_main.run after any other I/O resolutions that are already in its queue.
(2) The good implication is that all your callbacks run in a single thread. This means that in most situations, you don't have to worry about locks, synchronization, etc. Anything that is in the same callback is guaranteed to run without interruption. Lwt programs are often much easier to write and refactor, than equivalent programs written with threads – but both are concurrent!
This module Lwt is the pure-OCaml definition of promises and callback-calling. It has a few extras on top of what's described above:
Lwt.Canceled. It has extra helpers in the Lwt API.Lwt.bind. As we saw, Lwt concurrency requires only deciding whether to run something inside a callback, or outside it. These functions just implement common patterns, and make intent explicit.The next layer above module Lwt is the pure-OCaml Lwt “core” library, which provides some promise-friendly patterns, like streams and mvars. This consists of the modules Lwt_list, Lwt_stream, Lwt_result, Lwt_mutex, Lwt_condition, Lwt_mvar, Lwt_pool, and Lwt_switch.
Above that is the Lwt Unix binding, where I/O begins. This includes the module Lwt_main, including the all-important Lwt_main.run. The rest of the Unix binding consists of functions, each one of which...
Lwt_main.run, so if you attach callbacks to the promise, they will be called when the I/O operation completes.The functions are grouped into modules:
Lwt_unix for Unix system calls.Lwt_bytes for Unix system calls on bigarrays.Lwt_io for Stdlib-like high-level channels, TCP servers, etc.Lwt_process for managing subprocesses.Lwt_preemptive for spawning system threads.Lwt_gc, Lwt_engine, Lwt_throttle, Lwt_timeout, Lwt_sys.Warning! Introductory material ends and detailed reference begins!
Promises for values of type 'a.
A promise is a memory cell that is always in one of three states:
'a,A resolved promise is one that is either fulfilled or rejected, i.e. not pending. Once a promise is resolved, its content cannot change. So, promises are write-once references. The only possible state changes are (1) from pending to fulfilled and (2) from pending to rejected.
Promises are typically “read” by attaching callbacks to them. The most basic functions for that are Lwt.bind, which attaches a callback that is called when a promise becomes fulfilled, and Lwt.catch, for rejection.
Promise variables of this type, 'a Lwt.t, are actually read-only in Lwt. Separate resolvers of type 'a Lwt.u are used to write to them. Promises and their resolvers are created together by calling Lwt.wait. There is one exception to this: most promises can be canceled by calling Lwt.cancel, without going through a resolver.
Resolvers for promises of type 'a Lwt.t.
Each resolver can be thought of as the write end of one promise. It can be passed to Lwt.wakeup_later, Lwt.wakeup_later_exn, or Lwt.wakeup_later_result to resolve that promise.
Creates a new pending promise, paired with its resolver.
It is rare to use this function directly. Many helpers in Lwt, and Lwt-aware libraries, call it internally, and return only the promise. You then chain the promises together using Lwt.bind.
However, it is important to understand Lwt.wait as the fundamental promise “constructor.” All other functions that evaluate to a promise can be, or are, eventually implemented in terms of it.
val wakeup_later : 'a u -> 'a -> unitLwt.wakeup_later r v fulfills, with value v, the pending promise associated with resolver r. This triggers callbacks attached to the promise.
If the promise is not pending, Lwt.wakeup_later raises Stdlib.Invalid_argument, unless the promise is canceled. If the promise is canceled, Lwt.wakeup_later has no effect.
If your program has multiple threads, it is important to make sure that Lwt.wakeup_later (and any similar function) is only called from the main thread. Lwt.wakeup_later can trigger callbacks attached to promises by the program, and these assume they are running in the main thread. If you need to communicate from a worker thread to the main thread running Lwt, see Lwt_preemptive or Lwt_unix.send_notification.
val wakeup_later_exn : _ u -> exn -> unitLwt.wakeup_later_exn r exn is like Lwt.wakeup_later, except, if the associated promise is pending, it is rejected with exn.
val return : 'a -> 'a tLwt.return v creates a new promise that is already fulfilled with value v.
This is needed to satisfy the type system in some cases. For example, in a match expression where one case evaluates to a promise, the other cases have to evaluate to promises as well:
match need_input with
+| true -> Lwt_io.(read_line stdin) (* Has type string Lwt.t... *)
+| false -> Lwt.return "" (* ...so wrap empty string in a promise. *)Another typical usage is in let%lwt. The expression after the “in” has to evaluate to a promise. So, if you compute an ordinary value instead, you have to wrap it:
let%lwt line = Lwt_io.(read_line stdin) in
+Lwt.return (line ^ ".")val fail : exn -> _ tLwt.fail exn is like Lwt.return, except the new promise that is already rejected with exn.
Whenever possible, it is recommended to use raise exn instead, as raise captures a backtrace, while Lwt.fail does not. If you call raise exn in a callback that is expected by Lwt to return a promise, Lwt will automatically wrap exn in a rejected promise, but the backtrace will have been recorded by the OCaml runtime.
For example, bind's second argument is a callback which returns a promise. And so it is recommended to use raise in the body of that callback. This applies to the aliases of bind as well: ( >>= ) and ( let* ).
Use Lwt.fail only when you specifically want to create a rejected promise, to pass to another function, or store in a data structure.
Lwt.bind p_1 f makes it so that f will run when p_1 is fulfilled.
When p_1 is fulfilled with value v_1, the callback f is called with that same value v_1. Eventually, after perhaps starting some I/O or other computation, f returns promise p_2.
Lwt.bind itself returns immediately. It only attaches the callback f to p_1 – it does not wait for p_2. What Lwt.bind returns is yet a third promise, p_3. Roughly speaking, fulfillment of p_3 represents both p_1 and p_2 becoming fulfilled, one after the other.
A minimal example of this is an echo program:
let () =
+ let p_3 =
+ Lwt.bind
+ Lwt_io.(read_line stdin)
+ (fun line -> Lwt_io.printl line)
+ in
+ Lwt_main.run p_3
+
+(* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)Rejection of p_1 and p_2, and raising an exception in f, are all forwarded to rejection of p_3.
Precise behavior
Lwt.bind returns a promise p_3 immediately. p_3 starts out pending, and is resolved as follows:
p_1 becomes resolved. It does not matter whether p_1 is already resolved when Lwt.bind is called, or becomes resolved later – the rest of the behavior is the same.p_1 becomes resolved, it will, by definition, be either fulfilled or rejected.p_1 is rejected, p_3 is rejected with the same exception.p_1 is fulfilled, with value v, f is applied to v.f may finish by returning the promise p_2, or raising an exception.f raises an exception, p_3 is rejected with that exception.f returns p_2. From that point on, p_3 is effectively made into a reference to p_2. This means they have the same state, undergo the same state changes, and performing any operation on one is equivalent to performing it on the other.Syntactic sugar
Lwt.bind is almost never written directly, because sequences of Lwt.bind result in growing indentation and many parentheses:
let () =
+ Lwt_main.run begin
+ Lwt.bind Lwt_io.(read_line stdin) (fun line ->
+ Lwt.bind (Lwt_unix.sleep 1.) (fun () ->
+ Lwt_io.printf "One second ago, you entered %s\n" line))
+ end
+
+(* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)The recommended way to write Lwt.bind is using the let%lwt syntactic sugar:
let () =
+ Lwt_main.run begin
+ let%lwt line = Lwt_io.(read_line stdin) in
+ let%lwt () = Lwt_unix.sleep 1. in
+ Lwt_io.printf "One second ago, you entered %s\n" line
+ end
+
+(* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)This uses the Lwt PPX (preprocessor). Note that we had to add package lwt_ppx to the command line for building this program. We will do that throughout this manual.
Another way to write Lwt.bind, that you may encounter while reading code, is with the >>= operator:
open Lwt.Infix
+
+let () =
+ Lwt_main.run begin
+ Lwt_io.(read_line stdin) >>= fun line ->
+ Lwt_unix.sleep 1. >>= fun () ->
+ Lwt_io.printf "One second ago, you entered %s\n" line
+ end
+
+(* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)The >>= operator comes from the module Lwt.Infix, which is why we opened it at the beginning of the program.
See also Lwt.map.
reraise e raises the exception e. Unlike raise e, reraise e preserves the existing exception backtrace and even adds a "Re-raised at" entry with the call location.
This function is intended to be used in the exception handlers of Lwt.catch and Lwt.try_bind.
It is also used in the code produced by Lwt_ppx.
Lwt.catch f h applies f (), which returns a promise, and then makes it so that h (“handler”) will run when that promise is rejected.
let () =
+ Lwt_main.run begin
+ Lwt.catch
+ (fun () -> raise Exit)
+ (function
+ | Exit -> Lwt_io.printl "Got Stdlib.Exit"
+ | exn -> Lwt.reraise exn)
+ end
+
+(* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)Despite the above code, the recommended way to write Lwt.catch is using the try%lwt syntactic sugar from the PPX. Here is an equivalent example:
let () =
+ Lwt_main.run begin
+ try%lwt raise Exit
+ with Exit -> Lwt_io.printl "Got Stdlb.Exit"
+ end
+
+(* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)A particular advantage of the PPX syntax is that it is not necessary to artificially insert a catch-all exn -> reraise exn case. Like in the core language's try expression, the catch-all case is implied in try%lwt.
Lwt.catch is a counterpart to Lwt.bind – Lwt.bind is for fulfillment, and Lwt.catch is for rejection.
As with Lwt.bind, three promises are involved:
p_1, the promise returned from applying f ().p_2, the promise returned from applying h exn.p_3, the promise returned by Lwt.catch itself.The remainder is (1) a precise description of how p_3 is resolved, and (2) a warning about accidentally using ordinary try for exception handling in asynchronous code.
(1) Lwt.catch first applies f (). It then returns p_3 immediately. p_3 starts out pending. It is resolved as follows:
f () returned a promise p_1, and p_1 becomes fulfilled, p_3 is fulfilled with the same value.p_1 can instead become rejected. There is one other possibility: f () itself raised an exception, instead of returning a promise. The behavior of Lwt.catch is the same whether f () raised an exception, or returned a promise that is later rejected with an exception. Let's call the exception exn.h exn is applied.h exn may return a promise, or might itself raise an exception. The first case is the interesting one, but the exception case is simple, so we cover the exception case first.h exn raises another exception exn', p_3 is rejected with exn'.h exn instead returns the promise p_2, p_3 is effectively made into a reference to p_2. This means p_3 and p_2 have the same state, undergo the same state changes, and performing any operation one is equivalent to performing it on the other.Lwt.finalize f c applies f (), which returns a promise, and then makes it so c (“cleanup”) will run when that promise is resolved.
In other words, c runs no matter whether promise f () is fulfilled or rejected. As the names suggest, Lwt.finalize corresponds to the finally construct found in many programming languages, and c is typically used for cleaning up resources:
let () =
+ Lwt_main.run begin
+ let%lwt file = Lwt_io.(open_file ~mode:Input "code.ml") in
+ Lwt.finalize
+ (fun () ->
+ let%lwt content = Lwt_io.read file in
+ Lwt_io.print content)
+ (fun () ->
+ Lwt_io.close file)
+ end
+
+(* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)As with Lwt.bind and Lwt.catch, there is a syntactic sugar for Lwt.finalize, though it is not as often used:
let () =
+ Lwt_main.run begin
+ let%lwt file = Lwt_io.(open_file ~mode:Input "code.ml") in
+ begin
+ let%lwt content = Lwt_io.read file in
+ Lwt_io.print content
+ end
+ [%lwt.finally
+ Lwt_io.close file]
+ end
+
+(* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)Also as with Lwt.bind and Lwt.catch, three promises are involved:
p_1, the promise returned from applying f ().p_2, the promise returned from applying c ().p_3, the promise returned by Lwt.finalize itself.p_3 is returned immediately. It starts out pending, and is resolved as follows:
f () is applied. If it finishes, it will either return a promise p_1, or raise an exception.f () raises an exception, p_1 is created artificially as a promise rejected with that exception. So, no matter how f () finishes, there is a promise p_1 representing the outcome.p_1 is resolved (fulfilled or rejected), c () is applied. This is meant to be the cleanup code.c () finishes, it will also either return a promise, p_2, or raise an exception.c () raises an exception, p_2 is created artificially as a promise rejected with that exception. Again, no matter how c () finishes, there is a promise p_2 representing the outcome of cleanup.p_2 is fulfilled, p_3 is resolved the same way p_1 had been resolved. In other words, p_1 is forwarded to p_3 when cleanup is successful.p_2 is rejected, p_3 is rejected with the same exception. In other words, p_2 is forwarded to p_3 when cleanup is unsuccessful. Note this means that if both the protected code and the cleanup fail, the cleanup exception has precedence.Lwt.try_bind f g h applies f (), and then makes it so that:
Lwt.try_bind is a generalized Lwt.finalize. The difference is that Lwt.try_bind runs different callbacks depending on how f () is resolved. This has two main implications:
g and h each “know” whether f () was fulfilled or rejected.g and h are passed the value f () was fulfilled with, and, respectively, the exception f () was rejected with.As with Lwt.catch, it is recommended to use reraise in the catch-all case of the exception handler:
let () =
+ Lwt_main.run begin
+ Lwt.try_bind
+ (fun () -> raise Exit)
+ (fun () -> Lwt_io.printl "Got Success")
+ (function
+ | Exit -> Lwt_io.printl "Got Stdlib.Exit"
+ | exn -> Lwt.reraise exn)
+ end
+
+(* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)The rest is a detailed description of the promises involved.
As with Lwt.finalize and the several preceding functions, three promises are involved.
p_1 is the promise returned from applying f ().p_2 is the promise returned from applying h or g, depending on which one is chosen.p_3 is the promise returned by Lwt.try_bind itself.Lwt.try_bind returns p_3 immediately. p_3 starts out pending, and is resolved as follows:
f () is applied. If it finishes, it either returns p_1, or raises an exception.f () raises an exception, p_1 is created artificially as a promise rejected with that exception. So, no matter how f () finishes, there is a promise p_1 representing the outcome.p_1 is fulfilled, g is applied to the value p_1 is fulfilled with.p_1 is rejected, h is applied to the exception p_1 is rejected with.p_2, or raises an exception.p_3 is rejected with that exception.p_2, p_3 is effectively made into an reference to p_2. They have the same state, including any state changes, and performing any operation on one is equivalent to performing it on the other.val dont_wait : (unit -> unit t) -> (exn -> unit) -> unitLwt.dont_wait f handler applies f (), which returns a promise, and then makes it so that if the promise is rejected, the exception is passed to handler.
In addition, if f () raises an exception, it is also passed to handler.
As the name implies, dont_wait (fun () -> <e>) handler is a way to evaluate the expression <e> (which typically has asynchronous side-effects) without waiting for the resolution of the promise <e> evaluates to.
dont_wait is meant as an alternative to async with a local, explicit, predictable exception handler.
Note that dont_wait f h causes f () to be evaluated immediately. Consequently, the non-yielding/non-pausing prefix of the body of f is evaluated immediately.
val async : (unit -> unit t) -> unitLwt.async f applies f (), which returns a promise, and then makes it so that if the promise is rejected, the exception is passed to !Lwt.async_exception_hook.
In addition, if f () raises an exception, it is also passed to !Lwt.async_exception_hook.
!Lwt.async_exception_hook typically prints an error message and terminates the program. If you need a similar behaviour with a different exception handler, you can use Lwt.dont_wait.
Lwt.async is misleadingly named. Itself, it has nothing to do with asynchronous execution. It's actually a safety function for making Lwt programs more debuggable.
For example, take this program, which prints messages in a loop, while waiting for one line of user input:
let () =
+ let rec show_nag () : _ Lwt.t =
+ let%lwt () = Lwt_io.printl "Please enter a line" in
+ let%lwt () = Lwt_unix.sleep 1. in
+ show_nag ()
+ in
+ ignore (show_nag ()); (* Bad – see note for (1)! *)
+
+ Lwt_main.run begin
+ let%lwt line = Lwt_io.(read_line stdin) in
+ Lwt_io.printl line
+ end
+
+(* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)If one of the I/O operations in show_nag were to fail, the promise representing the whole loop would get rejected. However, since we are ignoring that promise at (1), we never find out about the rejection. If this failure and resulting rejection represents a bug in the program, we have a harder time finding out about the bug.
A safer version differs only in using Lwt.async instead of Stdlib.ignore:
let () =
+ let rec show_nag () : _ Lwt.t =
+ let%lwt () = Lwt_io.printl "Please enter a line" in
+ let%lwt () = Lwt_unix.sleep 1. in
+ show_nag ()
+ in
+ Lwt.async (fun () -> show_nag ());
+
+ Lwt_main.run begin
+ let%lwt line = Lwt_io.(read_line stdin) in
+ Lwt_io.printl line
+ end
+
+(* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)In this version, if I/O in show_nag fails with an exception, the exception is printed by Lwt.async, and then the program exits.
The general rule for when to use Lwt.async is:
Lwt.bind, Lwt.catch, Lwt.join, etc., are top-level promises.Lwt_main.run, as can be seen in most examples in this manual.Lwt.async.val async_exception_hook : (exn -> unit) refReference to a function, to be called on an "unhandled" exception.
This reference is used by Lwt.async, Lwt.on_cancel, Lwt.on_success, Lwt.on_failure, Lwt.on_termination, Lwt.on_any, Lwt_react.of_stream, and the deprecated Lwt.ignore_result.
The initial, default implementation prints the exception, then terminates the process with non-zero exit status, as if the exception had reached the top level of the program:
let () = Lwt.async (fun () -> raise Exit)
+
+(* ocamlfind opt -linkpkg -package lwt code.ml && ./a.out *)produces in the output:
Fatal error: exception Stdlib.Exit
If you are writing an application, you are welcome to reassign the reference, and replace the function with something more appropriate for your needs.
If you are writing a library, you should leave this reference alone. Its behavior should be determined by the application.
Lwt.both p_1 p_2 returns a promise that is pending until both promises p_1 and p_2 become resolved.
let () =
+ let p_1 =
+ let%lwt () = Lwt_unix.sleep 3. in
+ Lwt_io.printl "Three seconds elapsed"
+ in
+
+ let p_2 =
+ let%lwt () = Lwt_unix.sleep 5. in
+ Lwt_io.printl "Five seconds elapsed"
+ in
+
+ let p_3 = Lwt.both p_1 p_2 in
+ Lwt_main.run p_3
+
+(* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)If both p_1 and p_2 become fulfilled, Lwt.both p_1 p_2 is also fulfilled, with the pair of their final values. Otherwise, if at least one of the two promises becomes rejected, Lwt.both p_1 p_2 is rejected with the same exception as one such promise, chosen arbitrarily. Note that this occurs only after both promises are resolved, not immediately when the first promise is rejected.
Lwt.join ps returns a promise that is pending until all promises in the list ps become resolved.
let () =
+ let p_1 =
+ let%lwt () = Lwt_unix.sleep 3. in
+ Lwt_io.printl "Three seconds elapsed"
+ in
+
+ let p_2 =
+ let%lwt () = Lwt_unix.sleep 5. in
+ Lwt_io.printl "Five seconds elapsed"
+ in
+
+ let p_3 = Lwt.join [p_1; p_2] in
+ Lwt_main.run p_3
+
+(* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)If all of the promises in ps become fulfilled, Lwt.join ps is also fulfilled. Otherwise, if at least one promise in ps becomes rejected, Lwt.join ps is rejected with the same exception as one such promise, chosen arbitrarily. Note that this occurs only after all the promises are resolved, not immediately when the first promise is rejected.
Lwt.all ps is like Lwt.join ps: it waits for all promises in the list ps to become resolved.
It then resolves the returned promise with the list of all resulting values.
Note that if any of the promises in ps is rejected, the returned promise is also rejected. This means that none of the values will be available, even if some of the promises in ps were already resolved when one of them is rejected. For more fine-grained handling of rejection, structure the program with Lwt_stream or Lwt_list, handle rejections explicitly, or use Lwt.join and collect values manually.
Lwt.pick ps returns a promise that is pending until one promise in the list ps becomes resolved.
When at least one promise in ps is resolved, Lwt.pick tries to cancel all other promises that are still pending, using Lwt.cancel.
let () =
+ let echo =
+ let%lwt line = Lwt_io.(read_line stdin) in
+ Lwt_io.printl line
+ in
+
+ let timeout = Lwt_unix.sleep 5. in
+
+ Lwt_main.run (Lwt.pick [echo; timeout])
+
+(* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)If the first promise in ps to become resolved is fulfilled, the result promise p is also fulfilled, with the same value. Likewise, if the first promise in ps to become resolved is rejected, p is rejected with the same exception.
If ps has no promises (if it is the empty list), Lwt.pick ps raises Stdlib.Invalid_argument _.
It's possible for multiple promises in ps to become resolved simultaneously. This happens most often when some promises ps are already resolved at the time Lwt.pick is called.
In that case, if at least one of the promises is rejected, the result promise p is rejected with the same exception as one such promise, chosen arbitrarily. If all promises are fulfilled, p is fulfilled with the value of one of the promises, also chosen arbitrarily.
The remaining functions in this section are variations on Lwt.pick.
Lwt.choose ps is the same as Lwt.pick ps, except that it does not try to cancel pending promises in ps.
Lwt.npick ps is similar to Lwt.pick ps, the difference being that when multiple promises in ps are fulfilled simultaneously (and none are rejected), the result promise is fulfilled with the list of values the promises were fulfilled with.
When at least one promise is rejected, Lwt.npick still rejects the result promise with the same exception.
Lwt.nchoose ps is the same as Lwt.npick ps, except that it does not try to cancel pending promises in ps.
Lwt.nchoose_split ps is the same as Lwt.nchoose ps, except that when multiple promises in ps are fulfilled simultaneously (and none are rejected), the result promise is fulfilled with both the list of values of the fulfilled promises, and the list of promises that are still pending.
Note: cancelation has proved difficult to understand, explain, and maintain, so use of these functions is discouraged in new code. See ocsigen/lwt#283.
Canceled promises are those rejected with this exception, Lwt.Canceled. See Lwt.cancel.
Lwt.task is the same as Lwt.wait, except the resulting promise p is cancelable.
This is significant, because it means promises created by Lwt.task can be resolved (specifically, rejected) by canceling them directly, in addition to being resolved through their paired resolvers.
In contrast, promises returned by Lwt.wait can only be resolved through their resolvers.
val cancel : _ t -> unitLwt.cancel p attempts to cancel the pending promise p, without needing access to its resolver.
It is recommended to avoid Lwt.cancel, and handle cancelation by tracking the needed extra state explicitly within your library or application.
A canceled promise is one that has been rejected with exception Lwt.Canceled.
There are straightforward ways to make promises canceled. One could create a promise that starts out canceled, with Lwt.fail Lwt.Canceled. It's also possible to make a promise canceled through its resolver, by calling Lwt.wakeup_later_exn r Lwt.Canceled.
This function, Lwt.cancel, provides another method, which can cancel pending promises without going through their resolvers – it acts directly on promises.
Like any other promise rejection, the canceled state of a promise is propagated “forwards” by Lwt.bind, Lwt.join, etc., as described in the documentation of those functions.
Cancellation is a separate phase, triggered only by Lwt.cancel, that searches backwards, strating from p, for promises to reject with Lwt.Canceled. Once those promises are found, they are canceled, and then ordinary, forwards rejection propagation takes over.
All of this will be made precise, but first let's have an example:
let () =
+ let p =
+ let%lwt () = Lwt_unix.sleep 5. in
+ Lwt_io.printl "Slept five seconds"
+ in
+
+ Lwt.cancel p;
+
+ Lwt_main.run p
+
+(* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)At the time Lwt.cancel is called, p “depends” on the sleep promise (the printl is not yet called, so its promise hasn't been created).
So, Lwt.cancel recursively tries to cancel the sleep promise. That is an example of the backwards search. The sleep promise is a pending promise that doesn't depend on anything, so backwards search stops at it. The state of the sleep promise is set to rejected with Lwt.Canceled.
Lwt.bind then propagates the rejection forwards to p, so p also becomes canceled.
Eventually, this rejection reaches Lwt_main.run, which raises the Lwt.Canceled as an ordinary exception. The sleep does not complete, and the printl is never started.
Promises, like the sleep promise above, that can be rejected by Lwt.cancel are cancelable. Most promises in Lwt are either cancelable, or depend on cancelable promises. The functions Lwt.wait and Lwt.no_cancel create promises that are not cancelable.
The rest is a detailed description of how the Lwt.cancel backwards search works.
p is already resolved, Lwt.cancel does nothing.p was created by Lwt.wait or Lwt.no_cancel, Lwt.cancel does nothing.p was created by Lwt.task or Lwt.protected, Lwt.cancel rejects it with Lwt.Canceled. This rejection then propagates normally through any Lwt calls that depend on p. Most I/O promises are internally created by calling Lwt.task.p_3 was returned by Lwt.bind, Lwt.map, Lwt.catch, Lwt.finalize, or Lwt.try_bind. Then, see those functions for the naming of the other promises involved. If p_3 is pending, then either p_1 is pending, or p_2 is pending. Lwt.cancel p_3 then tries recursively to cancel whichever of these two is still pending. If that succeeds, p_3 may be canceled later by the normal propagation of rejection.p was returned by Lwt.join, Lwt.pick, or similar function, which was applied to the promise list ps. Lwt.cancel then recursively tries to cancel each promise in ps. If one of those cancellations succeeds, p may be canceled later by the normal propagation of rejection.val on_cancel : _ t -> (unit -> unit) -> unitLwt.on_cancel p f makes it so that f will run when p becomes canceled.
Callbacks scheduled with on_cancel are guaranteed to run before any other callbacks that are triggered by rejection, such as those added by Lwt.catch.
Note that this does not interact directly with the cancellation mechanism, the backwards search described in Lwt.cancel. For example, manually rejecting a promise with Lwt.Canceled is sufficient to trigger f.
f should not raise exceptions. If it does, they are passed to !Lwt.async_exception_hook, which terminates the process by default.
Lwt.protected p creates a cancelable promise p'. The original state of p' is the same as the state of p at the time of the call.
The state of p' can change in one of two ways: a. if p changes state (i.e., is resolved), then p' eventually changes state to match p's, and b. during cancellation, if the backwards search described in Lwt.cancel reaches p' then it changes state to rejected Canceled and the search stops.
As a consequence of the b. case, Lwt.cancel (protected p) does not cancel p.
The promise p can still be canceled either directly (through Lwt.cancel p) or being reached by the backwards cancellation search via another path. Lwt.protected only prevents cancellation of p through p'.
Lwt.no_cancel p creates a non-cancelable promise p'. The original state of p' is the same as p at the time of the call.
If the state of p changes, then the state of p' eventually changes too to match p's.
Note that even though p' is non-cancelable, it can still become canceled if p is canceled. Lwt.no_cancel only prevents cancellation of p and p' through p'.
Lwt.wrap_in_cancelable p creates a cancelable promise p'. The original state of p' is the same as p.
The state of p' can change in one of two ways: a. if p changes state (i.e., is resolved), then p' eventually changes state to match p's, and b. during cancellation, if the backwards search described in Lwt.cancel reaches p' then it changes state to rejected Canceled and the search continues to p.
The primitives protected, no_cancel, and wrap_in_cancelable give you some level of control over the cancellation mechanism of Lwt. Note that promises passed as arguments to either of these three functions are unchanged. The functions return new promises with a specific cancellation behaviour.
The three behaviour of all three functions are summarised in the following table.
+----------------------------+--------------------+--------------------+
+| setup - action | cancel p | cancel p' |
++----------------------------+--------------------+--------------------+
+| p is cancelable | p is canceled | p is not canceled |
+| p' = protected p | p' is canceled | p' is canceled |
++----------------------------+--------------------+--------------------+
+| p is not cancelable | p is not canceled | p is not canceled |
+| p' = protected p | p' is not canceled | p' is canceled |
++----------------------------+--------------------+--------------------+
+| p is cancelable | p is canceled | p is not canceled |
+| p' = no_cancel p | p' is canceled | p' is not canceled |
++----------------------------+--------------------+--------------------+
+| p is not cancelable | p is not canceled | p is not canceled |
+| p' = no_cancel p | p' is not canceled | p' is not canceled |
++----------------------------+--------------------+--------------------+
+| p is cancelable | p is canceled | p is canceled |
+| p' = wrap_in_cancelable p | p' is canceled | p' is canceled |
++----------------------------+--------------------+--------------------+
+| p is not cancelable | p is not canceled | p is not canceled |
+| p' = wrap_in_cancelable p | p' is not canceled | p' is canceled |
++----------------------------+--------------------+--------------------+Lwt.map f p_1 is similar to Lwt.bind p_1 f, but f is not expected to return a promise.
This function is more convenient than Lwt.bind when f inherently does not return a promise. An example is Stdlib.int_of_string:
let read_int : unit -> int Lwt.t = fun () ->
+ Lwt.map
+ int_of_string
+ Lwt_io.(read_line stdin)
+
+let () =
+ Lwt_main.run begin
+ let%lwt number = read_int () in
+ Lwt_io.printf "%i\n" number
+ end
+
+(* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)By comparison, the Lwt.bind version is more awkward:
let read_int : unit -> int Lwt.t = fun () ->
+ Lwt.bind
+ Lwt_io.(read_line stdin)
+ (fun line -> Lwt.return (int_of_string line))As with Lwt.bind, sequences of calls to Lwt.map result in excessive indentation and parentheses. The recommended syntactic sugar for avoiding this is the >|= operator, which comes from module Lwt.Infix:
open Lwt.Infix
+
+let read_int : unit -> int Lwt.t = fun () ->
+ Lwt_io.(read_line stdin) >|= int_of_stringThe detailed operation follows. For consistency with the promises in Lwt.bind, the two promises involved are named p_1 and p_3:
p_1 is the promise passed to Lwt.map.p_3 is the promise returned by Lwt.map.Lwt.map returns a promise p_3. p_3 starts out pending. It is resolved as follows:
p_1 may be, or become, resolved. In that case, by definition, it will become fulfilled or rejected. Fulfillment is the interesting case, but the behavior on rejection is simpler, so we focus on rejection first.p_1 becomes rejected, p_3 is rejected with the same exception.p_1 instead becomes fulfilled, call the value it is fulfilled with v.f v is applied. If this finishes, it may either return another value, or raise an exception.f v returns another value v', p_3 is fulfilled with v'.f v raises exception exn, p_3 is rejected with exn.val on_success : 'a t -> ('a -> unit) -> unitLwt.on_success p f makes it so that f will run when p is fulfilled.
It is similar to Lwt.bind, except no new promises are created. f is a plain, arbitrary function attached to p, to perform some side effect.
If f raises an exception, it is passed to !Lwt.async_exception_hook. By default, this will terminate the process.
val on_failure : _ t -> (exn -> unit) -> unitLwt.on_failure p f makes it so that f will run when p is rejected.
It is similar to Lwt.catch, except no new promises are created.
If f raises an exception, it is passed to !Lwt.async_exception_hook. By default, this will terminate the process.
val on_termination : _ t -> (unit -> unit) -> unitLwt.on_termination p f makes it so that f will run when p is resolved – that is, fulfilled or rejected.
It is similar to Lwt.finalize, except no new promises are created.
If f raises an exception, it is passed to !Lwt.async_exception_hook. By default, this will terminate the process.
val on_any : 'a t -> ('a -> unit) -> (exn -> unit) -> unitLwt.on_any p f g makes it so that:
It is similar to Lwt.try_bind, except no new promises are created.
If f or g raise an exception, the exception is passed to !Lwt.async_exception_hook. By default, this will terminate the process.
module Infix : sig ... endThis module provides several infix operators for making programming with Lwt more convenient.
module Let_syntax : sig ... endmodule Syntax : sig ... endval return_unit : unit tLwt.return_unit is defined as Lwt.return (), but this definition is evaluated only once, during initialization of module Lwt, at the beginning of your program.
This means the promise is allocated only once. By contrast, each time Lwt.return () is evaluated, it allocates a new promise.
It is recommended to use Lwt.return_unit only where you know the allocations caused by an instance of Lwt.return () are a performance bottleneck. Generally, the cost of I/O tends to dominate the cost of Lwt.return () anyway.
In future Lwt, we hope to perform this optimization, of using a single, pre-allocated promise, automatically, wherever Lwt.return () is written.
val return_none : _ option tLwt.return_none is like Lwt.return_unit, but for Lwt.return None.
val return_nil : _ list tLwt.return_nil is like Lwt.return_unit, but for Lwt.return [].
val return_true : bool tLwt.return_true is like Lwt.return_unit, but for Lwt.return true.
val return_false : bool tLwt.return_false is like Lwt.return_unit, but for Lwt.return false.
val return_some : 'a -> 'a option tCounterpart to Lwt.return_none. However, unlike Lwt.return_none, this function performs no optimization. This is because it takes an argument, so it cannot be evaluated at initialization time, at which time the argument is not yet available.
Like Lwt.return_some, this function performs no optimization.
Like Lwt.return_some, this function performs no optimization.
val fail_with : string -> _ tLwt.fail_with s is an abbreviation for
Lwt.fail (Stdlib.Failure s)In most cases, it is better to use failwith s from the standard library. See Lwt.fail for an explanation.
val fail_invalid_arg : string -> _ tLwt.invalid_arg s is an abbreviation for
Lwt.fail (Stdlib.Invalid_argument s)In most cases, it is better to use invalid_arg s from the standard library. See Lwt.fail for an explanation.
A resolved promise of type 'a Lwt.t is either fulfilled with a value of type 'a, or rejected with an exception.
This corresponds to the cases of a ('a, exn)Stdlib.result: fulfilled corresponds to Ok of 'a, and rejected corresponds to Error of exn.
For Lwt programming with result where the Error constructor can carry arbitrary error types, see module Lwt_result.
Lwt.of_result r converts an r to a resolved promise.
r is Ok v, Lwt.of_result r is Lwt.return v, i.e. a promise fulfilled with v.r is Error exn, Lwt.of_result r is Lwt.fail exn, i.e. a promise rejected with exn.Lwt.wakeup_later_result r result resolves the pending promise p associated to resolver r, according to result:
result is Ok v, p is fulfilled with v.result is Error exn, p is rejected with exn.If p is not pending, Lwt.wakeup_later_result raises Stdlib.Invalid_argument _, except if p is canceled. If p is canceled, Lwt.wakeup_later_result has no effect.
Using this mechanism is discouraged, because it is non-syntactic, and because it manipulates hidden state in module Lwt. It is recommended instead to pass additional values explicitly in tuples, or maintain explicit associative maps for them.
Keys into the implicit callback argument map, for implicit arguments of type 'a option.
The keys are abstract, but they are basically integers that are all distinct from each other.
See Lwt.with_value.
val new_key : unit -> 'a keyCreates a fresh implicit callback argument key.
The key is distinct from any other key created by the current process. The value None of type 'a option is immediately associated with the key.
See Lwt.with_value.
val get : 'a key -> 'a optionRetrieves the value currently associated with the given implicit callback argument key.
See Lwt.with_value.
val with_value : 'a key -> 'a option -> (unit -> 'b) -> 'bLwt.with_value k v f sets k to v in Lwt's internal implicit callback argument map, then runs f (), then restores the previous value associated with k.
Lwt maintains a single, global map, that can be used to “pass” extra arguments to callbacks:
let () =
+ let k : string Lwt.key = Lwt.new_key () in
+
+ let say_hello () =
+ match Lwt.get k with
+ | None -> assert false
+ | Some s -> Lwt_io.printl s
+ in
+
+ Lwt_main.run begin
+ Lwt.with_value k (Some "Hello world!") begin fun () ->
+ Lwt.bind
+ (Lwt_unix.sleep 1.)
+ (fun () -> say_hello ())
+ end
+ end
+
+(* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)Note that the string Hello world! was passed to say_hello through the key k. Meanwhile, the only explicit argument of the callback say_hello is ().
The way this works is functions like Lwt.bind take a snapshot of the implicit argument map. Later, right before the callback is run, the map is restored to that snapshot. In other words, the map has the same state inside the callback as it did at the time the callback was registered.
To be more precise:
Lwt.with_value associates Some "Hello world!" with k, and runs the function passed to it.Lwt.bind.Lwt_unix.sleep 1. promise is created.Lwt.bind then attaches the callback in its second argument, the one which calls say_hello, to that sleep promise.Lwt.bind also takes a snapshot of the current state of the implicit argument map, and pairs the callback with that snapshot.sleep promise will be resolved.Lwt.bind returns its result promise p_3. This causes Lwt.with_value to also return p_3, first restoring k to be associated with None.Lwt_main.run gets the pending p_3, and blocks the whole process, with k associated with None.sleep I/O completes, resolving the sleep promise.say_hello callback. Right before the callback is called, the implicit argument map is restored to its snapshot, so k is associated with Some "Hello world!".k to be associated with None.The Lwt functions that take snapshots of the implicit callback argument map are exactly those which attach callbacks to promises: Lwt.bind and its variants >>= and let%lwt, Lwt.map and its variant >|=, Lwt.catch and its variant try%lwt, Lwt.finalize and its variant %lwt.finally, Lwt.try_bind, Lwt.on_success, Lwt.on_failure, Lwt.on_termination, and Lwt.on_any.
Lwt.with_value should only be called in the main thread, i.e. do not call it inside Lwt_preemptive.detach.
val wakeup : 'a u -> 'a -> unitLwt.wakeup r v is like Lwt.wakeup_later r v, except it guarantees that callbacks associated with r will be called immediately, deeper on the current stack.
In contrast, Lwt.wakeup_later may call callbacks immediately, or may queue them for execution on a shallower stack – though still before the next time Lwt blocks the process on I/O.
Using this function is discouraged, because calling it in a loop can exhaust the stack. The loop might be difficult to detect or predict, due to combined mutually-recursive calls between multiple modules and libraries.
Also, trying to use this function to guarantee the timing of callback calls for synchronization purposes is discouraged. This synchronization effect is obscure to readers. It is better to use explicit promises, or Lwt_mutex, Lwt_condition, and/or Lwt_mvar.
val wakeup_exn : _ u -> exn -> unitLwt.wakeup_exn r exn is like Lwt.wakeup_later_exn r exn, but has the same problems as Lwt.wakeup.
Lwt.wakeup_result r result is like Lwt.wakeup_later_result r result, but has the same problems as Lwt.wakeup.
val add_task_r : 'a u Lwt_sequence.t -> 'a tLwt.add_task_r sequence is equivalent to
let p, r = Lwt.task () in
+let node = Lwt_sequence.add_r r sequence in
+Lwt.on_cancel p (fun () -> Lwt_sequence.remove node);
+pval add_task_l : 'a u Lwt_sequence.t -> 'a tLike Lwt.add_task_r, but the equivalent code calls Lwt_sequence.add_l instead.
val pause : unit -> unit tLwt.pause () creates a pending promise that is fulfilled after Lwt finishes calling all currently ready callbacks, i.e. it is fulfilled on the next “tick.”
Putting the rest of your computation into a callback of Lwt.pause () creates a “yield” that gives other callbacks a chance to run first.
For example, to break up a long-running computation, allowing I/O to be handled between chunks:
let () =
+ let rec handle_io () =
+ let%lwt () = Lwt_io.printl "Handling I/O" in
+ let%lwt () = Lwt_unix.sleep 0.1 in
+ handle_io ()
+ in
+
+ let rec compute n =
+ if n = 0 then
+ Lwt.return ()
+ else
+ let%lwt () =
+ if n mod 1_000_000 = 0 then
+ Lwt.pause ()
+ else
+ Lwt.return ()
+ in
+ compute (n - 1)
+ in
+
+ Lwt.async handle_io;
+ Lwt_main.run (compute 100_000_000)
+
+(* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)If you replace the call to Lwt.pause by Lwt.return in the program above, "Handling I/O" is printed only once. With Lwt.pause, it is printed several times, depending on the speed of your machine.
An alternative way to handle long-running computations is to detach them to preemptive threads using Lwt_preemptive.
val wrap : (unit -> 'a) -> 'a tLwt.wrap f applies f (). If f () returns a value v, Lwt.wrap returns Lwt.return v. If f () raises an exception exn, Lwt.wrap returns Lwt.fail exn.
val wrap1 : ('a -> 'b) -> 'a -> 'b tval wrap2 : ('a -> 'b -> 'c) -> 'a -> 'b -> 'c tval wrap3 : ('a -> 'b -> 'c -> 'd) -> 'a -> 'b -> 'c -> 'd tval wrap4 : ('a -> 'b -> 'c -> 'd -> 'e) -> 'a -> 'b -> 'c -> 'd -> 'e tval wrap5 :
+ ('a -> 'b -> 'c -> 'd -> 'e -> 'f) ->
+ 'a ->
+ 'b ->
+ 'c ->
+ 'd ->
+ 'e ->
+ 'f tval wrap6 :
+ ('a -> 'b -> 'c -> 'd -> 'e -> 'f -> 'g) ->
+ 'a ->
+ 'b ->
+ 'c ->
+ 'd ->
+ 'e ->
+ 'f ->
+ 'g tval wrap7 :
+ ('a -> 'b -> 'c -> 'd -> 'e -> 'f -> 'g -> 'h) ->
+ 'a ->
+ 'b ->
+ 'c ->
+ 'd ->
+ 'e ->
+ 'f ->
+ 'g ->
+ 'h tAs a “prototype,” Lwt_wrap1 f creates a promise-valued function g:
let g v =
+ try
+ let v' = f v in
+ Lwt.return v'
+ with exn ->
+ Lwt.fail exnThe remainder of the functions work analogously – they just work on f with larger numbers of arguments.
Note that there is an important difference to Lwt.wrap. These functions don't run f, nor create the final promise, immediately. In contrast, Lwt.wrap runs its argument f eagerly.
To get a suspended function instead of the eager execution of Lwt.wrap, use Lwt.wrap1.
Use the operators in module Lwt.Infix instead. Using these instances of the operators directly requires opening module Lwt, which brings an excessive number of other names into scope.
val ignore_result : _ t -> unitAn obsolete variant of Lwt.async.
Lwt.ignore_result p behaves as follows:
p is already fulfilled, Lwt.ignore_result p does nothing.p is already rejected with exn, Lwt.ignore_result p raises exn immediately.p is pending, Lwt.ignore_result p does nothing, but if p becomes rejected later, the exception is passed to !Lwt.async_exception_hook.Use of this function is discouraged for two reasons:
p is rejected now or later.Stdlib.ignore, i.e. that it waits for p to become resolved, completing any associated side effects along the way. In fact, the function that does that is ordinary Lwt.bind.Depending on the kind of programs that you write, you may need to treat exceptions thrown by the OCaml runtime (namely Out_of_memory and Stack_overflow) differently than all the other exceptions. This is because (a) these exceptions are not reproducible (in that they are thrown at different points of your program depending on the machine that your program runs on) and (b) recovering from these errors may be impossible.
The helpers below allow you to change the way that Lwt handles the two OCaml runtime exceptions Out_of_memory and Stack_overflow.
module Exception_filter : sig ... endLwt_bytesByte arrays
type t =
+ (char, Stdlib.Bigarray.int8_unsigned_elt, Stdlib.Bigarray.c_layout)
+ Stdlib.Bigarray.Array1.tType of array of bytes.
val create : int -> tCreates a new byte array of the given size.
val length : t -> intReturns the length of the given byte array.
val get : t -> int -> charget buffer offset returns the byte at offset offset in buffer.
val set : t -> int -> char -> unitget buffer offset value changes the value of the byte at offset offset in buffer to value.
val of_bytes : bytes -> tof_bytes buf returns a newly allocated byte array with the same contents as buf.
val of_string : string -> tof_string buf returns a newly allocated byte array with the same contents as buf.
val to_bytes : t -> bytesto_bytes buf returns newly allocated bytes with the same contents as buf.
val to_string : t -> stringto_string buf returns a newly allocated string with the same contents as buf.
blit buf1 ofs1 buf2 ofs2 len copies len bytes from buf1 starting at offset ofs1 to buf2 starting at offset ofs2.
val blit_from_string : string -> int -> t -> int -> int -> unitSame as blit but the first buffer is a String.t instead of a byte array.
val blit_from_bytes : bytes -> int -> t -> int -> int -> unitSame as blit but the first buffer is a Bytes.t instead of a byte array.
val blit_to_bytes : t -> int -> bytes -> int -> int -> unitSame as blit but the second buffer is a Bytes.t instead of a byte array.
val unsafe_blit_from_bytes : bytes -> int -> t -> int -> int -> unitSame as Lwt_bytes.blit_from_bytes but without bounds checking.
val unsafe_blit_from_string : string -> int -> t -> int -> int -> unitSame as Lwt_bytes.blit_from_string but without bounds checking.
val unsafe_blit_to_bytes : t -> int -> bytes -> int -> int -> unitSame as Lwt_bytes.blit_to_bytes but without bounds checking.
proxy buffer offset length creates a ``proxy''. The returned byte array share the data of buffer but with different bounds.
extract buffer offset length creates a new byte array of length length and copy the length bytes of buffer at offset into it.
val fill : t -> int -> int -> char -> unitfill buffer offset length value puts value in all length bytes of buffer starting at offset offset.
The following functions behave similarly to the ones in Lwt_unix, except they use byte arrays instead of Bytes.t, and they never perform extra copies of the data.
val read : Lwt_unix.file_descr -> t -> int -> int -> int Lwt.tval write : Lwt_unix.file_descr -> t -> int -> int -> int Lwt.tval recv :
+ Lwt_unix.file_descr ->
+ t ->
+ int ->
+ int ->
+ Unix.msg_flag list ->
+ int Lwt.tNot implemented on Windows.
val send :
+ Lwt_unix.file_descr ->
+ t ->
+ int ->
+ int ->
+ Unix.msg_flag list ->
+ int Lwt.tNot implemented on Windows.
val recvfrom :
+ Lwt_unix.file_descr ->
+ t ->
+ int ->
+ int ->
+ Unix.msg_flag list ->
+ (int * Unix.sockaddr) Lwt.tNot implemented on Windows.
val sendto :
+ Lwt_unix.file_descr ->
+ t ->
+ int ->
+ int ->
+ Unix.msg_flag list ->
+ Unix.sockaddr ->
+ int Lwt.tNot implemented on Windows.
val recv_msg :
+ socket:Lwt_unix.file_descr ->
+ io_vectors:io_vector list ->
+ (int * Unix.file_descr list) Lwt.tNot implemented on Windows.
val send_msg :
+ socket:Lwt_unix.file_descr ->
+ io_vectors:io_vector list ->
+ fds:Unix.file_descr list ->
+ int Lwt.tNot implemented on Windows.
val map_file :
+ fd:Unix.file_descr ->
+ ?pos:int64 ->
+ shared:bool ->
+ ?size:int ->
+ unit ->
+ tmap_file ~fd ?pos ~shared ?size () maps the file descriptor fd to an array of bytes.
val mapped : t -> boolmapped buffer returns true iff buffer is a memory mapped file.
Type of advise that can be sent to the kernel by the program. See the manual madvise(2) for a description of each.
madvise buffer pos len advice advises the kernel how the program will use the memory mapped file between pos and pos + len.
This call is not available on windows.
val mincore : t -> int -> bool array -> unitmincore buffer offset states tests whether the given pages are in the system memory (the RAM). The offset argument must be a multiple of page_size. states is used to store the result; each cases is true if the corresponding page is in RAM and false otherwise.
This call is not available on windows and cygwin.
Lwt_conditionConditions
Condition variables to synchronize between threads.
Condition variable type. The type parameter denotes the type of value propagated from notifier to waiter.
val create : unit -> 'a tcreate () creates a new condition variable.
val wait : ?mutex:Lwt_mutex.t -> 'a t -> 'a Lwt.twait mutex condvar will cause the current thread to block, awaiting notification for a condition variable, condvar. If provided, the mutex must have been previously locked (within the scope of Lwt_mutex.with_lock, for example) and is temporarily unlocked until the condition is notified. Upon notification, mutex is re-locked before wait returns and the thread's activity is resumed. When the awaited condition is notified, the value parameter passed to signal is returned.
val signal : 'a t -> 'a -> unitsignal condvar value notifies that a condition is ready. A single waiting thread will be awoken and will receive the notification value which will be returned from wait. Note that condition notification is not "sticky", i.e. if there is no waiter when signal is called, the notification will be missed and the value discarded.
val broadcast : 'a t -> 'a -> unitbroadcast condvar value notifies all waiting threads. Each will be awoken in turn and will receive the same notification value.
val broadcast_exn : 'a t -> exn -> unitbroadcast_exn condvar exn fails all waiting threads with exception exn.
Lwt_configinclude module type of struct include Lwt_features endLwt_engine.Ev_backendval default : tval select : tval poll : tval epoll : tval kqueue : tval devpoll : tval port : tval pp : Stdlib.Format.formatter -> t -> unitVersioned.libev_1Old version of Lwt_engine.libev. The current Lwt_engine.libev allows selecting the libev back end.
Versioned.libev_2Since Lwt 3.0.0, this is just an alias for Lwt_engine.libev.
Lwt_engine.Versionedclass libev_1 : object ... endOld version of Lwt_engine.libev. The current Lwt_engine.libev allows selecting the libev back end.
class libev_2 : ?backend:Ev_backend.t -> unit -> object ... endSince Lwt 3.0.0, this is just an alias for Lwt_engine.libev.
Lwt_engine.abstractAbstract class for engines.
method transfer : abstract -> unittransfer engine moves all events from the current engine to engine. Note that timers are reset in the destination engine, i.e. if a timer with a delay of 2 seconds was registered 1 second ago it will occur in 2 seconds in the destination engine.
method on_readable : Unix.file_descr -> (event -> unit) -> eventmethod on_writable : Unix.file_descr -> (event -> unit) -> eventmethod fake_io : Unix.file_descr -> unitNotes:
unit -> unit and not event -> unitmethod private virtual register_readable : Unix.file_descr ->
+ (unit -> unit) ->
+ unit Lazy.tmethod private virtual register_writable : Unix.file_descr ->
+ (unit -> unit) ->
+ unit Lazy.tmethod private virtual register_timer : float ->
+ bool ->
+ (unit -> unit) ->
+ unit Lazy.tLwt_engine.libevEngine based on libev. If not compiled with libev support, the creation of the class will raise Lwt_sys.Not_available.
Lwt_engine.poll_basedAbstract class for engines based on a poll-like function.
inherit tmethod private virtual poll : (Unix.file_descr * bool * bool) list ->
+ float ->
+ (Unix.file_descr * bool * bool) listpoll fds tiomeout, where fds is a list of tuples of the form (fd, check_readable, check_writable), waits for either:
check_readable set to true to become readablecheck_writable set to true to become writableand returns the list of file descriptors with their readable and writable status.
Lwt_engine.selectEngine based on Unix.select.
inherit abstractmethod private register_readable : Unix.file_descr ->
+ (unit -> unit) ->
+ unit Lazy.tmethod private register_writable : Unix.file_descr ->
+ (unit -> unit) ->
+ unit Lazy.tmethod private register_timer : float -> bool -> (unit -> unit) -> unit Lazy.tLwt_engine.select_basedAbstract class for engines based on a select-like function.
inherit tmethod private virtual select : Unix.file_descr list ->
+ Unix.file_descr list ->
+ float ->
+ Unix.file_descr list * Unix.file_descr listselect fds_r fds_w timeout waits for either:
fds_r to become readablefds_w to become writableand returns the list of readable file descriptor and the list of writable file descriptors.
Lwt_engine.tType of engines.
inherit abstractmethod private register_readable : Unix.file_descr ->
+ (unit -> unit) ->
+ unit Lazy.tmethod private register_writable : Unix.file_descr ->
+ (unit -> unit) ->
+ unit Lazy.tmethod private register_timer : float -> bool -> (unit -> unit) -> unit Lazy.tLwt_engineLwt unix main loop engine
Type of events. An event represent a callback registered to be called when some event occurs.
val stop_event : event -> unitstop_event event stops the given event.
val fake_event : eventEvent which does nothing when stopped.
iter block performs one iteration of the main loop. If block is true the function must block until one event becomes available, otherwise it should just check for available events and return immediately.
val on_readable : Unix.file_descr -> (event -> unit) -> eventon_readable fd f calls f each time fd becomes readable.
val on_writable : Unix.file_descr -> (event -> unit) -> eventon_readable fd f calls f each time fd becomes writable.
on_timer delay repeat f calls f one time after delay seconds. If repeat is true then f is called each delay seconds, otherwise it is called only one time.
Returns the number of events waiting for a file descriptor to become readable.
Returns the number of events waiting for a file descriptor to become writable.
val fake_io : Unix.file_descr -> unitSimulates activity on the given file descriptor.
Called internally by Lwt_unix.fork to make sure we don't get strange behaviour
forwards_signal signum is true if the engine will call Lwt_unix.handle_signal when signal signum occurs. In this case, Lwt will not install its own signal handler.
Normally, this just returns false, but when Lwt is used in combination with other IO libraries, this allows sharing e.g. the SIGCHLD handler.
An engine represents a set of functions used to register different kinds of callbacks for different kinds of events.
class virtual abstract : object ... endAbstract class for engines.
class type t = object ... endType of engines.
module Ev_backend : sig ... endType of libev loops.
class libev : ?backend:Ev_backend.t -> unit -> object ... endEngine based on libev. If not compiled with libev support, the creation of the class will raise Lwt_sys.Not_available.
Engine based on Unix.select.
class virtual select_based : object ... endAbstract class for engines based on a select-like function.
class virtual poll_based : object ... endAbstract class for engines based on a poll-like function.
val get : unit -> tget () returns the engine currently in use.
val set : ?transfer:bool -> ?destroy:bool -> t -> unitset ?transfer ?destroy engine replaces the current engine by the given one.
If transfer is true (the default) all events from the current engine are transferred to the new one.
If destroy is true (the default) then the current engine is destroyed before being replaced.
module Versioned : sig ... endLwt_featuresLwt_fmtFormat API for Lwt-powered IOs
This module bridges the gap between Stdlib.Format and Lwt. Although it is not required, it is recommended to use this module with the Fmt library.
Compared to regular formatting function, the main difference is that printing statements will now return promises instead of blocking.
val printf : ('a, Stdlib.Format.formatter, unit, unit Lwt.t) format4 -> 'aReturns a promise that prints on the standard output. Similar to Stdlib.Format.printf.
val eprintf : ('a, Stdlib.Format.formatter, unit, unit Lwt.t) format4 -> 'aReturns a promise that prints on the standard error. Similar to Stdlib.Format.eprintf.
val make_stream : unit -> order Lwt_stream.t * formattermake_stream () returns a formatter and a stream of all the writing order given on that stream.
val of_channel : Lwt_io.output_channel -> formatterof_channel oc creates a formatter that writes to the channel oc.
val stdout : formatterFormatter printing on Lwt_io.stdout.
val stderr : formatterFormatter printing on Lwt_io.stdout.
val make_formatter :
+ commit:(unit -> unit Lwt.t) ->
+ fmt:Stdlib.Format.formatter ->
+ unit ->
+ formattermake_formatter ~commit ~fmt creates a new lwt formatter based on the Stdlib.Format.formatter fmt. The commit function will be called by the printing functions to update the underlying channel.
val get_formatter : formatter -> Stdlib.Format.formatterget_formatter fmt returns the underlying Stdlib.Format.formatter. To access the underlying formatter during printing, it is recommended to use %t and %a.
val fprintf :
+ formatter ->
+ ('a, Stdlib.Format.formatter, unit, unit Lwt.t) format4 ->
+ 'aval kfprintf :
+ (formatter -> unit Lwt.t -> 'a) ->
+ formatter ->
+ ('b, Stdlib.Format.formatter, unit, 'a) format4 ->
+ 'bval ifprintf :
+ formatter ->
+ ('a, Stdlib.Format.formatter, unit, unit Lwt.t) format4 ->
+ 'aval ikfprintf :
+ (formatter -> unit Lwt.t -> 'a) ->
+ formatter ->
+ ('b, Stdlib.Format.formatter, unit, 'a) format4 ->
+ 'bflush fmt flushes the formatter (as with Stdlib.Format.pp_print_flush) and executes all the printing action on the underlying channel.
Low level functions
val write_order : Lwt_io.output_channel -> order -> unit Lwt.twrite_order oc o applies the order o on the channel oc.
Lwt_gcInteraction with the garbage collector
This module offers a convenient way to add a finaliser launching a thread to a value, without having to use Lwt_unix.run in the finaliser.
val finalise : ('a -> unit Lwt.t) -> 'a -> unitfinalise f x ensures f x is evaluated after x has been garbage collected. If f x yields, then Lwt will wait for its termination at the end of the program.
Note that f x is not called at garbage collection time, but later in the main loop.
val finalise_or_exit : ('a -> unit Lwt.t) -> 'a -> unitfinalise_or_exit f x call f x when x is garbage collected or (exclusively) when the program exits.
Lwt_io.BEReading/writing of numbers in big-endian
val read_int : input_channel -> int Lwt.tReads a 32-bits integer as an ocaml int
val read_int16 : input_channel -> int Lwt.tval read_int32 : input_channel -> int32 Lwt.tval read_int64 : input_channel -> int64 Lwt.tval read_float32 : input_channel -> float Lwt.tReads an IEEE single precision floating point value
val read_float64 : input_channel -> float Lwt.tReads an IEEE double precision floating point value
val write_int : output_channel -> int -> unit Lwt.tWrites an ocaml int as a 32-bits integer
val write_int16 : output_channel -> int -> unit Lwt.tval write_int32 : output_channel -> int32 -> unit Lwt.tval write_int64 : output_channel -> int64 -> unit Lwt.tval write_float32 : output_channel -> float -> unit Lwt.tWrites an IEEE single precision floating point value
val write_float64 : output_channel -> float -> unit Lwt.tWrites an IEEE double precision floating point value
Lwt_io.LEReading/writing of numbers in little-endian
val read_int : input_channel -> int Lwt.tReads a 32-bits integer as an ocaml int
val read_int16 : input_channel -> int Lwt.tval read_int32 : input_channel -> int32 Lwt.tval read_int64 : input_channel -> int64 Lwt.tval read_float32 : input_channel -> float Lwt.tReads an IEEE single precision floating point value
val read_float64 : input_channel -> float Lwt.tReads an IEEE double precision floating point value
val write_int : output_channel -> int -> unit Lwt.tWrites an ocaml int as a 32-bits integer
val write_int16 : output_channel -> int -> unit Lwt.tval write_int32 : output_channel -> int32 -> unit Lwt.tval write_int64 : output_channel -> int64 -> unit Lwt.tval write_float32 : output_channel -> float -> unit Lwt.tWrites an IEEE single precision floating point value
val write_float64 : output_channel -> float -> unit Lwt.tWrites an IEEE double precision floating point value
Lwt_io.VersionedVersioned variants of APIs undergoing breaking changes.
val establish_server_1 :
+ ?fd:Lwt_unix.file_descr ->
+ ?buffer_size:int ->
+ ?backlog:int ->
+ Unix.sockaddr ->
+ ((input_channel * output_channel) -> unit) ->
+ serverOld version of Lwt_io.establish_server. The current Lwt_io.establish_server automatically closes channels passed to the callback, and notifies the caller when the server's listening socket is bound.
val establish_server_2 :
+ ?fd:Lwt_unix.file_descr ->
+ ?buffer_size:int ->
+ ?backlog:int ->
+ ?no_close:bool ->
+ Unix.sockaddr ->
+ ((input_channel * output_channel) -> unit Lwt.t) ->
+ server Lwt.tSince Lwt 3.0.0, this is just an alias for Lwt_io.establish_server.
val shutdown_server_1 : server -> unitOld version of Lwt_io.shutdown_server. The current Lwt_io.shutdown_server returns a promise, which resolves when the server's listening socket is closed.
Since Lwt 3.0.0, this is just an alias for Lwt_io.shutdown_server.
Lwt_ioBuffered byte channels
A channel is a high-level object for performing input/output (IO). It allows to read/write from/to the outside world in an efficient way, by minimising the number of system calls.
An output channel is used to send data and an input channel is used to receive data.
If you are familiar with buffered channels you may be familiar too with the flush operation. Note that byte channels of this module are automatically flushed when there is nothing else to do (i.e. before the program becomes idle), so this means that you no longer have to write:
eprintf "log message\n";
+flush stderr;to have your messages displayed.
Note about errors: input functions of this module raise End_of_file when the end-of-file is reached (i.e. when the read function returns 0). Other exceptions are ones caused by the backend read/write functions, such as Unix.Unix_error.
Exception raised when a channel is closed. The parameter is a description of the channel.
val stdin : input_channelThe standard input, it reads data from Lwt_unix.stdin
val stdout : output_channelThe standard output, it writes data to Lwt_unix.stdout
val stderr : output_channelThe standard output for error messages, it writes data to Lwt_unix.stderr
val zero : input_channelInputs which returns always '\x00'
val null : output_channelOutput which drops everything
val pipe :
+ ?cloexec:bool ->
+ ?in_buffer:Lwt_bytes.t ->
+ ?out_buffer:Lwt_bytes.t ->
+ unit ->
+ input_channel * output_channelpipe ?cloexec ?in_buffer ?out_buffer () creates a pipe using Lwt_unix.pipe and makes two channels from the two returned file descriptors
val make :
+ ?buffer:Lwt_bytes.t ->
+ ?close:(unit -> unit Lwt.t) ->
+ ?seek:(int64 -> Unix.seek_command -> int64 Lwt.t) ->
+ mode:'mode mode ->
+ (Lwt_bytes.t -> int -> int -> int Lwt.t) ->
+ 'mode channelmake ?buffer ?close ~mode perform_io is the main function for creating new channels.
val of_bytes : mode:'mode mode -> Lwt_bytes.t -> 'mode channelCreate a channel from a byte array. Reading/writing is done directly on the provided array.
val of_fd :
+ ?buffer:Lwt_bytes.t ->
+ ?close:(unit -> unit Lwt.t) ->
+ mode:'mode mode ->
+ Lwt_unix.file_descr ->
+ 'mode channelof_fd ?buffer ?close ~mode fd creates a channel from a file descriptor.
val of_unix_fd :
+ ?buffer:Lwt_bytes.t ->
+ ?close:(unit -> unit Lwt.t) ->
+ mode:'mode mode ->
+ Unix.file_descr ->
+ 'mode channelof_unix_fd ?buffer ?close ~mode fd is a short-hand for:
of_fd ?buffer ?close (Lwt_unix.of_unix_file_descr fd)
close ch closes the given channel. If ch is an output channel, it performs all pending actions, flushes it and closes it. If ch is an input channel, it just closes it immediately.
close returns the result of the close function of the channel. Multiple calls to close will return exactly the same value.
Note: you cannot use close on channels obtained with atomic.
abort ch abort current operations and close the channel immediately.
atomic f transforms a sequence of io operations into one single atomic io operation.
Note:
f is invalid after f terminatesatomic can be called inside another atomicval file_length : string -> int64 Lwt.tRetrieves the length of the file at the given path. If the path refers to a directory, the returned promise is rejected with Unix.(Unix_error (EISDIR, _, _)).
val buffered : 'a channel -> intbuffered oc returns the number of bytes in the buffer
val flush : output_channel -> unit Lwt.tflush oc performs all pending writes on oc
val flush_all : unit -> unit Lwt.tflush_all () flushes all open output channels
val buffer_size : 'a channel -> intReturns the size of the internal buffer.
val is_busy : 'a channel -> boolis_busy channel returns whether the given channel is currently busy. A channel is busy when there is at least one job using it that has not yet terminated.
val is_closed : 'a channel -> boolis_closed channel returns whether the given channel is currently closed.
val position : 'a channel -> int64position ch Returns the current position in the channel.
set_position ch pos Sets the position in the output channel. This does not work if the channel does not support random access.
Note: except for functions dealing with streams (read_chars and read_lines) all functions are atomic.
val read_char : input_channel -> char Lwt.tread_char ic reads the next character of ic.
val read_char_opt : input_channel -> char option Lwt.tSame as Lwt_io.read_char, but does not raise End_of_file on end of input
val read_chars : input_channel -> char Lwt_stream.tread_chars ic returns a stream holding all characters of ic
val read_line : input_channel -> string Lwt.tread_line ic reads one complete line from ic and returns it without the end of line. End of line is either "\n" or "\r\n".
If the end of input is reached before reading any character, End_of_file is raised. If it is reached before reading an end of line but characters have already been read, they are returned.
val read_line_opt : input_channel -> string option Lwt.tSame as read_line but do not raise End_of_file on end of input.
val read_lines : input_channel -> string Lwt_stream.tread_lines ic returns a stream holding all lines of ic
val read : ?count:int -> input_channel -> string Lwt.tIf ~count is specified, read ~count ic reads at most ~count bytes from ic in one read operation. Note that fewer than ~count bytes can be read. This can happen for multiple reasons, including end of input, or no more data currently available. Check the size of the resulting string. read resolves with "" if the input channel is already at the end of input.
If ~count is not specified, read ic reads all bytes until the end of input.
val read_into : input_channel -> bytes -> int -> int -> int Lwt.tread_into ic buffer offset length reads up to length bytes, stores them in buffer at offset offset, and returns the number of bytes read.
Note: read_into does not raise End_of_file, it returns a length of 0 instead.
val read_into_exactly : input_channel -> bytes -> int -> int -> unit Lwt.tread_into_exactly ic buffer offset length reads exactly length bytes and stores them in buffer at offset offset.
val read_into_bigstring :
+ input_channel ->
+ Lwt_bytes.t ->
+ int ->
+ int ->
+ int Lwt.tval read_into_exactly_bigstring :
+ input_channel ->
+ Lwt_bytes.t ->
+ int ->
+ int ->
+ unit Lwt.tval read_value : input_channel -> 'a Lwt.tread_value channel reads a marshaled value from channel; it corresponds to the standard library's Stdlib.Marshal.from_channel. The corresponding writing function is write_value.
Note that reading marshaled values is not, in general, type-safe. See the warning in the description of module Stdlib.Marshal for details. The short version is: if you read a value of one type, such as string, when a value of another type, such as int has actually been marshaled to channel, you may get arbitrary behavior, including segmentation faults, access violations, security bugs, etc.
Note: as for reading functions, all functions except write_chars and write_lines are atomic.
For example if you use write_line in two different threads, the two operations will be serialized, and lines cannot be mixed.
val write_char : output_channel -> char -> unit Lwt.twrite_char oc char writes char on oc
val write_chars : output_channel -> char Lwt_stream.t -> unit Lwt.twrite_chars oc chars writes all characters of chars on oc
val write : output_channel -> string -> unit Lwt.twrite oc str writes all characters of str on oc
val write_line : output_channel -> string -> unit Lwt.twrite_line oc str writes str on oc followed by a new-line.
val write_lines : output_channel -> string Lwt_stream.t -> unit Lwt.twrite_lines oc lines writes all lines of lines to oc
val write_from : output_channel -> bytes -> int -> int -> int Lwt.twrite_from oc buffer offset length writes up to length bytes to oc, from buffer at offset offset and returns the number of bytes actually written
val write_from_bigstring :
+ output_channel ->
+ Lwt_bytes.t ->
+ int ->
+ int ->
+ int Lwt.tval write_from_string : output_channel -> string -> int -> int -> int Lwt.tSee write.
val write_from_exactly : output_channel -> bytes -> int -> int -> unit Lwt.twrite_from_exactly oc buffer offset length writes all length bytes from buffer at offset offset to oc
val write_from_exactly_bigstring :
+ output_channel ->
+ Lwt_bytes.t ->
+ int ->
+ int ->
+ unit Lwt.tval write_from_string_exactly :
+ output_channel ->
+ string ->
+ int ->
+ int ->
+ unit Lwt.tSee write_from_exactly.
val write_value :
+ output_channel ->
+ ?flags:Stdlib.Marshal.extern_flags list ->
+ 'a ->
+ unit Lwt.twrite_value channel ?flags v writes v to channel using the Marshal module of the standard library. See Stdlib.Marshal.to_channel for an explanation of ?flags.
The corresponding reading function is read_value. See warnings about type safety in the description of read_value.
These functions are basically helpers. Also you may prefer using the name printl rather than write_line because it is shorter.
The general name of a printing function is <prefix>print<suffixes>,
where <prefix> is one of:
'f', which means that the function takes as argument a channelstdout'e', which means that the function prints on stderrand <suffixes> is a combination of:
'l' which means that a new-line character is printed after the message'f' which means that the function takes as argument a format instead of a stringval fprint : output_channel -> string -> unit Lwt.tval fprintl : output_channel -> string -> unit Lwt.tval fprintf : output_channel -> ('a, unit, string, unit Lwt.t) format4 -> 'a%! does nothing here. To flush the channel, use Lwt_io.flush channel.
val fprintlf : output_channel -> ('a, unit, string, unit Lwt.t) format4 -> 'a%! does nothing here. To flush the channel, use Lwt_io.flush channel.
val print : string -> unit Lwt.tval printl : string -> unit Lwt.t%! does nothing here. To flush the channel, use Lwt_io.(flush stdout).
%! does nothing here. To flush the channel, use Lwt_io.(flush stdout).
val eprint : string -> unit Lwt.tval eprintl : string -> unit Lwt.t%! does nothing here. To flush the channel, use Lwt_io.(flush stderr).
%! does nothing here. To flush the channel, use Lwt_io.(flush stderr).
val hexdump_stream : output_channel -> char Lwt_stream.t -> unit Lwt.thexdump_stream oc byte_stream produces the same output as the command hexdump -C.
val hexdump : output_channel -> string -> unit Lwt.thexdump oc str = hexdump_stream oc (Lwt_stream.of_string str)
val open_file :
+ ?buffer:Lwt_bytes.t ->
+ ?flags:Unix.open_flag list ->
+ ?perm:Unix.file_perm ->
+ mode:'a mode ->
+ file_name ->
+ 'a channel Lwt.tLwt_io.open_file ~mode file opens the given file, either for reading (with ~mode:Input) or for writing (with ~mode:Output). The returned channel provides buffered I/O on the file.
If ~buffer is supplied, it is used as the I/O buffer.
If ~flags is supplied, the file is opened with the given flags (see Unix.open_flag). Note that ~flags is used exactly as given. For example, opening a file with ~flags and ~mode:Input does not implicitly add O_RDONLY. So, you should include O_RDONLY when opening for reading (~mode:Input), and O_WRONLY when opening for writing (~mode:Input). It is also recommended to include O_NONBLOCK, unless you are sure that the file cannot be a socket or a named pipe.
The default permissions used for creating new files are 0o666, i.e. reading and writing are allowed for the file owner, group, and everyone. These default permissions can be overridden by supplying ~perm.
Note: if opening for writing (~mode:Output), and the file already exists, open_file truncates (clears) the file by default. If you would like to keep the pre-existing contents of the file, use the ~flags parameter to pass a custom flags list that does not include Unix.open_flag.O_TRUNC.
val with_file :
+ ?buffer:Lwt_bytes.t ->
+ ?flags:Unix.open_flag list ->
+ ?perm:Unix.file_perm ->
+ mode:'a mode ->
+ file_name ->
+ ('a channel -> 'b Lwt.t) ->
+ 'b Lwt.tLwt_io.with_file ~mode filename f opens the given using Lwt_io.open_file, and passes the resulting channel to f. Lwt_io.with_file ensures that the channel is closed when the promise returned by f resolves, or if f raises an exception.
See Lwt_io.open_file for a description of the arguments, warnings, and other notes.
val open_temp_file :
+ ?buffer:Lwt_bytes.t ->
+ ?flags:Unix.open_flag list ->
+ ?perm:Unix.file_perm ->
+ ?temp_dir:string ->
+ ?prefix:string ->
+ ?suffix:string ->
+ unit ->
+ (string * output_channel) Lwt.topen_temp_file () starts creating a new temporary file, and evaluates to a promise for the pair of the file's name, and an output channel for writing to the file.
The caller should take care to delete the file later. Alternatively, see Lwt_io.with_temp_file.
The ?buffer and ?perm arguments are passed directly to an internal call to Lwt_io.open_file.
If not specified, ?flags defaults to [O_CREATE; O_EXCL; O_WRONLY; O_CLOEXEC]. If specified, the specified flags are used exactly. Note that these should typically contain at least O_CREAT and O_EXCL, otherwise open_temp_file may open an existing file.
?temp_dir can be used to choose the directory in which the file is created. For the current directory, use Stdlib.Filename.current_dir_name. If not specified, the directory is taken from Stdlib.Filename.get_temp_dir_name, which is typically set to your system temporary file directory.
?prefix helps determine the name of the file. It will be the prefix concatenated with a random sequence of characters. If not specified, open_temp_file uses some default prefix.
?suffix is like prefix, but it is appended at the end of the filename. In particular, it can be used to set the extension. This argument is supported since Lwt 4.4.0.
val with_temp_file :
+ ?buffer:Lwt_bytes.t ->
+ ?flags:Unix.open_flag list ->
+ ?perm:Unix.file_perm ->
+ ?temp_dir:string ->
+ ?prefix:string ->
+ ?suffix:string ->
+ ((string * output_channel) -> 'b Lwt.t) ->
+ 'b Lwt.twith_temp_file f calls open_temp_file (), passing all optional arguments directly to it. It then attaches f to run after the file is created, passing the filename and output channel to f. When the promise returned by f is resolved, with_temp_file closes the channel and deletes the temporary file by calling Lwt_unix.unlink.
val create_temp_dir :
+ ?perm:Unix.file_perm ->
+ ?parent:string ->
+ ?prefix:string ->
+ ?suffix:string ->
+ unit ->
+ string Lwt.tCreates a temporary directory, and returns a promise that resolves to its path. The caller must take care to remove the directory. Alternatively, see Lwt_io.with_temp_dir.
If ~perm is specified, the directory is created with the given permissions. The default permissions are 0755.
~parent is the directory in which the temporary directory is created. If not specified, the default value is the result of Filename.get_temp_dir_name ().
~prefix is prepended to the directory name, and ~suffix is appended to it.
val with_temp_dir :
+ ?perm:Unix.file_perm ->
+ ?parent:string ->
+ ?prefix:string ->
+ ?suffix:string ->
+ (string -> 'a Lwt.t) ->
+ 'a Lwt.twith_temp_dir f first calls create_temp_dir, forwarding all optional arguments to it. Once the temporary directory is created at path, with_temp_dir f calls f path. When the promise returned by f path is resolved, with_temp_dir f recursively deletes the temporary directory and all its contents by calling Lwt_io.delete_recursively.
val delete_recursively : string -> unit Lwt.tdelete_recursively path attempts to delete the directory path and all its content recursively.
This is likely VERY slow for directories with many files. That is probably best addressed by switching to blocking calls run inside a worker thread, i.e. with Lwt_preemptive.
val open_connection :
+ ?fd:Lwt_unix.file_descr ->
+ ?in_buffer:Lwt_bytes.t ->
+ ?out_buffer:Lwt_bytes.t ->
+ Unix.sockaddr ->
+ (input_channel * output_channel) Lwt.topen_connection ?fd ?in_buffer ?out_buffer addr opens a connection to the given address and returns two channels for using it. If fd is not specified, a fresh one will be used.
The connection is completely closed when you close both channels.
val with_connection :
+ ?fd:Lwt_unix.file_descr ->
+ ?in_buffer:Lwt_bytes.t ->
+ ?out_buffer:Lwt_bytes.t ->
+ Unix.sockaddr ->
+ ((input_channel * output_channel) -> 'a Lwt.t) ->
+ 'a Lwt.twith_connection ?fd ?in_buffer ?out_buffer addr f opens a connection to the given address and passes the channels to f
val establish_server_with_client_socket :
+ ?server_fd:Lwt_unix.file_descr ->
+ ?backlog:int ->
+ ?no_close:bool ->
+ Unix.sockaddr ->
+ (Lwt_unix.sockaddr -> Lwt_unix.file_descr -> unit Lwt.t) ->
+ server Lwt.testablish_server_with_client_socket listen_address f creates a server which listens for incoming connections on listen_address. When a client makes a new connection, it is passed to f: more precisely, the server calls
f client_address client_socketwhere client_address is the address (peer name) of the new client, and client_socket is the socket connected to the client.
The server does not block waiting for f to complete: it concurrently tries to accept more client connections while f is handling the client.
When the promise returned by f completes (i.e., f is done handling the client), establish_server_with_client_socket automatically closes client_socket. This is a default behavior that is useful for simple cases, but for a robust application you should explicitly close these channels yourself, and handle any exceptions as appropriate. If the channels are still open when f completes, and their automatic closing raises an exception, establish_server_with_client_socket treats it as an unhandled exception reaching the top level of the application: it passes that exception to Lwt.async_exception_hook, the default behavior of which is to print the exception and terminate your process.
Automatic closing can be completely disabled by passing ~no_close:true.
Similarly, if f raises an exception (or the promise it returns fails with an exception), establish_server_with_client_socket can do nothing with that exception, except pass it to Lwt.async_exception_hook.
~server_fd can be specified to use an existing file descriptor for listening. Otherwise, a fresh socket is created internally. In either case, establish_server_with_client_socket will internally assign listen_address to the server socket.
~backlog is the argument passed to Lwt_unix.listen. Its default value is SOMAXCONN, which varies by platform and socket kind.
The returned promise (a server Lwt.t) resolves when the server has just started listening on listen_address: right after the internal call to listen, and right before the first internal call to accept.
val establish_server_with_client_address :
+ ?fd:Lwt_unix.file_descr ->
+ ?buffer_size:int ->
+ ?backlog:int ->
+ ?no_close:bool ->
+ Unix.sockaddr ->
+ (Lwt_unix.sockaddr -> (input_channel * output_channel) -> unit Lwt.t) ->
+ server Lwt.tLike Lwt_io.establish_server_with_client_socket, but passes two buffered channels to the connection handler f. These channels wrap the client socket.
The channels are closed automatically when the promise returned by f resolves. To avoid this behavior, pass ~no_close:true.
Closes the given server's listening socket. The returned promise resolves when the close(2) system call completes. This function does not affect the sockets of connections that have already been accepted, i.e. passed to f by establish_server.
val lines_of_file : file_name -> string Lwt_stream.tlines_of_file name returns a stream of all lines of the file with name name. The file is automatically closed when all lines have been read.
val lines_to_file : file_name -> string Lwt_stream.t -> unit Lwt.tlines_to_file name lines writes all lines of lines to file with name name.
val chars_of_file : file_name -> char Lwt_stream.tchars_of_file name returns a stream of all characters of the file with name name. As for lines_of_file the file is closed when all characters have been read.
val chars_to_file : file_name -> char Lwt_stream.t -> unit Lwt.tchars_to_file name chars writes all characters of chars to name
module type NumberIO = sig ... endCommon interface for reading/writing integers in binary
Reading/writing of numbers in the system endianness.
include NumberIOval read_int : input_channel -> int Lwt.tReads a 32-bits integer as an ocaml int
val read_int16 : input_channel -> int Lwt.tval read_int32 : input_channel -> int32 Lwt.tval read_int64 : input_channel -> int64 Lwt.tval read_float32 : input_channel -> float Lwt.tReads an IEEE single precision floating point value
val read_float64 : input_channel -> float Lwt.tReads an IEEE double precision floating point value
val write_int : output_channel -> int -> unit Lwt.tWrites an ocaml int as a 32-bits integer
val write_int16 : output_channel -> int -> unit Lwt.tval write_int32 : output_channel -> int32 -> unit Lwt.tval write_int64 : output_channel -> int64 -> unit Lwt.tval write_float32 : output_channel -> float -> unit Lwt.tWrites an IEEE single precision floating point value
val write_float64 : output_channel -> float -> unit Lwt.tWrites an IEEE double precision floating point value
val system_byte_order : byte_orderSame as Lwt_sys.byte_order.
val block : 'a channel -> int -> (Lwt_bytes.t -> int -> 'b Lwt.t) -> 'b Lwt.tblock ch size f pass to f the internal buffer and an offset. The buffer contains size chars at offset. f may read or write these chars. size must satisfy 0 <= size <= 16
type direct_access = {da_buffer : Lwt_bytes.t;The internal buffer
*)mutable da_ptr : int;The pointer to:
mutable da_max : int;The maximum offset
*)da_perform : unit -> int Lwt.t;}Information for directly accessing the internal buffer of a channel
val direct_access : 'a channel -> (direct_access -> 'b Lwt.t) -> 'b Lwt.tdirect_access ch f passes to f a direct_access structure. f must use it and update da_ptr to reflect how many bytes have been read/written.
Return the default size for buffers. Channels that are created without a specific buffer use new buffer of this size.
val establish_server :
+ ?fd:Lwt_unix.file_descr ->
+ ?buffer_size:int ->
+ ?backlog:int ->
+ ?no_close:bool ->
+ Unix.sockaddr ->
+ ((input_channel * output_channel) -> unit Lwt.t) ->
+ server Lwt.tLike establish_server_with_client_address, but does not pass the client address or fd to the callback f.
module Versioned : sig ... endVersioned variants of APIs undergoing breaking changes.
Lwt_io.NumberIOCommon interface for reading/writing integers in binary
val read_int : input_channel -> int Lwt.tReads a 32-bits integer as an ocaml int
val read_int16 : input_channel -> int Lwt.tval read_int32 : input_channel -> int32 Lwt.tval read_int64 : input_channel -> int64 Lwt.tval read_float32 : input_channel -> float Lwt.tReads an IEEE single precision floating point value
val read_float64 : input_channel -> float Lwt.tReads an IEEE double precision floating point value
val write_int : output_channel -> int -> unit Lwt.tWrites an ocaml int as a 32-bits integer
val write_int16 : output_channel -> int -> unit Lwt.tval write_int32 : output_channel -> int32 -> unit Lwt.tval write_int64 : output_channel -> int64 -> unit Lwt.tval write_float32 : output_channel -> float -> unit Lwt.tWrites an IEEE single precision floating point value
val write_float64 : output_channel -> float -> unit Lwt.tWrites an IEEE double precision floating point value
Lwt_listList helpers
Note: this module use the same naming convention as Lwt_stream.
Lwt_main.Enter_iter_hooksHooks, of type unit -> unit, that are called before each iteration of the Lwt main loop.
Hooks are functions of either type unit -> unit or unit -> unit Lwt.t; this type constructor is used only to express both possibilities in one signature.
Values of type hook represent hooks that have been added, so that they can be removed later (if needed).
Adds a hook to the hook sequence underlying this module, to be run first, before any other hooks already added.
Adds a hook to the hook sequence underlying this module, to be run last, after any other hooks already added.
Lwt_main.Exit_hooksPromise-returning hooks, of type unit -> unit Lwt.t, that are called at process exit. Exceptions raised by these hooks are ignored.
type 'return_value kind = 'return_value Lwt.tHooks are functions of either type unit -> unit or unit -> unit Lwt.t; this type constructor is used only to express both possibilities in one signature.
Values of type hook represent hooks that have been added, so that they can be removed later (if needed).
Adds a hook to the hook sequence underlying this module, to be run first, before any other hooks already added.
Adds a hook to the hook sequence underlying this module, to be run last, after any other hooks already added.
Lwt_main.Leave_iter_hooksHooks, of type unit -> unit, that are called after each iteration of the Lwt main loop.
Hooks are functions of either type unit -> unit or unit -> unit Lwt.t; this type constructor is used only to express both possibilities in one signature.
Values of type hook represent hooks that have been added, so that they can be removed later (if needed).
Adds a hook to the hook sequence underlying this module, to be run first, before any other hooks already added.
Adds a hook to the hook sequence underlying this module, to be run last, after any other hooks already added.
Lwt_mainMain loop and event queue
This module controls the ``main-loop'' of Lwt.
val run : 'a Lwt.t -> 'aLwt_main.run p calls the Lwt scheduler, performing I/O until p resolves. Lwt_main.run p returns the value in p if p is fulfilled. If p is rejected with an exception instead, Lwt_main.run p raises that exception.
Every native and bytecode program that uses Lwt should call this function at its top level. It implements the Lwt main loop.
Example:
let main () = Lwt_io.write_line Lwt_io.stdout "hello world"
+
+let () = Lwt_main.run (main ())Lwt_main.run is not available when targeting JavaScript, because the environment (such as Node.js or the browser's script engine) implements the I/O loop.
On Unix, calling Lwt_main.run installs a SIGCHLD handler, which is needed for the implementations of Lwt_unix.waitpid and Lwt_unix.wait4. As a result, programs that call Lwt_main.run and also use non-Lwt system calls need to handle those system calls failing with EINTR.
Nested calls to Lwt_main.run are not allowed. That is, do not call Lwt_main.run in a callback triggered by a promise that is resolved by an outer invocation of Lwt_main.run. If your program makes such a call, Lwt_main.run will raise Failure. This should be considered a logic error (i.e., code making such a call is inherently broken).
In addition, note that if you have set the exception filter to let runtime exceptions bubble up (via Lwt.Exception_filter.(set handle_all_except_runtime)) then Lwt does not attempt to catch exceptions thrown by the OCaml runtime. Specifically, in this case, Lwt lets Out_of_memory and Stack_overflow exceptions traverse all of its functions and bubble up to the caller of Lwt_main.run. Moreover because these exceptions are left to traverse the call stack, they leave the internal data-structures in an inconsistent state. For this reason, calling Lwt_main.run again after such an exception will raise Failure.
It is not safe to call Lwt_main.run in a function registered with Stdlib.at_exit, use Lwt_main.at_exit instead.
val yield : unit -> unit Lwt.tyield () is a pending promise that is fulfilled after Lwt finishes calling all currently ready callbacks, i.e. it is fulfilled on the next “tick.”
Causes promises created with Lwt.pause and Lwt_main.yield to remain forever pending.
(Note that yield is deprecated in favor of the more general Lwt.pause.)
This is meant for use with Lwt_unix.fork, as a way to “abandon” more promise chains that are pending in your process.
module type Hooks = sig ... endHook sequences. Each module of this type is a set of hooks, to be run by Lwt at certain points during execution. See modules Enter_iter_hooks, Leave_iter_hooks, and Exit_hooks.
module Enter_iter_hooks : Hooks with type 'return_value kind = 'return_valueHooks, of type unit -> unit, that are called before each iteration of the Lwt main loop.
module Leave_iter_hooks : Hooks with type 'return_value kind = 'return_valueHooks, of type unit -> unit, that are called after each iteration of the Lwt main loop.
module Exit_hooks : Hooks with type 'return_value kind = 'return_value Lwt.tPromise-returning hooks, of type unit -> unit Lwt.t, that are called at process exit. Exceptions raised by these hooks are ignored.
val enter_iter_hooks : (unit -> unit) Lwt_sequence.tval leave_iter_hooks : (unit -> unit) Lwt_sequence.tval exit_hooks : (unit -> unit Lwt.t) Lwt_sequence.tval at_exit : (unit -> unit Lwt.t) -> unitLwt_main.at_exit hook is the same as ignore (Lwt_main.Exit_hooks.add_first hook).
Lwt_main.HooksHook sequences. Each module of this type is a set of hooks, to be run by Lwt at certain points during execution. See modules Enter_iter_hooks, Leave_iter_hooks, and Exit_hooks.
Hooks are functions of either type unit -> unit or unit -> unit Lwt.t; this type constructor is used only to express both possibilities in one signature.
Values of type hook represent hooks that have been added, so that they can be removed later (if needed).
Adds a hook to the hook sequence underlying this module, to be run first, before any other hooks already added.
Adds a hook to the hook sequence underlying this module, to be run last, after any other hooks already added.
Lwt_mutexCooperative locks for mutual exclusion
val create : unit -> tcreate () creates a new mutex, which is initially unlocked
lock mutex lockcs the mutex, that is:
lock returns immediatelylock waits for all threads waiting on the mutex to terminate, then it resumes when the last one unlocks the mutexNote: threads are woken up in the same order they try to lock the mutex
val unlock : t -> unitunlock mutex unlock the mutex if no threads is waiting on it. Otherwise it will eventually removes the first one and resumes it.
val is_locked : t -> boollocked mutex returns whether mutex is currently locked
val is_empty : t -> boolis_empty mutex returns true if they are no thread waiting on the mutex, and false otherwise
with_lock lock f is used to lock a mutex within a block scope. The function f () is called with the mutex locked, and its result is returned from the call to with_lock. If an exception is raised from f, the mutex is also unlocked before the scope of with_lock is exited.
Lwt_mvarMailbox variables
“Mailbox” variables implement a synchronising variable, used for communication between concurrent threads.
The type of a mailbox variable. Mailbox variables are used to communicate values between threads in a synchronous way. The type parameter specifies the type of the value propagated from put to take.
val create : 'a -> 'a tcreate v creates a new mailbox variable containing value v.
val create_empty : unit -> 'a tcreate () creates a new empty mailbox variable.
put mvar value puts a value into a mailbox variable. This value will remain in the mailbox until take is called to remove it. If the mailbox is not empty, the current thread will block until it is emptied.
take mvar will take any currently available value from the mailbox variable. If no value is currently available, the current thread will block, awaiting a value to be put by another thread.
val take_available : 'a t -> 'a optiontake_available mvar immediately takes the value from mvar without blocking, returning None if the mailbox is empty.
val is_empty : 'a t -> boolis_empty mvar indicates if put mvar can be called without blocking.
Lwt_poolExternal resource pools.
This module provides an abstraction for managing collections of resources. One example use case is for managing a pool of database connections, where instead of establishing a new connection each time you need one (which is expensive), you can keep a pool of opened connections and reuse ones that are free.
It also provides the capability of:
The following example illustrates how it is used with an imaginary Db module:
let uri = "postgresql://localhost:5432"
+
+(* Create a database connection pool with max size of 10. *)
+let pool =
+ Lwt_pool.create 10
+ ~dispose:(fun connection -> Db.close connection |> Lwt.return)
+ (fun () -> Db.connect uri |> Lwt.return)
+
+(* Use the pool in queries. *)
+let create_user name =
+ Lwt_pool.use pool (fun connection ->
+ connection
+ |> Db.insert "users" [("name", name)]
+ |> Lwt.return
+ )Note that this is not intended to keep a pool of system threads. If you want to have such pool, consider using Lwt_preemptive.
val create :
+ int ->
+ ?validate:('a -> bool Lwt.t) ->
+ ?check:('a -> (bool -> unit) -> unit) ->
+ ?dispose:('a -> unit Lwt.t) ->
+ (unit -> 'a Lwt.t) ->
+ 'a tcreate n ?check ?validate ?dispose f creates a new pool with at most n elements. f is used to create a new pool element. Elements are created on demand and re-used until disposed of.
use p f requests one free element of the pool p and gives it to the function f. The element is put back into the pool after the promise created by f completes.
In the case that p is exhausted and the maximum number of elements is reached, use will wait until one becomes free.
clear p will clear all elements in p, calling the dispose function associated with p on each of the cleared elements. Any elements from p which are currently in use will be disposed of once they are released.
The next call to use p after clear p guarantees a freshly created pool element.
Disposals are performed sequentially in an undefined order.
Make.OrdLwt_pqueue.MakeGenerates priority queue types from ordered types.
module Ord : OrderedTypetype elt = Ord.tType of elements contained in the priority queue.
val empty : tThe empty priority queue. Contains no elements.
val is_empty : t -> boolis_empty q evaluates to true iff q is empty.
add e q evaluates to a new priority queue, which contains all the elements of q, and the additional element e.
union q q' evaluates to a new priority queue, which contains all the elements of both q and q'.
find_min q evaluates to the minimum element of q if it is not empty, and raises Not_found otherwise.
lookup_min q evaluates to Some e, where e is the minimum element of q, if q is not empty, and evaluates to None otherwise.
remove_min q evaluates to a new priority queue, which contains all the elements of q except for its minimum element. Raises Not_found if q is empty.
val size : t -> intsize q evaluates to the number of elements in q.
Lwt_pqueueFunctional priority queues (deprecated).
A priority queue maintains, in the abstract sense, a set of elements in order, and supports fast lookup and removal of the first (“minimum”) element. This is used in Lwt for organizing threads that are waiting for timeouts.
The priority queues in this module preserve “duplicates”: elements that compare equal in their order.
module type OrderedType = sig ... endSignature pairing an element type with an ordering function.
module type S = sig ... endSignature of priority queues.
Lwt_pqueue.OrderedTypeSignature pairing an element type with an ordering function.
Lwt_pqueue.SSignature of priority queues.
val empty : tThe empty priority queue. Contains no elements.
val is_empty : t -> boolis_empty q evaluates to true iff q is empty.
add e q evaluates to a new priority queue, which contains all the elements of q, and the additional element e.
union q q' evaluates to a new priority queue, which contains all the elements of both q and q'.
find_min q evaluates to the minimum element of q if it is not empty, and raises Not_found otherwise.
lookup_min q evaluates to Some e, where e is the minimum element of q, if q is not empty, and evaluates to None otherwise.
remove_min q evaluates to a new priority queue, which contains all the elements of q except for its minimum element. Raises Not_found if q is empty.
val size : t -> intsize q evaluates to the number of elements in q.
Lwt_preemptiveThis module allows to mix preemptive threads with Lwt cooperative threads. It maintains an extensible pool of preemptive threads to which you can detach computations.
See Mwt for a more modern implementation.
val detach : ('a -> 'b) -> 'a -> 'b Lwt.tdetach f x runs the computation f x in a separate preemptive thread. detach evaluates to an Lwt promise, which is pending until the preemptive thread completes.
detach calls simple_init internally, which means that the number of preemptive threads is capped by default at four. If you would like a higher limit, call init or set_bounds directly.
Note that Lwt thread-local storage (i.e., Lwt.with_value) cannot be safely used from within f. The same goes for most of the rest of Lwt. If you need to run an Lwt thread in f, use run_in_main.
val run_in_main : (unit -> 'a Lwt.t) -> 'arun_in_main f can be called from a detached computation to execute f () in the main preemptive thread, i.e. the one executing Lwt_main.run. run_in_main f blocks until f () completes, then returns its result. If f () raises an exception, run_in_main f raises the same exception.
Lwt.with_value may be used inside f (). Lwt.get can correctly retrieve values set this way inside f (), but not values set using Lwt.with_value outside f ().
val run_in_main_dont_wait : (unit -> unit Lwt.t) -> (exn -> unit) -> unitrun_in_main_dont_wait f h does the same as run_in_main f but a bit faster and lighter as it does not wait for the result of f.
If f's promise is rejected (or if it raises), then the function h is called with the rejection exception.
init min max log initialises this module. i.e. it launches the minimum number of preemptive threads and starts the dispatcher.
simple_init () checks if the library is not yet initialized, and if not, does a simple initialization. The minimum number of threads is set to zero, maximum to four, and the log function is left unchanged, i.e. the default built-in logging function is used. See Lwt_preemptive.init.
Note: this function is automatically called by detach.
get_bounds () returns the minimum and the maximum number of preemptive threads.
set_bounds (min, max) set the minimum and the maximum number of preemptive threads.
Sets the size of the waiting queue, if no more preemptive threads are available. When the queue is full, detach will sleep until a thread is available.
Lwt_process.processLwt_process.process_fullinherit process_nonemethod stdin : Lwt_io.output_channelmethod stdout : Lwt_io.input_channelmethod stderr : Lwt_io.input_channelLwt_process.process_ininherit process_nonemethod stdout : Lwt_io.input_channelThe standard output of the process
Lwt_process.process_nonemethod state : stateReturn the state of the process
Terminates the process. It is equivalent to kill Sys.sigkill on Unix but also works on Windows (unlike Lwt_process.process_none.kill).
method status : Unix.process_status Lwt.tThreads which wait for the sub-process to exit then returns its exit status
method rusage : Lwt_unix.resource_usage Lwt.tThreads which wait for the sub-process to exit then returns its resource usages
method close : Unix.process_status Lwt.tCloses the process and returns its exit status. This closes all channels used to communicate with the process
Lwt_process.process_outinherit process_nonemethod stdin : Lwt_io.output_channelThe standard input of the process
Lwt_processProcess management
This module allows you to spawn processes and communicate with them.
A command. The first field is the name of the executable and the second is the list of arguments. For example:
("ls", [|"ls"; "-l"|])Notes:
"\000". For example:("", [|"echo"; "\000foo bar"|])is the same as:
("", [|"echo"; "foo"; "bar"|])val shell : string -> commandA command executed with the shell. (with "/bin/sh -c <cmd>" on Unix and "cmd.exe /c <cmd>" on Windows).
All the following functions take an optional argument timeout, in seconds. If specified, after expiration, the process will be sent a Unix.sigkill signal and channels will be closed. When the channels are closed, any pending I/O operations on them (such as Lwt_io.read_chars) fail with exception Lwt_io.Channel_closed.
type redirection = [ | `KeepPoint to the same file as in the parent.
*)| `Dev_nullRedirect to /dev/null (POSIX) or nul (Win32).
| `CloseClose the file descriptor.
*)| `FD_copy of Unix.file_descrRedirect to the file pointed to by fd. fd remains open in the parent.
| `FD_move of Unix.file_descrRedirect to the file pointed to by fd. fd is then closed in the parent.
]File descriptor redirections. These are used with the ~stdin, ~stdout, and ~stderr arguments below to specify how the standard file descriptors should be redirected in the child process. All optional redirection arguments default to `Keep.
val exec :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stdin:redirection ->
+ ?stdout:redirection ->
+ ?stderr:redirection ->
+ command ->
+ Unix.process_status Lwt.tExecutes the given command and returns its exit status.
val pread :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stdin:redirection ->
+ ?stderr:redirection ->
+ command ->
+ string Lwt.tval pread_chars :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stdin:redirection ->
+ ?stderr:redirection ->
+ command ->
+ char Lwt_stream.tval pread_line :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stdin:redirection ->
+ ?stderr:redirection ->
+ command ->
+ string Lwt.tval pread_lines :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stdin:redirection ->
+ ?stderr:redirection ->
+ command ->
+ string Lwt_stream.tval pwrite :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stdout:redirection ->
+ ?stderr:redirection ->
+ command ->
+ string ->
+ unit Lwt.tval pwrite_chars :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stdout:redirection ->
+ ?stderr:redirection ->
+ command ->
+ char Lwt_stream.t ->
+ unit Lwt.tval pwrite_line :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stdout:redirection ->
+ ?stderr:redirection ->
+ command ->
+ string ->
+ unit Lwt.tval pwrite_lines :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stdout:redirection ->
+ ?stderr:redirection ->
+ command ->
+ string Lwt_stream.t ->
+ unit Lwt.tval pmap :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stderr:redirection ->
+ command ->
+ string ->
+ string Lwt.tval pmap_chars :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stderr:redirection ->
+ command ->
+ char Lwt_stream.t ->
+ char Lwt_stream.tval pmap_line :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stderr:redirection ->
+ command ->
+ string ->
+ string Lwt.tval pmap_lines :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stderr:redirection ->
+ command ->
+ string Lwt_stream.t ->
+ string Lwt_stream.ttype state = | RunningThe process is still running
*)| Exited of Unix.process_statusThe process has exited
*)State of a sub-process
class process_none : ?timeout:float -> ?env:string array -> ?cwd:string -> ?stdin:redirection -> ?stdout:
+ redirection -> ?stderr:redirection -> command -> object ... endval open_process_none :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stdin:redirection ->
+ ?stdout:redirection ->
+ ?stderr:redirection ->
+ command ->
+ process_noneval with_process_none :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stdin:redirection ->
+ ?stdout:redirection ->
+ ?stderr:redirection ->
+ command ->
+ (process_none -> 'a Lwt.t) ->
+ 'a Lwt.tclass process_in : ?timeout:float -> ?env:string array -> ?cwd:string -> ?stdin:redirection -> ?stderr:
+ redirection -> command -> object ... endval open_process_in :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stdin:redirection ->
+ ?stderr:redirection ->
+ command ->
+ process_inval with_process_in :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stdin:redirection ->
+ ?stderr:redirection ->
+ command ->
+ (process_in -> 'a Lwt.t) ->
+ 'a Lwt.tclass process_out : ?timeout:float -> ?env:string array -> ?cwd:string -> ?stdout:redirection -> ?stderr:
+ redirection -> command -> object ... endval open_process_out :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stdout:redirection ->
+ ?stderr:redirection ->
+ command ->
+ process_outval with_process_out :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stdout:redirection ->
+ ?stderr:redirection ->
+ command ->
+ (process_out -> 'a Lwt.t) ->
+ 'a Lwt.tclass process : ?timeout:float -> ?env:string array -> ?cwd:string -> ?stderr:redirection ->
+ command -> object ... endval open_process :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stderr:redirection ->
+ command ->
+ processval with_process :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ ?stderr:redirection ->
+ command ->
+ (process -> 'a Lwt.t) ->
+ 'a Lwt.tclass process_full : ?timeout:float -> ?env:string array -> ?cwd:string -> command -> object ... endval open_process_full :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ command ->
+ process_fullval with_process_full :
+ ?timeout:float ->
+ ?env:string array ->
+ ?cwd:string ->
+ command ->
+ (process_full -> 'a Lwt.t) ->
+ 'a Lwt.tLwt_result.InfixLet_syntax.Open_on_rhsLet_syntax.Let_syntaxval return : 'a -> ('a, _) tSee Lwt_result.return.
See Lwt_result.map.
See Lwt_result.bind.
See Lwt_result.both.
module Open_on_rhs : sig ... endLwt_result.Let_syntaxmodule Let_syntax : sig ... endLwt_result.SyntaxLwt_resultExplicit error handling
This module provides helpers for values of type ('a, 'b) result Lwt.t. The module is experimental and may change in the future.
val return : 'a -> ('a, _) tval fail : 'b -> (_, 'b) tcatch x behaves like return y if x () evaluates to y, and like fail e if x () raises e
Lwt.both p_1 p_2 returns a promise that is pending until both promises p_1 and p_2 become resolved. If only p_1 is Error e, the promise is resolved with Error e, If only p_2 is Error e, the promise is resolved with Error e, If both p_1 and p_2 resolve with Error _, the promise is resolved with the error that occurred first.
iter f r is f v if r is a promise resolved with Ok v, and Lwt.return_unit otherwise.
iter_error f r is f v if r is a promise resolved with Error v, and Lwt.return_unit otherwise.
module Infix : sig ... endmodule Let_syntax : sig ... endmodule Syntax : sig ... endLwt_seqThe type of delayed lists containing elements of type 'a. Note that the concrete list node 'a node is delayed under a closure, not a lazy block, which means it might be recomputed every time we access it.
and +'a node = | Nil| Cons of 'a * 'a tA fully-evaluated list node, either empty or containing an element and a delayed tail.
*)val empty : 'a tThe empty sequence, containing no elements.
val return : 'a -> 'a tThe singleton sequence containing only the given element.
cons x xs is the sequence containing the element x followed by the sequence xs
cons x xs is the sequence containing the element promised by x followed by the sequence xs
map f seq returns a new sequence whose elements are the elements of seq, transformed by f. This transformation is lazy, it only applies when the result is traversed.
map_s f seq is like map f seq but f is a function that returns a promise.
Note that there is no concurrency between the promises from the underlying sequence seq and the promises from applying the function f. In other words, the next promise-element of the underlying sequence (seq) is only created when the current promise-element of the returned sequence (as mapped by f) has resolved. This scheduling is true for all the _s functions of this module.
Remove from the sequence the elements that do not satisfy the given predicate. This transformation is lazy, it only applies when the result is traversed.
filter_s is like filter but the predicate returns a promise.
See map_s for additional details about scheduling.
Apply the function to every element; if f x = None then x is dropped; if f x = Some y then y is returned. This transformation is lazy, it only applies when the result is traversed.
filter_map_s is like filter but the predicate returns a promise.
See map_s for additional details about scheduling.
Map each element to a subsequence, then return each element of this sub-sequence in turn. This transformation is lazy, it only applies when the result is traversed.
Traverse the sequence from left to right, combining each element with the accumulator using the given function. The traversal happens immediately and will not terminate (i.e., the promise will not resolve) on infinite sequences.
fold_left_s is like fold_left but the function returns a promise.
See map_s for additional details about scheduling.
Iterate on the sequence, calling the (imperative) function on every element.
The sequence's next node is evaluated only once the function has finished processing the current element. More formally: the promise for the n+1th node of the sequence is created only once the promise returned by f on the nth element of the sequence has resolved.
The traversal happens immediately and will not terminate (i.e., the promise will not resolve) on infinite sequences.
iter_s is like iter but the function returns a promise.
See map_s for additional details about scheduling.
Iterate on the sequence, calling the (imperative) function on every element.
The sequence's next node is evaluated as soon as the previous node is resolved.
The traversal happens immediately and will not terminate (i.e., the promise will not resolve) on infinite sequences.
iter_n ~max_concurrency f s
Iterates on the sequence s, calling the (imperative) function f on every element.
The sum total of unresolved promises returned by f never exceeds max_concurrency. Node suspensions are evaluated only when there is capacity for f-promises to be evaluated. Consequently, there might be significantly fewer than max_concurrency promises being evaluated concurrently; especially if the node suspensions take longer to evaluate than the f-promises.
The traversal happens immediately and will not terminate (i.e., the promise will not resolve) on infinite sequences.
val unfold : ('b -> ('a * 'b) option) -> 'b -> 'a tBuild a sequence from a step function and an initial value. unfold f u returns empty if the promise f u resolves to None, or fun () -> Lwt.return (Cons (x, unfold f y)) if the promise f u resolves to Some (x, y).
unfold_lwt is like unfold but the step function returns a promise.
Convert a sequence to a list, preserving order. The traversal happens immediately and will not terminate (i.e., the promise will not resolve) on infinite sequences.
val of_list : 'a list -> 'a tConvert a list to a sequence, preserving order.
val of_seq : 'a Stdlib.Seq.t -> 'a tConvert from 'a Stdlib.Seq.t to 'a Lwt_seq.t. This transformation is lazy, it only applies when the result is traversed.
val of_seq_lwt : 'a Lwt.t Stdlib.Seq.t -> 'a tConvert from 'a Lwt.t Stdlib.Seq.t to 'a Lwt_seq.t. This transformation is lazy, it only applies when the result is traversed.
Lwt_sequenceMutable sequence of elements (deprecated)
A sequence is an object holding a list of elements which support the following operations:
val get : 'a node -> 'aReturns the contents of a node
val set : 'a node -> 'a -> unitChange the contents of a node
val remove : 'a node -> unitRemoves a node from the sequence it is part of. It does nothing if the node has already been removed.
val create : unit -> 'a tcreate () creates a new empty sequence
val clear : 'a t -> unitRemoves all nodes from the given sequence. The nodes are not actually mutated to note their removal. Only the sequence's pointers are updated.
val is_empty : 'a t -> boolReturns true iff the given sequence is empty
val length : 'a t -> intReturns the number of elements in the given sequence. This is a O(n) operation where n is the number of elements in the sequence.
val take_l : 'a t -> 'atake_l x s remove and returns the leftmost element of s
val take_r : 'a t -> 'atake_r x s remove and returns the rightmost element of s
val take_opt_l : 'a t -> 'a optiontake_opt_l x s remove and returns Some x where x is the leftmost element of s or None if s is empty
val take_opt_r : 'a t -> 'a optiontake_opt_r x s remove and returns Some x where x is the rightmost element of s or None if s is empty
transfer_l s1 s2 removes all elements of s1 and add them at the left of s2. This operation runs in constant time and space.
transfer_r s1 s2 removes all elements of s1 and add them at the right of s2. This operation runs in constant time and space.
Note: it is OK to remove a node while traversing a sequence
val iter_l : ('a -> unit) -> 'a t -> unititer_l f s applies f on all elements of s starting from the left
val iter_r : ('a -> unit) -> 'a t -> unititer_r f s applies f on all elements of s starting from the right
iter_node_l f s applies f on all nodes of s starting from the left
iter_node_r f s applies f on all nodes of s starting from the right
val fold_l : ('a -> 'b -> 'b) -> 'a t -> 'b -> 'bfold_l f s is:
fold_l f s x = f en (... (f e2 (f e1 x)))where e1, e2, ..., en are the elements of s
val fold_r : ('a -> 'b -> 'b) -> 'a t -> 'b -> 'bfold_r f s is:
fold_r f s x = f e1 (f e2 (... (f en x)))where e1, e2, ..., en are the elements of s
find_node_opt_l f s returns Some x, where x is the first node of s starting from the left that satisfies f or None if none exists.
find_node_opt_r f s returns Some x, where x is the first node of s starting from the right that satisfies f or None if none exists.
find_node_l f s returns the first node of s starting from the left that satisfies f or raises Not_found if none exists.
Lwt_stream.bounded_pushType of sources for bounded push-streams.
Change the size of the stream queue. Note that the new size can smaller than the current stream queue size.
It raises Stdlib.Invalid_argument if size < 0.
method push : 'a -> unit Lwt.tPushes a new element to the stream. If the stream is full then it will block until one element is consumed. If another thread is already blocked on push, it raises Lwt_stream.Full.
Closes the stream. Any thread currently blocked on Lwt_stream.bounded_push.push fails with Lwt_stream.Closed.
Lwt_streamData streams
A stream holding values of type 'a.
Naming convention: in this module, all functions applying a function to each element of a stream are suffixed by:
_s when the function returns a thread and calls are serialised_p when the function returns a thread and calls are parallelisedfrom f creates a stream from the given input function. f is called each time more input is needed, and the stream ends when f returns None.
If f, or the thread produced by f, raises an exception, that exception is forwarded to the consumer of the stream (for example, a caller of get). Note that this does not end the stream. A subsequent attempt to read from the stream will cause another call to f, which may succeed with a value.
val from_direct : (unit -> 'a option) -> 'a tException raised by the push function of a push-stream when pushing an element after the end of stream (= None) has been pushed.
val create : unit -> 'a t * ('a option -> unit)create () returns a new stream and a push function.
To notify the stream's consumer of errors, either use a separate communication channel, or use a Stdlib.result stream. There is no way to push an exception into a push-stream.
val create_with_reference : unit -> 'a t * ('a option -> unit) * ('b -> unit)create_with_reference () returns a new stream and a push function. The last function allows a reference to be set to an external source. This prevents the external source from being garbage collected.
For example, to convert a reactive event to a stream:
let stream, push, set_ref = Lwt_stream.create_with_reference () in
+set_ref (map_event push event)Exception raised by the push function of a bounded push-stream when the stream queue is full and a thread is already waiting to push an element.
class type 'a bounded_push = object ... endType of sources for bounded push-streams.
val create_bounded : int -> 'a t * 'a bounded_pushcreate_bounded size returns a new stream and a bounded push source. The stream can hold a maximum of size elements. When this limit is reached, pushing a new element will block until one is consumed.
Note that you cannot clone or parse (with parse) a bounded stream. These functions will raise Invalid_argument if you try to do so.
It raises Invalid_argument if size < 0.
val return : 'a -> 'a treturn a creates a stream containing the value a and being immediately closed stream (in the sense of is_closed).
return_lwt l creates a stream returning the value that l resolves to. The value is pushed into the stream immediately after the promise becomes resolved and the stream is then immediately closed (in the sense of is_closed).
If, instead, l becomes rejected, then the stream is closed without any elements in it. Attempting to fetch elements from it will raise Empty.
val of_seq : 'a Stdlib.Seq.t -> 'a tof_seq s creates a stream returning all elements of s. The elements are evaluated from s and pushed onto the stream as the stream is consumed.
of_lwt_seq s creates a stream returning all elements of s. The elements are evaluated from s and pushed onto the stream as the stream is consumed.
val of_list : 'a list -> 'a tof_list l creates a stream returning all elements of l. The elements are pushed into the stream immediately, resulting in a closed stream (in the sense of is_closed).
val of_array : 'a array -> 'a tof_array a creates a stream returning all elements of a. The elements are pushed into the stream immediately, resulting in a closed stream (in the sense of is_closed).
val of_string : string -> char tof_string str creates a stream returning all characters of str. The characters are pushed into the stream immediately, resulting in a closed stream (in the sense of is_closed).
clone st clone the given stream. Operations on each stream will not affect the other.
For example:
# let st1 = Lwt_stream.of_list [1; 2; 3];;
+val st1 : int Lwt_stream.t = <abstr>
+# let st2 = Lwt_stream.clone st1;;
+val st2 : int Lwt_stream.t = <abstr>
+# lwt x = Lwt_stream.next st1;;
+val x : int = 1
+# lwt y = Lwt_stream.next st2;;
+val y : int = 1It raises Invalid_argument if st is a bounded push-stream.
Returns the word composed of all characters of the given stream
peek st returns the first element of the stream, if any, without removing it.
npeek n st returns at most the first n elements of st, without removing them.
get st removes and returns the first element of the stream, if any.
nget n st removes and returns at most the first n elements of st.
get_while f st returns the longest prefix of st where all elements satisfy f.
next st removes and returns the next element of the stream or fails with Empty, if the stream is empty.
last_new st returns the last element that can be obtained without sleeping, or wait for one if none is available.
It fails with Empty if the stream has no more elements.
junk_while f st removes all elements at the beginning of the streams which satisfy f.
junk_old st removes all elements that are ready to be read without yielding from st.
val get_available : 'a t -> 'a listget_available st returns all available elements of l without blocking.
val get_available_up_to : int -> 'a t -> 'a listget_available_up_to n st returns up to n elements of l without blocking.
val is_closed : 'a t -> boolis_closed st returns whether the given stream has been closed. A closed stream is not necessarily empty. It may still contain unread elements. If is_closed s = true, then all subsequent reads until the end of the stream are guaranteed not to block.
closed st returns a thread that will sleep until the stream has been closed.
Note: all the following functions are destructive.
For example:
# let st1 = Lwt_stream.of_list [1; 2; 3];;
+val st1 : int Lwt_stream.t = <abstr>
+# let st2 = Lwt_stream.map string_of_int st1;;
+val st2 : string Lwt_stream.t = <abstr>
+# lwt x = Lwt_stream.next st1;;
+val x : int = 1
+# lwt y = Lwt_stream.next st2;;
+val y : string = "2"choose l creates an stream from a list of streams. The resulting stream will return elements returned by any stream of l in an unspecified order.
filter f st keeps only values, x, such that f x is true
filter_map f st filter and map st at the same time
map_list f st applies f on each element of st and flattens the lists returned
fold f s x fold_like function for streams.
iter f s iterates over all elements of the stream.
iter_n ?max_concurrency f s iterates over all elements of the stream s. Iteration is performed concurrently with up to max_threads concurrent instances of f.
Iteration is not guaranteed to be in order as this function will attempt to always process max_concurrency elements from s at once.
find_map f s find and map at the same time.
combine s1 s2 combines two streams. The stream will end when either stream ends.
append s1 s2 returns a stream which returns all elements of s1, then all elements of s2
wrap_exn s is a stream s' such that each time s yields a value v, s' yields Result.Ok v, and when the source of s raises an exception e, s' yields Result.Error e.
Note that push-streams (as returned by create) never raise exceptions.
If the stream source keeps raising the same exception e each time the stream is read, s' is unbounded. Reading it will produce Result.Error e indefinitely.
parse st f parses st with f. If f raise an exception, st is restored to its previous state.
It raises Invalid_argument if st is a bounded push-stream.
Lwt_switchLwt switches
Switch has two goals:
For example, consider the following interface:
type id
+
+val free : id -> unit Lwt.t
+
+val f : unit -> id Lwt.t
+val g : unit -> id Lwt.t
+val h : unit -> id Lwt.tNow you want to call f, g and h in parallel. You can simply do:
lwt idf = f () and idg = g () and idh = h () in
+...However, one may want to handle possible failures of f (), g () and h (), and disable all allocated resources if one of these three threads fails. This may be hard since you have to remember which one failed and which one returned correctly.
Now if we change the interface a little bit:
val f : ?switch : Lwt_switch.t -> unit -> id Lwt.t
+val g : ?switch : Lwt_switch.t -> unit -> id Lwt.t
+val h : ?switch : Lwt_switch.t -> unit -> id Lwt.tthe code becomes:
Lwt_switch.with_switch (fun switch ->
+ lwt idf = f ~switch ()
+ and idg = g ~switch ()
+ and idh = h ~switch () in
+ ...
+)val create : unit -> tcreate () creates a new switch.
with_switch fn is fn switch, where switch is a fresh switch that is turned off when the callback thread finishes (whether it succeeds or fails).
val is_on : t -> boolis_on switch returns true if the switch is currently on, and false otherwise.
turn_off switch turns off the switch. It calls all registered hooks, waits for all of them to terminate, then returns. If one of the hooks failed, it will fail with the exception raised by the hook. If the switch is already off, it does nothing.
val check : t option -> unitcheck switch does nothing if switch is None or contains an switch that is currently on, and raises Off otherwise.
Lwt_sysSystem informations.
Not_available(feature) is an exception that may be raised when a feature is not available on the current system.
type feature = [ | `wait4| `get_cpu| `get_affinity| `set_affinity| `recv_msg| `send_msg| `fd_passing| `get_credentials| `mincore| `madvise| `fdatasync| `libev ]Features that can be tested.
val have : feature -> boolTest whether the given feature is available on the current system.
val byte_order : byte_orderThe byte order used by the computer running the program.
Make.Hval hash : t -> intA hashing function on keys. It must be such that if two keys are equal according to equal, then they have identical hash values as computed by hash. Examples: suitable (equal, hash) pairs for arbitrary key types include
(=), hash) for comparing objects by structure (provided objects do not contain floats)(fun x y -> compare x y = 0), hash) for comparing objects by structure and handling Stdlib.nan correctly(==), hash) for comparing objects by physical equality (e.g. for mutable or cyclic objects).Lwt_throttle.Makemodule H : Stdlib.Hashtbl.HashedTypetype key = H.tval create : rate:int -> max:int -> n:int -> tCreates a rate limiter.
Lwt_throttle.wait limiter channel returns a new promise associated with the given rate limiter and channel.
If the maximum number of pending promises for limiter has not been reached, the promise starts pending. It will be resolved with true at some future time, such that the rate limit of limiter is not exceeded, with respect to other promises in the same channel.
If the maximum number of pending promises has been reached, the returned promise is already resolved with false.
Lwt_throttleRate limiters.
A rate limiter allows generating sets of promises that will be resolved in the future, at a maximum rate of N promises per second.
The rate limiters in this module support multiple channels, each given a different key by the user. The rate limit applies to each channel independently.
Lwt_throttle.Sval create : rate:int -> max:int -> n:int -> tCreates a rate limiter.
Lwt_throttle.wait limiter channel returns a new promise associated with the given rate limiter and channel.
If the maximum number of pending promises for limiter has not been reached, the promise starts pending. It will be resolved with true at some future time, such that the rate limit of limiter is not exceeded, with respect to other promises in the same channel.
If the maximum number of pending promises has been reached, the returned promise is already resolved with false.
Lwt_timeoutCancelable timeouts.
val create : int -> (unit -> unit) -> tLwt_timeout.create n f creates a new timeout object with duration n seconds. f is the action, a function to be called once the timeout expires. f should not raise exceptions.
The timeout is not started until Lwt_timeout.start is called on it.
val start : t -> unitStarts the given timeout.
Starting a timeout that has already been started has the same effect as stopping it, and then restarting it with its original duration. So, suppose you have timeout with a duration of three seconds, which was started two seconds ago. The next call to its action is scheduled for one second in the future. Calling Lwt_timeout.start timeout at this point cancels this upcoming action call, and schedules a call three seconds from now.
val stop : t -> unitStops (cancels) the given timeout.
val change : t -> int -> unitChanges the duration of the given timeout.
If the timeout has already been started, it is stopped, and restarted with its new duration. This is similar to how Lwt_timeout.start works on a timeout that has already been started.
Lwt_timeout.set_exn_handler f sets the handler to be used for exceptions raised by timeout actions. Recall that actions are not allowed to raise exceptions. If they do raise an exception exn despite this, f exn is called.
The default behavior of f exn, set by Lwt_timeout on program startup, is to pass exn to !Lwt.async_exception_hook. The default behavior of that is to terminate the process.
Lwt_unix.IO_vectorsSequences of buffer slices for writev.
Mutable sequences of I/O vectors. An I/O vector describes a slice of a bytes or Bigarray buffer. Each I/O vector is a triple containing a reference to the buffer, an offset into the buffer where the slice begins, and the length of the slice.
type _bigarray =
+ (char, Stdlib.Bigarray.int8_unsigned_elt, Stdlib.Bigarray.c_layout)
+ Stdlib.Bigarray.Array1.tType abbreviation equivalent to Lwt_bytes.t. Do not use this type name directly; use Lwt_bytes.t instead.
val create : unit -> tCreates an empty I/O vector sequence.
val append_bytes : t -> bytes -> int -> int -> unitappend_bytes vs buffer offset length appends a slice of the bytes buffer buffer beginning at offset and with length length to the I/O vector sequence vs.
append_bigarray vs buffer offset length appends a slice of the Bigarray buffer buffer beginning at offset and with length length to the I/O vector sequence vs.
val drop : t -> int -> unitdrop vs n adjusts the I/O vector sequence vs so that it no longer includes its first n bytes.
val is_empty : t -> boolis_empty vs is true if and only if vs has no I/O vectors, or all I/O vectors in vs have zero bytes.
val byte_count : t -> intbyte_count vs is the total number of bytes in vs.
Some systems limit the number of I/O vectors that can be passed in a single call to their writev or readv system calls. On those systems, if the limit is n, this value is equal to Some n. On systems without such a limit, the value is equal to None.
Unless you need atomic I/O operations, you can ignore this limit. The Lwt binding automatically respects it internally. See Lwt_unix.writev.
A typical limit is 1024 vectors.
Lwt_unix.LargeFileval lseek : file_descr -> int64 -> seek_command -> int64 Lwt.tWrapper for Unix.LargeFile.lseek
val truncate : string -> int64 -> unit Lwt.tWrapper for Unix.LargeFile.truncate
val ftruncate : file_descr -> int64 -> unit Lwt.tWrapper for Unix.LargeFile.ftruncate
type stats = Unix.LargeFile.stats = {st_dev : int;st_ino : int;st_kind : file_kind;st_perm : file_perm;st_nlink : int;st_uid : int;st_gid : int;st_rdev : int;st_size : int64;st_atime : float;st_mtime : float;st_ctime : float;}Wrapper for Unix.LargeFile.stat
Wrapper for Unix.LargeFile.lstat
val fstat : file_descr -> stats Lwt.tWrapper for Unix.LargeFile.fstat
val file_exists : string -> bool Lwt.tfile_exists name tests if a file named name exists.
Note that file_exists behaves similarly to Sys.file_exists:
file_exists name will return false in circumstances that would make stat raise a Unix.Unix_error exception.Lwt_unix.VersionedVersioned variants of APIs undergoing breaking changes.
val bind_1 : file_descr -> sockaddr -> unitOld version of Lwt_unix.bind. The current Lwt_unix.bind evaluates to a promise, because the internal bind(2) system call can block if the given socket is a Unix domain socket.
val bind_2 : file_descr -> sockaddr -> unit Lwt.tSince Lwt 3.0.0, this is just an alias for Lwt_unix.bind.
val recv_msg_2 :
+ socket:file_descr ->
+ io_vectors:IO_vectors.t ->
+ (int * Unix.file_descr list) Lwt.tSince Lwt 5.0.0, this is an alias for Lwt_unix.recv_msg.
val send_msg_2 :
+ socket:file_descr ->
+ io_vectors:IO_vectors.t ->
+ fds:Unix.file_descr list ->
+ int Lwt.tSince Lwt 5.0.0, this is an alias for Lwt_unix.send_msg.
Lwt_unixCooperative system calls
This modules maps system calls, like those of the standard library's Unix module, to cooperative ones, which will not block the program.
The semantics of all operations is the following: if the action (for example reading from a file descriptor) can be performed immediately, it is performed and returns an already resolved promise, otherwise it returns a pending promise which is resolved when the operation completes.
Most operations on sockets and pipes (on Windows it is only sockets) are cancelable, meaning you can cancel them with Lwt.cancel. For example if you want to read something from a file descriptor with a timeout, you can cancel the action after the timeout and the reading will not be performed if not already done.
For example, consider that you have two sockets sock1 and sock2. You want to read something from sock1 or exclusively from sock2 and fail with an exception if a timeout of 1 second expires, without reading anything from sock1 and sock2, even if they become readable in the future.
Then you can do:
Lwt.pick
+ [Lwt_unix.timeout 1.0;
+ read sock1 buf1 ofs1 len1;
+ read sock2 buf2 ofs2 len2]In this case, it is guaranteed that exactly one of the three operations will complete, and the others will be cancelled.
Same as Unix.handle_unix_error but catches lwt-level exceptions
val sleep : float -> unit Lwt.tsleep d is a promise that remains in a pending state for d seconds after which it is resolved with value ().
val yield : unit -> unit Lwt.tyield () is a promise in a pending state. It resumes itself as soon as possible and resolves with value ().
val auto_yield : float -> unit -> unit Lwt.tval auto_pause : float -> unit -> unit Lwt.tauto_pause timeout returns a function f, and f () has the following behavior:
timeout seconds since the last time f () behaved like Lwt.pause, f () calls Lwt.pause.timeout seconds, f () behaves like Lwt.return_unit, i.e. it does not yield.val timeout : float -> 'a Lwt.ttimeout d is a promise that remains pending for d seconds and then is rejected with Timeout.
with_timeout d f is a short-hand for:
Lwt.pick [Lwt_unix.timeout d; f ()]The abstract type for file descriptors. A Lwt file descriptor is a pair of a unix file descriptor (of type Unix.file_descr) and a state.
A file descriptor may be:
type state = | OpenedThe file descriptor is opened
*)| Closed| Aborted of exnThe file descriptor has been aborted, the only operation possible is close, all others will fail.
State of a file descriptor
val state : file_descr -> statestate fd returns the state of fd.
val unix_file_descr : file_descr -> Unix.file_descrReturns the underlying unix file descriptor. It always succeeds, even if the file descriptor's state is not Opened.
val of_unix_file_descr :
+ ?blocking:bool ->
+ ?set_flags:bool ->
+ Unix.file_descr ->
+ file_descrWraps a Unix file descriptor fd in an Lwt file_descr fd'.
~blocking controls the internal strategy Lwt uses to perform I/O on the underlying fd. Regardless of ~blocking, at the API level, Lwt_unix.read, Lwt_unix.write, etc. on fd' always block the Lwt promise, but never block the whole process. However, for performance reasons, it is important that ~blocking match the actual blocking mode of fd.
If ~blocking is not specified, of_unix_file_descr chooses non-blocking mode for Unix sockets, Unix pipes, and Windows sockets, and blocking mode for everything else. Note: not specifying ~blocking causes fstat to be lazily called on fd, the first time your code performs I/O on fd'. This fstat call can be expensive, so if you use of_unix_file_descr a lot, be sure to specify ~blocking explicitly.
of_unix_file_descr runs a system call to set the specified or chosen blocking mode on the underlying fd.
To prevent of_unix_file_descr from running this system call, you can pass ~set_flags:false. Note that, in this case, if ~blocking, whether passed explicitly or chosen by Lwt, does not match the true blocking mode of the underlying fd, I/O on fd' will suffer performance degradation.
Note that ~set_flags is effectively always false if running on Windows and fd is not a socket.
Generally, non-blocking I/O is faster: for blocking I/O, Lwt typically has to run system calls in worker threads to avoid blocking the process. See your system documentation for whether particular kinds of file descriptors support non-blocking I/O.
val blocking : file_descr -> bool Lwt.tblocking fd indicates whether Lwt is internally using blocking or non-blocking I/O with fd.
Note that this may differ from the blocking mode of the underlying Unix file descriptor (i.e. unix_file_descr fd).
See of_unix_file_descr for details.
val set_blocking : ?set_flags:bool -> file_descr -> bool -> unitset_blocking fd b causes Lwt to internally use blocking or non-blocking I/O with fd, according to the value of b.
If ~set_flags is true (the default), Lwt also makes a system call to set the underlying file descriptor's blocking mode to match. Otherwise, set_blocking is only informational for Lwt.
It is important that the underlying file descriptor actually have the same blocking mode as that indicated by b.
See of_unix_file_descr for details.
val abort : file_descr -> exn -> unitabort fd exn makes all current and further uses of the file descriptor fail with the given exception. This put the file descriptor into the Aborted state.
If the file descriptor is closed, this does nothing, if it is aborted, this replace the abort exception by exn.
Note that this only works for reading and writing operations on file descriptors supporting non-blocking mode.
fork () does the same as Unix.fork. You must use this function instead of Unix.fork when you want to use Lwt in the child process, even if you have not started using Lwt before the fork.
Notes:
Lwt_unix I/O jobs are abandoned. This may cause the child's copy of their associated promises to remain forever pending.Lwt_io.flush_all before callling fork to avoid double-flush.Lwt_main.Exit_hooks.remove_all to avoid Lwt calling Lwt_main.run during process exit.exec. Indeed, in that case, it is not even necessary to use Lwt_unix.fork. You can use Unix.fork.Lwt_main.abandon_yielded_and_paused.val wait : unit -> (int * process_status) Lwt.tWrapper for Unix.wait
val waitpid : wait_flag list -> int -> (int * process_status) Lwt.tA promise-returning analog to Unix.waitpid. This call is non-blocking on Unix-like systems, but is always blocking on Windows.
Resource usages
val wait4 :
+ wait_flag list ->
+ int ->
+ (int * process_status * resource_usage) Lwt.twait4 flags pid returns (pid, status, rusage) where (pid, status) is the same result as Unix.waitpid flags pid, and rusage contains accounting information about the child.
On windows it will always returns { utime = 0.0; stime = 0.0 }.
Returns the number of promises waiting for a child process to terminate.
val system : string -> process_status Lwt.tExecutes the given command, waits until it terminates, and return its termination status. The string is interpreted by the shell /bin/sh on Unix and cmd.exe on Windows. The result WEXITED 127 indicates that the shell couldn't be executed.
val stdin : file_descrThe file descriptor for standard input.
val stdout : file_descrThe file descriptor for standard output.
val stderr : file_descrThe file descriptor for standard error.
type file_perm = Unix.file_permtype open_flag = Unix.open_flag = val openfile : string -> open_flag list -> file_perm -> file_descr Lwt.tWrapper for Unix.openfile.
val close : file_descr -> unit Lwt.tClose a file descriptor. This close the underlying unix file descriptor and set its state to Closed.
val read : file_descr -> bytes -> int -> int -> int Lwt.tread fd buf ofs len reads up to len bytes from fd, and writes them to buf, starting at offset ofs. The function immediately evaluates to an Lwt promise which waits for the operation to complete. If it completes successfully, the promise resolves to the number of bytes actually read, or zero if the end of file has been reached.
Note that the Lwt promise waits for data (or end of file) even if the underlying file descriptor is in non-blocking mode. See of_unix_file_descr for a discussion of non-blocking I/O and Lwt.
If Lwt is using blocking I/O on fd, read writes data into a temporary buffer, then copies it into buf.
The promise can be rejected with any exception that can be raised by Unix.read, except Unix.Unix_error Unix.EAGAIN, Unix.Unix_error Unix.EWOULDBLOCK or Unix.Unix_error Unix.EINTR.
val pread : file_descr -> bytes -> file_offset:int -> int -> int -> int Lwt.tpread fd buf ~file_offset ofs len on file descriptors allowing seek, reads up to len bytes from fd at offset file_offset from the beginning of the file, and writes them to buf, starting at offset ofs.
On Unix systems, the file descriptor position is unaffected. On Windows it is changed to be just after the last read position.
The promise can be rejected with any exception that can be raised by read or lseek.
val write : file_descr -> bytes -> int -> int -> int Lwt.twrite fd buf ofs len writes up to len bytes to fd from buf, starting at buffer offset ofs. The function immediately evaluates to an Lwt promise which waits for the operation to complete. If the operation completes successfully, the promise resolves to the number of bytes actually written, which may be less than len.
Note that the Lwt promise waits to write even if the underlying file descriptor is in non-blocking mode. See of_unix_file_descr for a discussion of non-blocking I/O and Lwt.
If Lwt is using blocking I/O on fd, buf is copied before writing.
The promise can be rejected with any exception that can be raised by Unix.single_write, except Unix.Unix_error Unix.EAGAIN, Unix.Unix_error Unix.EWOULDBLOCK or Unix.Unix_error Unix.EINTR.
val pwrite : file_descr -> bytes -> file_offset:int -> int -> int -> int Lwt.tpwrite fd buf ~file_offset ofs len on file descriptors allowing seek, writes up to len bytes to fd from buf, starting at buffer offset ofs. The data is written at offset file_offset from the beginning of fd.
On Unix systems, the file descriptor position is unaffected. On Windows it is changed to be just after the last written position.
The promise can be rejected with any exception that can be raised by write or lseek.
val write_string : file_descr -> string -> int -> int -> int Lwt.tSee write.
val pwrite_string :
+ file_descr ->
+ string ->
+ file_offset:int ->
+ int ->
+ int ->
+ int Lwt.tSee pwrite.
module IO_vectors : sig ... endSequences of buffer slices for writev.
val readv : file_descr -> IO_vectors.t -> int Lwt.treadv fd vs reads bytes from fd into the buffer slices vs. If the operation completes successfully, the resulting promise resolves to the number of bytes read.
Data is always read directly into Bigarray slices. If the Unix file descriptor underlying fd is in non-blocking mode, data is also read directly into bytes slices. Otherwise, data for bytes slices is first read into temporary buffers, then copied.
Note that the returned Lwt promise is pending until failure or a successful read, even if the underlying file descriptor is in non-blocking mode. See of_unix_file_descr for a discussion of non-blocking I/O and Lwt.
If IO_vectors.system_limit is Some n and the count of slices in vs exceeds n, then Lwt_unix.readv reads only into the first n slices of vs.
Not implemented on Windows. It should be possible to implement, upon request, for Windows sockets only.
See readv(3p).
val writev : file_descr -> IO_vectors.t -> int Lwt.twritev fd vs writes the bytes in the buffer slices vs to the file descriptor fd. If the operation completes successfully, the resulting promise resolves to the number of bytes written.
If the Unix file descriptor underlying fd is in non-blocking mode, writev does not make a copy the bytes before writing. Otherwise, it copies bytes slices, but not Bigarray slices.
Note that the returned Lwt promise is pending until failure or a successful write, even if the underlying descriptor is in non-blocking mode. See of_unix_file_descr for a discussion of non-blocking I/O and Lwt.
If IO_vectors.system_limit is Some n and the count of slices in vs exceeds n, then Lwt_unix.writev passes only the first n slices in vs to the underlying writev system call.
Not implemented on Windows. It should be possible to implement, upon request, for Windows sockets only.
The behavior of writev when vs has zero slices depends on the system, and may change in future versions of Lwt. On Linux, writev will succeed and write zero bytes. On BSD (including macOS), writev will fail with Unix.Unix_error (Unix.EINVAL, "writev", ...).
See writev(3p).
val readable : file_descr -> boolReturns whether the given file descriptor is currently readable.
val writable : file_descr -> boolReturns whether the given file descriptor is currently writable.
val wait_read : file_descr -> unit Lwt.tWaits (without blocking other promises) until there is something to read from the file descriptor.
Note that you don't need to use this function if you are using Lwt I/O functions for reading, since they provide non-blocking waiting automatically.
The intended use case for this function is interfacing with existing libraries that are known to be blocking.
val wait_write : file_descr -> unit Lwt.tWaits (without blocking other promises) until it is possible to write on the file descriptor.
Note that you don't need to use this function if you are using Lwt I/O functions for writing, since they provide non-blocking waiting automatically.
The intended use case for this function is interfacing with existing libraries that are known to be blocking.
val lseek : file_descr -> int -> seek_command -> int Lwt.tWrapper for Unix.lseek
val truncate : string -> int -> unit Lwt.tWrapper for Unix.truncate
val ftruncate : file_descr -> int -> unit Lwt.tWrapper for Unix.ftruncate
val fsync : file_descr -> unit Lwt.tSynchronise all data and metadata of the file descriptor with the disk. On Windows it uses FlushFileBuffers.
val fdatasync : file_descr -> unit Lwt.tSynchronise all data (but not metadata) of the file descriptor with the disk.
Note that fdatasync is not available on Windows and OS X.
type stats = Unix.stats = {st_dev : int;st_ino : int;st_kind : file_kind;st_perm : file_perm;st_nlink : int;st_uid : int;st_gid : int;st_rdev : int;st_size : int;st_atime : float;st_mtime : float;st_ctime : float;}Wrapper for Unix.lstat
val fstat : file_descr -> stats Lwt.tWrapper for Unix.fstat
val file_exists : string -> bool Lwt.tfile_exists name tests if a file named name exists.
Note that file_exists behaves similarly to Sys.file_exists:
file_exists name will return false in circumstances that would make stat raise a Unix.Unix_error exception.val utimes : string -> float -> float -> unit Lwt.tutimes path atime mtime updates the access and modification times of the file at path. The access time is set to atime and the modification time to mtime. To set both to the current time, call utimes path 0. 0..
This function corresponds to Unix.utimes. See also utimes(3p).
val isatty : file_descr -> bool Lwt.tWrapper for Unix.isatty
module LargeFile : sig ... endval unlink : string -> unit Lwt.tWrapper for Unix.unlink
val rename : string -> string -> unit Lwt.tWrapper for Unix.rename
Wrapper for Unix.chmod
val fchmod : file_descr -> file_perm -> unit Lwt.tWrapper for Unix.fchmod
val chown : string -> int -> int -> unit Lwt.tWrapper for Unix.chown
val fchown : file_descr -> int -> int -> unit Lwt.tWrapper for Unix.fchown
val access : string -> access_permission list -> unit Lwt.tWrapper for Unix.access
val dup : ?cloexec:bool -> file_descr -> file_descrWrapper for Unix.dup
val dup2 : ?cloexec:bool -> file_descr -> file_descr -> unitWrapper for Unix.dup2
val set_close_on_exec : file_descr -> unitWrapper for Unix.set_close_on_exec
val clear_close_on_exec : file_descr -> unitWrapper for Unix.clear_close_on_exec
Wrapper for Unix.mkdir
val rmdir : string -> unit Lwt.tWrapper for Unix.rmdir
val chdir : string -> unit Lwt.tWrapper for Unix.chdir
val getcwd : unit -> string Lwt.tWrapper for Unix.getcwd
val chroot : string -> unit Lwt.tWrapper for Unix.chroot
type dir_handle = Unix.dir_handleval opendir : string -> dir_handle Lwt.tOpens a directory for listing. Directories opened with this function must be explicitly closed with closedir. This is a cooperative analog of Unix.opendir.
val readdir : dir_handle -> string Lwt.tReads the next directory entry from the given directory. Special entries such as . and .. are included. If all entries have been read, raises End_of_file. This is a cooperative analog of Unix.readdir.
val readdir_n : dir_handle -> int -> string array Lwt.treaddir_n handle count reads at most count entries from the given directory. It is more efficient than calling readdir count times. If the length of the returned array is smaller than count, this means that the end of the directory has been reached.
val rewinddir : dir_handle -> unit Lwt.tResets the given directory handle, so that directory listing can be restarted. Cooperative analog of Unix.rewinddir.
val closedir : dir_handle -> unit Lwt.tCloses a directory handle. Cooperative analog of Unix.closedir.
val files_of_directory : string -> string Lwt_stream.tfiles_of_directory dir returns the stream of all files of dir.
val pipe : ?cloexec:bool -> unit -> file_descr * file_descrpipe () creates pipe using Unix.pipe and returns two lwt file descriptors created from unix file_descriptor
val pipe_in : ?cloexec:bool -> unit -> file_descr * Unix.file_descrpipe_in () is the same as pipe but maps only the unix file descriptor for reading into a lwt one. The second is not put into non-blocking mode. You usually want to use this before forking to receive data from the child process.
val pipe_out : ?cloexec:bool -> unit -> Unix.file_descr * file_descrpipe_out () is the inverse of pipe_in. You usually want to use this before forking to send data to the child process
Wrapper for Unix.mkfifo
val symlink : ?to_dir:bool -> string -> string -> unit Lwt.tWrapper for Unix.symlink
val readlink : string -> string Lwt.tWrapper for Unix.readlink
val lockf : file_descr -> lock_command -> int -> unit Lwt.tWrapper for Unix.lockf
type passwd_entry = Unix.passwd_entry = {pw_name : string;pw_passwd : string;pw_uid : int;pw_gid : int;pw_gecos : string;pw_dir : string;pw_shell : string;}val getlogin : unit -> string Lwt.tWrapper for Unix.getlogin
val getpwnam : string -> passwd_entry Lwt.tWrapper for Unix.getpwnam
val getgrnam : string -> group_entry Lwt.tWrapper for Unix.getgrnam
val getpwuid : int -> passwd_entry Lwt.tWrapper for Unix.getpwuid
val getgrgid : int -> group_entry Lwt.tWrapper for Unix.getgrgid
val on_signal : int -> (int -> unit) -> signal_handler_idon_signal signum f calls f each time the signal with numnber signum is received by the process. It returns a signal handler identifier that can be used to stop monitoring signum.
val on_signal_full :
+ int ->
+ (signal_handler_id -> int -> unit) ->
+ signal_handler_idon_signal_full f is the same as on_signal f except that f also receive the signal handler identifier as argument so it can disable it.
val disable_signal_handler : signal_handler_id -> unitStops receiving this signal
reinstall_signal_handler signum if any signal handler is registered for this signal with on_signal, it reinstall the signal handler (with Sys.set_signal). This is useful in case another part of the program install another signal handler.
handle_signal signum acts as if Lwt had received the signum signal. This allows another IO library to install the handler, perform its own handling, but still notify Lwt. It is particularly useful for SIGCHLD, where several IO libraries may be spawning sub-processes.
This function is thread-safe.
type inet_addr = Unix.inet_addrval socket : ?cloexec:bool -> socket_domain -> socket_type -> int -> file_descrsocket domain type proto is the same as Unix.socket but maps the result into a lwt file descriptor
val socketpair :
+ ?cloexec:bool ->
+ socket_domain ->
+ socket_type ->
+ int ->
+ file_descr * file_descrWrapper for Unix.socketpair
val bind : file_descr -> sockaddr -> unit Lwt.tval listen : file_descr -> int -> unitWrapper for Unix.listen
val accept : ?cloexec:bool -> file_descr -> (file_descr * sockaddr) Lwt.tWrapper for Unix.accept
val accept_n :
+ ?cloexec:bool ->
+ file_descr ->
+ int ->
+ ((file_descr * sockaddr) list * exn option) Lwt.taccept_n fd count accepts up to count connections at one time.
count are available, it returns all of themcount are available, it returns the next count of themaccept_n has the advantage of improving performance. If you want a more detailed description, you can have a look at:
val connect : file_descr -> sockaddr -> unit Lwt.tWrapper for Unix.connect
val shutdown : file_descr -> shutdown_command -> unitWrapper for Unix.shutdown
val getsockname : file_descr -> sockaddrWrapper for Unix.getsockname
val getpeername : file_descr -> sockaddrWrapper for Unix.getpeername
val recv : file_descr -> bytes -> int -> int -> msg_flag list -> int Lwt.tWrapper for Unix.recv.
On Windows, recv writes data into a temporary buffer, then copies it into the given one.
val recvfrom :
+ file_descr ->
+ bytes ->
+ int ->
+ int ->
+ msg_flag list ->
+ (int * sockaddr) Lwt.tWrapper for Unix.recvfrom.
On Windows, recvfrom writes data into a temporary buffer, then copies it into the given one.
val send : file_descr -> bytes -> int -> int -> msg_flag list -> int Lwt.tWrapper for Unix.send.
On Windows, send copies the given buffer before writing.
val sendto :
+ file_descr ->
+ bytes ->
+ int ->
+ int ->
+ msg_flag list ->
+ sockaddr ->
+ int Lwt.tWrapper for Unix.sendto.
On Windows, sendto copies the given buffer before writing.
val recv_msg :
+ socket:file_descr ->
+ io_vectors:IO_vectors.t ->
+ (int * Unix.file_descr list) Lwt.trecv_msg ~socket ~io_vectors receives data into a list of io-vectors, plus any file-descriptors that may accompany the messages. It returns a tuple whose first field is the number of bytes received and second is a list of received file descriptors. The messages themselves will be recorded in the provided io_vectors list. Data is written directly into the iov_buffer buffers.
Not implemented on Windows.
val send_msg :
+ socket:file_descr ->
+ io_vectors:IO_vectors.t ->
+ fds:Unix.file_descr list ->
+ int Lwt.tsend_msg ~socket ~io_vectors ~fds sends data from a list of io-vectors, accompanied with a list of file-descriptors. It returns the number of bytes sent. If fd-passing is not possible on the current system and fds is not empty, it raises Lwt_sys.Not_available "fd_passing". Data is written directly from the io_vectors buffers.
Not implemented on Windows.
val send_msgto :
+ socket:file_descr ->
+ io_vectors:IO_vectors.t ->
+ fds:Unix.file_descr list ->
+ dest:Unix.sockaddr ->
+ int Lwt.tsend_msgto ~socket ~io_vectors ~fds ~dest is similar to send_msg but takes an additional dest argument to set the address when using a connection-less socket.
Not implemented on Windows.
val get_credentials : file_descr -> credentialsget_credentials fd returns credentials information from the given socket. On some platforms, obtaining the peer pid is not possible and it will be set to -1. If obtaining credentials is not possible on the current system, it raises Lwt_sys.Not_available "get_credentials".
This call is not available on windows.
type socket_bool_option = Unix.socket_bool_option = type socket_int_option = Unix.socket_int_option = type socket_float_option = Unix.socket_float_option = | SO_RCVTIMEO| SO_SNDTIMEONote: these options are provided for the sake of completeness only. Lwt places all sockets in non-blocking mode, for which these options are meaningless. Use Lwt.pick with Lwt_unix.sleep or Lwt_unix.timeout for timeouts.
val getsockopt : file_descr -> socket_bool_option -> boolWrapper for Unix.getsockopt
val setsockopt : file_descr -> socket_bool_option -> bool -> unitWrapper for Unix.setsockopt
val getsockopt_int : file_descr -> socket_int_option -> intWrapper for Unix.getsockopt_int
val setsockopt_int : file_descr -> socket_int_option -> int -> unitWrapper for Unix.setsockopt_int
val getsockopt_optint : file_descr -> socket_optint_option -> int optionWrapper for Unix.getsockopt_optint
val setsockopt_optint :
+ file_descr ->
+ socket_optint_option ->
+ int option ->
+ unitWrapper for Unix.setsockopt_optint
val getsockopt_float : file_descr -> socket_float_option -> floatWrapper for Unix.getsockopt_float
val setsockopt_float : file_descr -> socket_float_option -> float -> unitWrapper for Unix.setsockopt_float
val getsockopt_error : file_descr -> Unix.error optionWrapper for Unix.getsockopt_error
val mcast_set_loop : file_descr -> bool -> unitWhether sent multicast messages are received by the sending host
val mcast_set_ttl : file_descr -> int -> unitSet TTL/hops value
val mcast_add_membership :
+ file_descr ->
+ ?ifname:Unix.inet_addr ->
+ Unix.inet_addr ->
+ unitmcast_add_membership fd ~ifname addr joins the multicast group addr on the network interface ifname.
val mcast_drop_membership :
+ file_descr ->
+ ?ifname:Unix.inet_addr ->
+ Unix.inet_addr ->
+ unitmcast_drop_membership fd ~ifname addr leaves the multicast group addr on the network interface ifname.
type host_entry = Unix.host_entry = {h_name : string;h_aliases : string array;h_addrtype : socket_domain;h_addr_list : inet_addr array;}val gethostname : unit -> string Lwt.tWrapper for Unix.gethostname
val gethostbyname : string -> host_entry Lwt.tWrapper for Unix.gethostbyname
val gethostbyaddr : inet_addr -> host_entry Lwt.tWrapper for Unix.gethostbyaddr
val getprotobyname : string -> protocol_entry Lwt.tWrapper for Unix.getprotobyname
val getprotobynumber : int -> protocol_entry Lwt.tWrapper for Unix.getprotobynumber
val getservbyname : string -> string -> service_entry Lwt.tWrapper for Unix.getservbyname
val getservbyport : int -> string -> service_entry Lwt.tWrapper for Unix.getservbyport
type addr_info = Unix.addr_info = {ai_family : socket_domain;ai_socktype : socket_type;ai_protocol : int;ai_addr : sockaddr;ai_canonname : string;}type getaddrinfo_option = Unix.getaddrinfo_option = | AI_FAMILY of socket_domain| AI_SOCKTYPE of socket_type| AI_PROTOCOL of int| AI_NUMERICHOST| AI_CANONNAME| AI_PASSIVEval getaddrinfo :
+ string ->
+ string ->
+ getaddrinfo_option list ->
+ addr_info list Lwt.tWrapper for Unix.getaddrinfo
type getnameinfo_option = Unix.getnameinfo_option = val getnameinfo : sockaddr -> getnameinfo_option list -> name_info Lwt.tWrapper for Unix.getnameinfo
type terminal_io = Unix.terminal_io = {mutable c_ignbrk : bool;mutable c_brkint : bool;mutable c_ignpar : bool;mutable c_parmrk : bool;mutable c_inpck : bool;mutable c_istrip : bool;mutable c_inlcr : bool;mutable c_igncr : bool;mutable c_icrnl : bool;mutable c_ixon : bool;mutable c_ixoff : bool;mutable c_opost : bool;mutable c_obaud : int;mutable c_ibaud : int;mutable c_csize : int;mutable c_cstopb : int;mutable c_cread : bool;mutable c_parenb : bool;mutable c_parodd : bool;mutable c_hupcl : bool;mutable c_clocal : bool;mutable c_isig : bool;mutable c_icanon : bool;mutable c_noflsh : bool;mutable c_echo : bool;mutable c_echoe : bool;mutable c_echok : bool;mutable c_echonl : bool;mutable c_vintr : char;mutable c_vquit : char;mutable c_verase : char;mutable c_vkill : char;mutable c_veof : char;mutable c_veol : char;mutable c_vmin : int;mutable c_vtime : int;mutable c_vstart : char;mutable c_vstop : char;}val tcgetattr : file_descr -> terminal_io Lwt.tWrapper for Unix.tcgetattr
val tcsetattr : file_descr -> setattr_when -> terminal_io -> unit Lwt.tWrapper for Unix.tcsetattr
val tcsendbreak : file_descr -> int -> unit Lwt.tWrapper for Unix.tcsendbreak
val tcdrain : file_descr -> unit Lwt.tWrapper for Unix.tcdrain
val tcflush : file_descr -> flush_queue -> unit Lwt.tWrapper for Unix.tcflush
val tcflow : file_descr -> flow_action -> unit Lwt.tWrapper for Unix.tcflow
For system calls that cannot be made asynchronously, Lwt uses one of the following method:
val default_async_method : unit -> async_methodReturns the default async method.
This can be initialized using the environment variable "LWT_ASYNC_METHOD" with possible values "none", "detach" and "switch".
val set_default_async_method : async_method -> unitSets the default async method.
val async_method : unit -> async_methodasync_method () returns the async method used in the current thread.
val async_method_key : async_method Lwt.keyThe key for storing the local async method.
with_async_none f is a shorthand for:
Lwt.with_value async_method_key (Some Async_none) fwith_async_detach f is a shorthand for:
Lwt.with_value async_method_key (Some Async_detach) fwith_async_switch f is a shorthand for:
Lwt.with_value async_method_key (Some Async_switch) fIf an action raises Retry, it will be requeued until the file descriptor becomes readable/writable again.
If an action raises Retry_read, it will be requeued until the file descriptor becomes readable.
If an action raises Retry_read, it will be requeued until the file descriptor becomes writables.
val wrap_syscall : io_event -> file_descr -> (unit -> 'a) -> 'a Lwt.twrap_syscall set fd action wrap an action on a file descriptor. It tries to execute action, and if it can not be performed immediately without blocking, it is registered for later.
In the latter case, if the promise is canceled, action is removed from set.
val check_descriptor : file_descr -> unitcheck_descriptor fd raise an exception if fd is not in the state Open.
val register_action : io_event -> file_descr -> (unit -> 'a) -> 'a Lwt.tregister_action set fd action registers action on fd. When fd becomes readable/writable action is called.
Note:
check_descriptor fd before calling register_actionwrap_syscallType of job descriptions. A job description describe how to call a C function and how to get its result. The C function may be executed in another system thread.
val run_job : ?async_method:async_method -> 'a job -> 'a Lwt.trun_job ?async_method job starts job and wait for its termination.
The ~async_method argument will be ignored in Lwt 5.0.0, and this function will always act as if ~async_method:Async_detach is passed.
The async method is chosen follow:
async_method is specified, it is used,async_method_key is set in the current thread, it is used,default_async_method) is used.If the method is Async_none then the job is run synchronously and may block the current system thread, thus blocking all Lwt threads.
If the method is Async_detach then the job is run in another system thread, unless the the maximum number of worker threads has been reached (as given by pool_size).
If the method is Async_switch then the job is run synchronously and if it blocks, execution will continue in another system thread (unless the limit is reached).
abort_jobs exn make all pending jobs to fail with exn. Note that this does not abort the real job (i.e. the C function executing it), just the lwt thread for it.
val wait_for_jobs : unit -> unit Lwt.tWait for all pending jobs to terminate.
val execute_job :
+ ?async_method:async_method ->
+ job:'a job ->
+ result:('a job -> 'b) ->
+ free:('a job -> unit) ->
+ 'b Lwt.tLwt internally use a pipe to send notification to the main thread. The following functions allow to use this pipe.
make_notification ?once f registers a new notifier. It returns the id of the notifier. Each time a notification with this id is received, f is called.
if once is specified, then the notification is stopped after the first time it is received. It defaults to false.
send_notification id sends a notification.
This function is thread-safe.
Stop the given notification. Note that you should not reuse the id after the notification has been stopped, the result is unspecified if you do so.
Call the handler associated to the given notification. Note that if the notification was defined with once = true it is removed.
set_notification id f replace the function associated to the notification by f. It raises Not_found if the given notification is not found.
If the program is using the async method Async_detach or Async_switch, Lwt will launch system threads to execute blocking system calls asynchronously.
Maximum number of system threads that can be started. If this limit is reached, jobs will be executed synchronously.
get_affinity ?pid () returns the list of CPUs the process with pid pid is allowed to run on. If pid is not specified then the affinity of the current process is returned.
set_affinity ?pid cpus sets the list of CPUs the given process is allowed to run on.
module Versioned : sig ... endVersioned variants of APIs undergoing breaking changes.
Lwt is a concurrent programming library for OCaml. It provides a single data type: the promise, which is a value that will become determined in the future. Creating a promise spawns a computation. When that computation is I/O, Lwt runs it in parallel with your OCaml code.
OCaml code, including creating and waiting on promises, is run in a single thread by default, so you don't have to worry about locking or preemption. You can detach code to be run in separate threads on an opt-in basis.
Here is a simplistic Lwt program which requests the Google front page, and fails if the request is not completed in five seconds:
open Lwt.Syntax
+
+let () =
+ let request =
+ let* addresses = Lwt_unix.getaddrinfo "google.com" "80" [] in
+ let google = Lwt_unix.((List.hd addresses).ai_addr) in
+
+ Lwt_io.(with_connection google (fun (incoming, outgoing) ->
+ let* () = write outgoing "GET / HTTP/1.1\r\n" in
+ let* () = write outgoing "Connection: close\r\n\r\n" in
+ let* response = read incoming in
+ Lwt.return (Some response)))
+ in
+
+ let timeout =
+ let* () = Lwt_unix.sleep 5. in
+ Lwt.return None
+ in
+
+ match Lwt_main.run (Lwt.pick [request; timeout]) with
+ | Some response -> print_string response
+ | None -> prerr_endline "Request timed out"; exit 1
+
+(* ocamlfind opt -package lwt.unix -linkpkg example.ml && ./a.out *)In the program, functions such as Lwt_io.write create promises. The let%lwt ... in construct is used to wait for a promise to become determined; the code after in is scheduled to run in a "callback." Lwt.pick races promises against each other, and behaves as the first one to complete. Lwt_main.run forces the whole promise-computation network to be executed. All the visible OCaml code is run in a single thread, but Lwt internally uses a combination of worker threads and non-blocking file descriptors to resolve in parallel the promises that do I/O.
Lwt compiles to native code on Linux, macOS, Windows, and other systems. It's also routinely compiled to JavaScript for the front end and Node by js_of_ocaml.
In Lwt,
Lwt provides promises...Lwt_unix, that binds almost every Unix system call. A higher-level module Lwt_io provides nice I/O channels.Lwt_process is for subprocess handling.Lwt_preemptive spawns system threads.libev-dev or libev-devel.opam install conf-libev lwtlwtThis is the system-independent, pure-OCaml core of Lwt. To link with it, use (libraries lwt) in your dune file.
Lwt Asynchronous programming with promises.Lwt_list List helpersLwt_stream Data streamsLwt_result Explicit error handlingLwt_mutex Cooperative locks for mutual exclusionLwt_condition ConditionsLwt_mvar Mailbox variablesLwt_switch Lwt switchesLwt_pool External resource pools.lwt.unixThis is the system call and I/O library. Despite its name, it is implemented on both Unix-like systems and Windows, although not all functions are available on Windows. To link with this library, use (libraries lwt.unix) in your dune file.
Lwt_unix Cooperative system callsLwt_main Main loop and event queueLwt_io Buffered byte channelsLwt_process Process managementLwt_bytes Byte arraysLwt_preemptive This module allows to mix preemptive threads with Lwt cooperative threads. It maintains an extensible pool of preemptive threads to which you can detach computations.Lwt_fmt Format API for Lwt-powered IOsLwt_throttle Rate limiters.Lwt_timeout Cancelable timeouts.Lwt_engine Lwt unix main loop engineLwt_gc Interaction with the garbage collectorLwt_sys System informations.| changes-files | |
| license-files | |
| readme-files |
Moonpool_lwt.IOIO using the Lwt event loop.
These IO operations work on non-blocking file descriptors and rely on a Lwt_engine event loop being active (meaning, Lwt_main.run is currently running in some thread).
Calling these functions must be done from a moonpool runner. A function like read will first try to perform the IO action directly (here, call Unix.read); if the action fails because the FD is not ready, then await_readable is called: it suspends the fiber and subscribes it to Lwt to be awakened when the FD becomes ready.
val read : Unix.file_descr -> bytes -> int -> int -> intRead from the file descriptor
val await_readable : Unix.file_descr -> unitSuspend the fiber until the FD is readable
val write_once : Unix.file_descr -> bytes -> int -> int -> intPerform one write into the file descriptor
val await_writable : Unix.file_descr -> unitSuspend the fiber until the FD is writable
val write : Unix.file_descr -> bytes -> int -> int -> unitLoop around write_once to write the entire slice.
IO_in.tRead into the slice. Returns 0 only if the stream is closed.
Moonpool_lwt.IO_inInput channel
class type t = object ... endval create :
+ ?close:(unit -> unit) ->
+ input:(bytes -> int -> int -> int) ->
+ unit ->
+ tval empty : tval of_bytes : ?off:int -> ?len:int -> bytes -> tval of_string : ?off:int -> ?len:int -> string -> tval input : t -> bytes -> int -> int -> intRead into the given slice.
val really_input : t -> bytes -> int -> int -> unitval really_input_string : t -> int -> stringval input_all : ?buf:bytes -> t -> stringval of_unix_fd : ?close_noerr:bool -> ?buf:bytes -> Unix.file_descr -> tIO_out.tMoonpool_lwt.IO_outOutput channel
class type t = object ... endval create :
+ ?flush:(unit -> unit) ->
+ ?close:(unit -> unit) ->
+ output_char:(char -> unit) ->
+ output:(bytes -> int -> int -> unit) ->
+ unit ->
+ tval dummy : tval of_unix_fd : ?close_noerr:bool -> ?buf:bytes -> Unix.file_descr -> tval of_buffer : Stdlib.Buffer.t -> tval output_char : t -> char -> unitOutput the buffer slice into this channel
val output : t -> bytes -> int -> int -> unitOutput the buffer slice into this channel
val output_string : t -> string -> unitval output_line : t -> string -> unitval output_int : t -> int -> unitval output_lines : t -> string Stdlib.Seq.t -> unitMoonpool_lwt.TCP_clientval connect : Unix.sockaddr -> Unix.file_descrval with_connect : Unix.sockaddr -> (IO_in.t -> IO_out.t -> 'a) -> 'aOpen a connection, and use IO to read and write from the socket in a non blocking way.
val with_connect_lwt :
+ Unix.sockaddr ->
+ (Lwt_io.input_channel -> Lwt_io.output_channel -> 'a) ->
+ 'aOpen a connection.
Moonpool_lwt.TCP_servertype t = Lwt_io.serverval establish_lwt :
+ ?backlog:int ->
+ ?no_close:bool ->
+ runner:Moonpool.Runner.t ->
+ Unix.sockaddr ->
+ (Unix.sockaddr -> Lwt_io.input_channel -> Lwt_io.output_channel -> unit) ->
+ testablish ~runner addr handler runs a TCP server in the Lwt thread. When a client connects, a moonpool fiber is started on runner to handle it.
val establish :
+ ?backlog:int ->
+ ?no_close:bool ->
+ runner:Moonpool.Runner.t ->
+ Unix.sockaddr ->
+ (Unix.sockaddr -> IO_in.t -> IO_out.t -> unit) ->
+ tLike establish_lwt but uses IO to directly handle reads and writes on client sockets.
val shutdown : t -> unitShutdown the server
Moonpool_lwtLwt_engine-based event loop for Moonpool.
In what follows, we mean by "lwt thread" the thread running Lwt_main.run (so, the thread where the Lwt event loop and all Lwt callbacks execute).
module Fiber = Moonpool_fib.Fibermodule FLS = Moonpool_fib.Flsval fut_of_lwt : 'a Lwt.t -> 'a Moonpool.Fut.tfut_of_lwt lwt_fut makes a thread-safe moonpool future that completes when lwt_fut does. This must be run from within the Lwt thread.
val lwt_of_fut : 'a Moonpool.Fut.t -> 'a Lwt.tlwt_of_fut fut makes a lwt future that completes when fut does. This must be called from the Lwt thread, and the result must always be used only from inside the Lwt thread.
val await_lwt : 'a Lwt.t -> 'aawait_lwt fut awaits a Lwt future from inside a task running on a moonpool runner. This must be run from within a Moonpool runner so that the await-ing effect is handled.
val run_in_lwt : (unit -> 'a Lwt.t) -> 'a Moonpool.Fut.trun_in_lwt f runs f() from within the Lwt thread and returns a thread-safe future. This can be run from anywhere.
val run_in_lwt_and_await : (unit -> 'a Lwt.t) -> 'arun_in_lwt_and_await f runs f in the Lwt thread, and awaits its result. Must be run from inside a moonpool runner so that the await-in effect is handled.
This is similar to Moonpool.await @@ run_in_lwt f.
val get_runner : unit -> Moonpool.Runner.tReturns the runner from within which this is called. Must be run from within a fiber.
module IO : sig ... endIO using the Lwt event loop.
module IO_in : sig ... endInput channel
module IO_out : sig ... endOutput channel
module TCP_server : sig ... endmodule TCP_client : sig ... endval detach_in_runner : runner:Moonpool.Runner.t -> (unit -> 'a) -> 'a Lwt.tdetach_in_runner ~runner f runs f in the given moonpool runner, and returns a lwt future. This must be run from within the thread running Lwt_main.
val main_with_runner : runner:Moonpool.Runner.t -> (unit -> 'a) -> 'amain_with_runner ~runner f starts a Lwt-based event loop and runs f() inside a fiber in runner.
Like main_with_runner but with a default choice of runner.
Moonpool_lwt__This module is hidden.
Moonpool_lwt__BaseThis module is hidden.
Moonpool_lwt__IOThis module is hidden.
Moonpool_lwt__IO_inThis module is hidden.
Moonpool_lwt__IO_outThis module is hidden.
Moonpool_lwt__Tcp_clientThis module is hidden.
Moonpool_lwt__Tcp_serverThis module is hidden.
+You are assuming that, if pool P1 has 5000 tasks, and pool P2 has 10 other tasks, then these 10 tasks will get to run faster than if we just added them at the end of pool P1. This sounds like a “fairness” assumption: separate pools will get comparable shares of domain compute ressources, or at least no pool will be delayed too much from running their first tasks. + +[…] + +- each pool uses a fixed number of threads, all running simultaneously; if there are more tasks sent to the pool, they are delayed and will only get one of the pool threads when previous tasks have finished +- separate pools run their separate threads simultaneously, so they compete for compute resources on their domain using OCaml’s systhreads scheduler – which does provide fairness in practice +- as a result, running in a new pool enables quicker completion than adding to an existing pool (as we will be scheduled right away instead of waiting for previous tasks in our pool to free some threads) +- the ratio of compute resources that each pool gets should be roughly proportional to its number of worker threads ++ +## OCaml versions + +This works for OCaml >= 4.08. +- On OCaml 4.xx, there are no domains, so this is just a library for regular thread pools + with not actual parallelism (except for threads that call C code that releases the runtime lock, that is). + C calls that do release the runtime lock (e.g. to call [Z3](https://github.com/Z3Prover/z3), hash a file, etc.) + will still run in parallel. +- on OCaml 5.xx, there is a fixed pool of domains (using the recommended domain count). + These domains do not do much by themselves, but we schedule new threads on them, and form pools + of threads that contain threads from each domain. + Each domain might thus have multiple threads that belong to distinct pools (and several threads from + the same pool, too — this is useful for threads blocking on IO); Each pool will have threads + running on distinct domains, which enables parallelism. + + A useful analogy is that each domain is a bit like a CPU core, and `Thread.t` is a logical thread running on a core. + Multiple threads have to share a single core and do not run in parallel on it[^2]. + We can therefore build pools that spread their worker threads on multiple cores to enable parallelism within each pool. + +TODO: actually use https://github.com/haesbaert/ocaml-processor to pin domains to cores, +possibly optionally using `select` in dune. + +## License + +MIT license. + +## Install + +```sh, skip +$ opam install moonpool +``` + +[^2]: ignoring hyperthreading for the sake of the analogy. diff --git a/dev/moonpool-lwt/index.html b/dev/moonpool-lwt/index.html new file mode 100644 index 00000000..468ef79d --- /dev/null +++ b/dev/moonpool-lwt/index.html @@ -0,0 +1,2 @@ + +
Moonpool_lwt Lwt_engine-based event loop for Moonpool.Create a new runner.
NOTE: the runner should support DLA and Suspend_ on OCaml 5.x, so that Fork_join and other 5.x features work properly.
val k_cur_runner : t option ref Moonpool_private.Thread_local_storage_.keyKey that should be used by each runner to store itself in TLS on every thread it controls, so that tasks running on these threads can access the runner. This is necessary for get_current_runner to work.