-
Notifications
You must be signed in to change notification settings - Fork 176
/
lwt.mli
2086 lines (1563 loc) · 75.5 KB
/
lwt.mli
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(* This file is part of Lwt, released under the MIT license. See LICENSE.md for
details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *)
(** Asynchronous programming with promises.
A {b 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 [ref]s:
- A promise might not have a value yet. A promise in this state is called a
{e pending} promise.
- Writing a value into a promise is called {e resolving} it. A promise with
a value is called a {e resolved} promise.
- Each promise can be resolved only once. After a promise has a value, the
promise is immutable.
- It's possible to attach {b callbacks} to a promise. They will run when the
promise has a value, i.e. is resolved. If the promise is already resolved
when a callback is attached, the callback is run (almost) right away. If
the promise is pending, the callback is put into a list and waits.
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 {e 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 {e 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:
- {{: #3_Quickstart} Quick start} links these concepts to actual functions
in Lwt – the most fundamental ones.
- {{: #3_Tutorial} Tutorial} shows how to write examples like the above, and
how concurrency happens.
- {{: #3_Executionmodel} Execution model} clarifies control flow when using
Lwt.
- {{: #3_GuidetotherestofLwt} Guide to the rest of Lwt} shows how
{e everything} else in Lwt fits into this framework.
After that is the {{: #2_Fundamentals} reference proper}, which goes into
{e 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!
{3 Quick start}
{e All} of Lwt is variations on:
- {b Promises} of type ['a ]{!Lwt.t} are placeholders for values of type
['a].
- {!Lwt.bind} attaches {b callbacks} to promises. When a promise gets a
value, its callbacks are called.
- Separate {b resolvers} of type ['a ]{!Lwt.u} are used to write values into
promises, through {!Lwt.wakeup_later}.
- Promises and resolvers are created in pairs using {!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.
{3 Tutorial}
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 {e 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 {e 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 {{: #2_Concurrency} helpers} for common concurrency patterns.
{3 Execution model}
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
{{: http://man7.org/linux/man-pages/man2/select.2.html} [select(2)]},
{{: http://man7.org/linux/man-pages/man7/epoll.7.html} [epoll(7)]},
{{: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2}
[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:
+ Uses [select(2)] to put your process to sleep until the next I/O
completes.
+ That next I/O happens to be the one that reads [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.
+ Resolving is an ordinary OCaml operation. It causes all the callbacks of
[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}.
+ In our example, our one callback registers a second I/O with
{!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.
{b (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_main.yield} into it, and resume your computation
in callbacks of [yield]. This is basically the same as
{!Lwt_unix.sleep}[ 0.] – it's a promise that will be resolved by
{!Lwt_main.run} {e after} any other I/O resolutions that are already in its
queue.
{b (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 {e much}
easier to write and refactor, than equivalent programs written with threads
– but both are concurrent!
{3 Guide to the rest of Lwt}
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:
- {{: #2_Rejection} Rejection}. Lwt promises can actually be resolved in two
ways: {e fulfilled} with a value, or {e rejected} with an exception. There
is nothing conceptually special about rejection – it's just that you can
ask for callbacks to run only on fulfillment, only on rejection, etc.
- {{: #2_Cancelation} Cancellation}. This is a special case of rejection,
specifically with exception {!Lwt.Canceled}. It has extra helpers in the
Lwt API.
- {{: #2_Concurrency} Concurrency helpers}. All of these could be
implemented on top of {!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.
- Miscellaneous {{: #2_Convenience} helpers}, and {{: #2_Deprecated}
deprecated} APIs.
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...
- ...starts a background I/O operation,
- creates a promise for it and gives it to you,
- registers with {!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 [Pervasives]-like high-level channels, TCP servers, etc.
- {!Lwt_process} for managing subprocesses.
- {!Lwt_preemptive} for spawning system threads.
- Miscellaneous modules {!Lwt_gc}, {!Lwt_engine}, {!Lwt_throttle},
{!Lwt_timeout}, {!Lwt_sys}.
Warning! Introductory material ends and detailed reference begins! *)
(** {2 Fundamentals} *)
(** {3 Promises} *)
type +'a t
(** Promises for values of type ['a].
A {b promise} is a memory cell that is always in one of three {b states}:
- {e fulfilled}, and containing one value of type ['a],
- {e rejected}, and containing one exception, or
- {e pending}, in which case it may become fulfilled or rejected later.
A {e 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 {e 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 {b 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 {b read-only} in
Lwt. Separate {e 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 {e canceled} by calling
{!Lwt.cancel}, without going through a resolver. *)
type -'a u
(** Resolvers for promises of type ['a ]{!Lwt.t}.
Each resolver can be thought of as the {b 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. *)
val wait : unit -> ('a t * 'a u)
(** Creates a new pending {{: #TYPEt} promise}, paired with its {{: #TYPEu}
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. *)
(** {3 Resolving} *)
val wakeup_later : 'a u -> 'a -> unit
(** [Lwt.wakeup_later r v] {e fulfills}, with value [v], the {e pending}
{{: #TYPEt} promise} associated with {{: #TYPEu} resolver} [r]. This
triggers callbacks attached to the promise.
If the promise is not pending, [Lwt.wakeup_later] raises
{{: https://caml.inria.fr/pub/docs/manual-ocaml/libref/Stdlib.html#VALinvalid_arg}
[Invalid_argument]}, unless the promise is {{: #VALcancel} 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 -> unit
(** [Lwt.wakeup_later_exn r exn] is like {!Lwt.wakeup_later}, except, if the
associated {{: #TYPEt} promise} is {e pending}, it is {e rejected} with
[exn]. *)
val return : 'a -> 'a t
(** [Lwt.return v] creates a new {{: #TYPEt} promise} that is {e 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 {{: #VALbind} [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 -> _ t
(** [Lwt.fail exn] is like {!Lwt.return}, except the new {{: #TYPEt} promise}
that is {e 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. Use [Lwt.fail] only when you
specifically want to create a rejected promise, to pass to another function,
or store in a data structure. *)
(** {3 Callbacks} *)
val bind : 'a t -> ('a -> 'b t) -> 'b t
(** [Lwt.bind p_1 f] makes it so that [f] will run when [p_1] is {{: #TYPEt}
{e 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]. {e 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].
{b Precise behavior}
[Lwt.bind] returns a promise [p_3] immediately. [p_3] starts out pending,
and is resolved as follows:
- The first condition to wait for is that [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.
- If and when [p_1] becomes resolved, it will, by definition, be either
fulfilled or rejected.
- If [p_1] is rejected, [p_3] is rejected with the same exception.
- If [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.
- If [f] raises an exception, [p_3] is rejected with that exception.
- Finally, the remaining case is when [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.
{b 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_lwt.html} 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}. *)
(** {2 Rejection} *)
val catch : (unit -> 'a t) -> (exn -> 'a t) -> 'a t
(** [Lwt.catch f h] applies [f ()], which returns a promise, and then makes it
so that [h] (“handler”) will run when that promise is {{: #TYPEt}
{e rejected}}.
{[
let () =
Lwt_main.run begin
Lwt.catch
(fun () -> Lwt.fail Exit)
(function
| Exit -> Lwt_io.printl "Got Stdlib.Exit"
| exn -> Lwt.fail 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_lwt.html} PPX}. Here is an
equivalent example:
{[
let () =
Lwt_main.run begin
try%lwt Lwt.fail 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 -> Lwt.fail 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.
{b (1)} [Lwt.catch] first applies [f ()]. It then returns [p_3] immediately.
[p_3] starts out pending. It is resolved as follows:
- If [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.
- If [h exn] raises another exception [exn'], [p_3] is rejected with [exn'].
- If [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.
{b (2)} {b Warning}: it may be tempting to write this code, which differs
from the second example above only in that [try] is used instead of
[try%lwt]:
{[
let () =
Lwt_main.run begin
try Lwt.fail Exit
with Exit -> Lwt_io.printl "Got Stdlib.Exit"
end
(* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)
]}
This does {e not} handle the exception and does not print the message.
Instead, it terminates the program with an unhandled [Stdlib.Exit].
This is because the call to {!Lwt.fail} creates a rejected promise. The
promise is still an ordinary OCaml value, though, and not a {e raised}
exception. So, [try] considers that code to have succeeded, and doesn't run
the handler. When that rejected promise reaches {!Lwt_main.run},
it is {!Lwt_main.run} that raises the exception.
Basically, the rule is: if the code inside [try] evaluates to a promise
(has type [_ Lwt.t]), replace [try] by [try%lwt]. *)
val finalize : (unit -> 'a t) -> (unit -> unit t) -> 'a t
(** [Lwt.finalize f c] applies [f ()], which returns a promise, and then makes
it so [c] (“cleanup”) will run when that promise is {{: #TYPEt}
{e 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.
- If [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.
- After [p_1] is resolved (fulfilled or rejected), [c ()] is applied. This
is meant to be the cleanup code.
- If [c ()] finishes, it will also either return a promise, [p_2], or raise
an exception.
- If [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.
- If [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.
- If [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 {e both} the protected code and the cleanup fail, the
cleanup exception has precedence. *)
val try_bind : (unit -> 'a t) -> ('a -> 'b t) -> (exn -> 'b t) -> 'b t
(** [Lwt.try_bind f g h] applies [f ()], and then makes it so that:
- [g] will run when promise [f ()] is {{: #TYPEt} {e fulfilled}},
- [h] will run when promise [f ()] is {{: #TYPEt} {e rejected}}.
[Lwt.try_bind] is a generalized {!Lwt.finalize}. The difference is that
[Lwt.try_bind] runs different callbacks depending on {e how} [f ()] is
resolved. This has two main implications:
- The cleanup functions [g] and [h] each “know” whether [f ()] was fulfilled
or rejected.
- The cleanup functions [g] and [h] are passed the value [f ()] was
fulfilled with, and, respectively, the exception [f ()] was rejected
with.
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.
- If [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.
- If [p_1] is fulfilled, [g] is applied to the value [p_1] is fulfilled
with.
- If [p_1] is rejected, [h] is applied to the exception [p_1] is rejected
with.
- So, in either case, a callback is applied. The rest of the procedure is
the same no matter which callback was chosen, so we will refer to it as
“the callback.”
- If the callback finishes, it either returns [p_2], or raises an exception.
- If the callback raises an exception, [p_3] is rejected with that
exception.
- If the callback returns [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) -> unit
(** [Lwt.dont_wait f handler] applies [f ()], which returns a promise, and then
makes it so that if the promise is {{: #TYPEt} {e 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) {e 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) -> unit
(** [Lwt.async f] applies [f ()], which returns a promise, and then makes it so
that if the promise is {{: #TYPEt} {e 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 {b (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:
- Promises which are {e not} passed {e to} {!Lwt.bind}, {!Lwt.catch},
{!Lwt.join}, etc., are {b top-level} promises.
- One top-level promise is passed to {!Lwt_main.run}, as can be seen in most
examples in this manual.
- Every other top-level promise should be wrapped in [Lwt.async]. *)
val async_exception_hook : (exn -> unit) ref
(** Reference 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 () -> Lwt.fail Exit)
(* ocamlfind opt -linkpkg -package lwt code.ml && ./a.out *)
]}
produces in the output:
{v
Fatal error: exception Stdlib.Exit
v}
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. *)
(** {2 Concurrency} *)
(** {3 Multiple wait} *)
val both : 'a t -> 'b t -> ('a * 'b) t
(** [Lwt.both p_1 p_2] returns a promise that is pending until {e both} promises
[p_1] and [p_2] become {{: #TYPEt} {e 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.
@since 4.2.0 *)
val join : (unit t) list -> unit t
(** [Lwt.join ps] returns a promise that is pending until {e all} promises in
the list [ps] become {{: #TYPEt} {e 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. *)
val all : ('a t) list -> ('a list) t
(** [Lwt.all ps] is like {!Lwt.join}[ ps]: it waits for all promises in the list
[ps] to become {{: #TYPEt} {e 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.
@since 5.1.0 *)