-
Notifications
You must be signed in to change notification settings - Fork 179
add the lwt_direct
package, for direct-style control flow
#1060
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
c-cube
wants to merge
22
commits into
ocsigen:master
Choose a base branch
from
c-cube:simon/lwt-direct
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
cd0e502
add the `lwt_direct` package, for direct-style control flow
c-cube 7145d61
Apply suggestions from code review
c-cube 8cab71e
some docs
c-cube 0978471
doc
c-cube 5b746aa
lwt: expose some storage primitives in `Private`
c-cube 33527db
lwt_direct: expose basic storage primitives
c-cube b869daa
add tests for Lwt_direct
c-cube 495852e
more tests for Lwt_direct
c-cube d776f41
CI: see if --best-effort helps
c-cube dc85027
CI
c-cube 9bee283
only test lwt_direct if OCaml >= 5.0
c-cube c5aa6c5
fix test on 4.xx
c-cube cdf51ce
dune
c-cube 6e408a8
some improvements as discussed in PR's review
raphael-proust 020ae8b
purely cosmetics tweaks
raphael-proust 92c1d3e
opam stuff
c-cube 61f338d
tighten a bit Lwt_direct, use Lwt.async_exception_hook
c-cube 05d3233
test: increase coverage
c-cube d696674
add TODO in comment
raphael-proust 8587e96
CHAGNELOG
raphael-proust 09fba3a
rename `run` to `spawn`
c-cube 24e7cce
document cancelation (or lack thereof)
c-cube File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# This file is generated by dune, edit dune-project instead | ||
opam-version: "2.0" | ||
synopsis: "Direct style control flow and `await` for Lwt" | ||
maintainer: [ | ||
"Raphaël Proust <[email protected]>" "Anton Bachin <[email protected]>" | ||
] | ||
authors: ["Jérôme Vouillon" "Jérémie Dimino"] | ||
license: "MIT" | ||
homepage: "https://github.com/ocsigen/lwt" | ||
doc: "https://ocsigen.org/lwt" | ||
bug-reports: "https://github.com/ocsigen/lwt/issues" | ||
depends: [ | ||
"dune" {>= "2.7"} | ||
"ocaml" {>= "5.0"} | ||
"base-unix" | ||
"lwt" {>= "3.0.0"} | ||
raphael-proust marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"bisect_ppx" {with-test} | ||
"odoc" {with-doc} | ||
] | ||
build: [ | ||
["dune" "subst"] {dev} | ||
[ | ||
"dune" | ||
"build" | ||
"-p" | ||
name | ||
"-j" | ||
jobs | ||
"@install" | ||
"@runtest" {with-test} | ||
"@doc" {with-doc} | ||
] | ||
] | ||
dev-repo: "git+https://github.com/ocsigen/lwt.git" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
(library | ||
(public_name lwt_direct) | ||
(synopsis "Direct style control flow and `await` for Lwt") | ||
(enabled_if (>= %{ocaml_version} "5.0")) | ||
(libraries lwt lwt.unix) | ||
(instrumentation | ||
(backend bisect_ppx))) | ||
|
||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
module ED = Effect.Deep | ||
|
||
type _ Effect.t += | ||
| Await : 'a Lwt.t -> 'a Effect.t | ||
| Yield : unit Effect.t | ||
|
||
(** Queue of microtasks that are ready *) | ||
let tasks : (unit -> unit) Queue.t = Queue.create () | ||
|
||
let[@inline] push_task f : unit = Queue.push f tasks | ||
|
||
let default_on_uncaught_exn exn bt = | ||
Printf.eprintf "lwt_task: uncaught task exception:\n%s\n%s\n%!" | ||
(Printexc.to_string exn) | ||
(Printexc.raw_backtrace_to_string bt) | ||
|
||
let run_all_tasks () : unit = | ||
let n_processed = ref 0 in | ||
let max_number_of_steps = min 10_000 (2 * Queue.length tasks) in | ||
raphael-proust marked this conversation as resolved.
Show resolved
Hide resolved
|
||
while (not (Queue.is_empty tasks)) && !n_processed < max_number_of_steps do | ||
let t = Queue.pop tasks in | ||
incr n_processed; | ||
try t () | ||
with exn -> | ||
let bt = Printexc.get_raw_backtrace () in | ||
default_on_uncaught_exn exn bt | ||
done; | ||
(* make sure we don't sleep forever if there's no lwt promise | ||
ready but [tasks] contains ready tasks *) | ||
if not (Queue.is_empty tasks) then ignore (Lwt.pause () : unit Lwt.t) | ||
raphael-proust marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
let setup_hooks = | ||
let already_done = ref false in | ||
fun () -> | ||
if not !already_done then ( | ||
already_done := true; | ||
let _hook1 = Lwt_main.Enter_iter_hooks.add_first run_all_tasks in | ||
let _hook2 = Lwt_main.Leave_iter_hooks.add_first run_all_tasks in | ||
raphael-proust marked this conversation as resolved.
Show resolved
Hide resolved
|
||
() | ||
) | ||
|
||
let await (fut : 'a Lwt.t) : 'a = | ||
match Lwt.state fut with | ||
| Lwt.Return x -> x | ||
| Lwt.Fail exn -> raise exn | ||
| Lwt.Sleep -> Effect.perform (Await fut) | ||
|
||
let yield () : unit = Effect.perform Yield | ||
|
||
(** the main effect handler *) | ||
let handler : _ ED.effect_handler = | ||
let effc : type b. b Effect.t -> ((b, unit) ED.continuation -> 'a) option = | ||
function | ||
| Yield -> | ||
Some (fun k -> push_task (fun () -> ED.continue k ())) | ||
| Await fut -> | ||
Some | ||
(fun k -> | ||
Lwt.on_any fut | ||
(fun res -> push_task (fun () -> ED.continue k res)) | ||
(fun exn -> push_task (fun () -> ED.discontinue k exn))) | ||
| _ -> None | ||
in | ||
{ effc } | ||
|
||
let run_inside_effect_handler_and_resolve_ (type a) (promise : a Lwt.u) f () : unit = | ||
let res = ref (Error (Failure "not resolved")) in | ||
let run_f_and_set_res () = | ||
(try | ||
let r = f () in | ||
res := Ok r | ||
with exn -> res := Error exn); | ||
Lwt.wakeup_result promise !res | ||
in | ||
ED.try_with run_f_and_set_res () handler | ||
|
||
let run f : _ Lwt.t = | ||
setup_hooks (); | ||
let lwt, resolve = Lwt.wait () in | ||
push_task (run_inside_effect_handler_and_resolve_ resolve f); | ||
lwt | ||
|
||
let run_inside_effect_handler_in_the_background_ ~on_uncaught_exn f () : unit = | ||
let run_f () : unit = | ||
try | ||
f () | ||
with exn -> | ||
let bt = Printexc.get_raw_backtrace () in | ||
on_uncaught_exn exn bt | ||
in | ||
ED.try_with run_f () handler | ||
|
||
let run_in_the_background ?(on_uncaught_exn=default_on_uncaught_exn) f : unit = | ||
setup_hooks (); | ||
push_task (run_inside_effect_handler_in_the_background_ ~on_uncaught_exn f) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
(** Direct style control flow for Lwt. *) | ||
|
||
val run : (unit -> 'a) -> 'a Lwt.t | ||
(** [run f] runs the function [f ()] in a task within | ||
the [Lwt_unix] event loop. [f ()] can create [Lwt] | ||
promises and use {!await} to wait for them. Like any promise | ||
in Lwt, [f ()] can starve the event loop if it runs long computations | ||
without yielding to the event loop. | ||
|
||
When [f ()] terminates (successfully or not), the promise | ||
[run f] is resolved with [f ()]'s result, or the exception | ||
raised by [f ()]. *) | ||
|
||
val run_in_the_background : | ||
?on_uncaught_exn:(exn -> Printexc.raw_backtrace -> unit) -> | ||
(unit -> unit) -> | ||
unit | ||
(** [run_in_the_background f] is similar to [ignore (run f)]. | ||
The computation [f()] runs in the background in the event loop | ||
and returns no result. | ||
@param on_uncaught_exn if provided, this is called when [f()] | ||
raises an exception. *) | ||
|
||
val yield : unit -> unit | ||
(** Yield to the event loop. | ||
Can only be used inside {!run} or {!run_in_the_background}. *) | ||
c-cube marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
val await : 'a Lwt.t -> 'a | ||
(** [await prom] returns the result of [prom], or re-raises the | ||
exception with which [prom] failed if it failed. | ||
If [prom] is not resolved yet, [await prom] will suspend the | ||
current task and resume it when [prom] is resolved. | ||
Can only be used inside {!run} or {!run_in_the_background}. *) | ||
c-cube marked this conversation as resolved.
Show resolved
Hide resolved
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.