Skip to content

Commit e04249d

Browse files
committed
Initial node and browser backends
1 parent e2d014f commit e04249d

16 files changed

+1357
-0
lines changed

dune-project

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,27 @@
5050
(luv_unix (>= 0.5.0))
5151
(mdx (and (>= 1.10.0) :with-test))
5252
(fmt (>= 0.8.9))))
53+
(package
54+
(name eio_node)
55+
(synopsis "Eio implementation using Nodejs")
56+
(description "An eio implementation for programs running in Nodejs.")
57+
(depends
58+
(eio (= :version))
59+
(brr (>= 0.0.4))
60+
(js_of_ocaml (>= 5.0.1))
61+
(alcotest :with-test)
62+
(logs (>= 0.7.0))
63+
(fmt (>= 0.8.9))))
64+
(package
65+
(name eio_browser)
66+
(synopsis "Eio implementation for the browser")
67+
(description "An eio implementation that is compatible with programs running in the browser.")
68+
(depends
69+
(eio (= :version))
70+
(brr (>= 0.0.4))
71+
(js_of_ocaml (>= 5.0.1))
72+
(logs (>= 0.7.0))
73+
(fmt (>= 0.8.9))))
5374
(package
5475
(name eio_main)
5576
(synopsis "Effect-based direct-style IO mainloop for OCaml")

eio_browser.opam

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# This file is generated by dune, edit dune-project instead
2+
opam-version: "2.0"
3+
synopsis: "Eio implementation for the browser"
4+
description:
5+
"An eio implementation that is compatible with programs running in the browser."
6+
maintainer: ["[email protected]"]
7+
authors: ["Anil Madhavapeddy" "Thomas Leonard"]
8+
license: "ISC"
9+
homepage: "https://github.com/ocaml-multicore/eio"
10+
doc: "https://ocaml-multicore.github.io/eio/"
11+
bug-reports: "https://github.com/ocaml-multicore/eio/issues"
12+
depends: [
13+
"dune" {>= "2.9"}
14+
"eio" {= version}
15+
"brr" {>= "0.0.4"}
16+
"js_of_ocaml" {>= "5.0.1"}
17+
"logs" {>= "0.7.0"}
18+
"fmt" {>= "0.8.9"}
19+
"odoc" {with-doc}
20+
]
21+
build: [
22+
["dune" "subst"] {dev}
23+
[
24+
"dune"
25+
"build"
26+
"-p"
27+
name
28+
"-j"
29+
jobs
30+
"--promote-install-files=false"
31+
"@install"
32+
"@runtest" {with-test}
33+
"@doc" {with-doc}
34+
]
35+
["dune" "install" "-p" name "--create-install-files" name]
36+
]
37+
dev-repo: "git+https://github.com/ocaml-multicore/eio.git"

eio_node.opam

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# This file is generated by dune, edit dune-project instead
2+
opam-version: "2.0"
3+
synopsis: "Eio implementation using Nodejs"
4+
description: "An eio implementation for programs running in Nodejs."
5+
maintainer: ["[email protected]"]
6+
authors: ["Anil Madhavapeddy" "Thomas Leonard"]
7+
license: "ISC"
8+
homepage: "https://github.com/ocaml-multicore/eio"
9+
doc: "https://ocaml-multicore.github.io/eio/"
10+
bug-reports: "https://github.com/ocaml-multicore/eio/issues"
11+
depends: [
12+
"dune" {>= "2.9"}
13+
"eio" {= version}
14+
"brr" {>= "0.0.4"}
15+
"js_of_ocaml" {>= "5.0.1"}
16+
"alcotest" {with-test}
17+
"logs" {>= "0.7.0"}
18+
"fmt" {>= "0.8.9"}
19+
"odoc" {with-doc}
20+
]
21+
build: [
22+
["dune" "subst"] {dev}
23+
[
24+
"dune"
25+
"build"
26+
"-p"
27+
name
28+
"-j"
29+
jobs
30+
"--promote-install-files=false"
31+
"@install"
32+
"@runtest" {with-test}
33+
"@doc" {with-doc}
34+
]
35+
["dune" "install" "-p" name "--create-install-files" name]
36+
]
37+
dev-repo: "git+https://github.com/ocaml-multicore/eio.git"

lib_eio_js/browser/dune

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
(library
2+
(name eio_browser)
3+
(public_name eio_browser)
4+
(libraries brr eio eio.utils js_of_ocaml))

lib_eio_js/browser/eio_browser.ml

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
(*
2+
* Copyright (C) 2021-2022 Thomas Leonard
3+
* Copyright (C) 2022 Patrick Ferris
4+
*
5+
* Permission to use, copy, modify, and distribute this software for any
6+
* purpose with or without fee is hereby granted, provided that the above
7+
* copyright notice and this permission notice appear in all copies.
8+
*
9+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16+
*)
17+
18+
open Brr
19+
20+
module Fiber_context = Eio.Private.Fiber_context
21+
module Lf_queue = Eio_utils.Lf_queue
22+
23+
module Ctf = Eio.Private.Ctf
24+
25+
module Suspended = struct
26+
type 'a t = {
27+
fiber : Eio.Private.Fiber_context.t;
28+
k : ('a, unit) Effect.Deep.continuation;
29+
}
30+
31+
let tid t = Eio.Private.Fiber_context.tid t.fiber
32+
33+
let continue t v =
34+
Ctf.note_switch (tid t);
35+
Effect.Deep.continue t.k v
36+
37+
let _discontinue t ex =
38+
Ctf.note_switch (tid t);
39+
Effect.Deep.discontinue t.k ex
40+
end
41+
42+
type t = {
43+
(* Suspended fibers waiting to run again.
44+
[Lf_queue] is like [Stdlib.Queue], but is thread-safe (lock-free) and
45+
allows pushing items to the head too, which we need. *)
46+
mutable run_q : (unit -> unit) Lf_queue.t;
47+
mutable pending_io : int;
48+
mutable timeout : int option; (* ID for the setTimeout *)
49+
}
50+
51+
let enqueue_thread t k v =
52+
Lf_queue.push t.run_q (fun () -> Suspended.continue k v)
53+
54+
type _ Effect.t += Enter_unchecked : (t -> 'a Suspended.t -> unit) -> 'a Effect.t
55+
let enter_unchecked fn = Effect.perform (Enter_unchecked fn)
56+
57+
let enter_io fn =
58+
enter_unchecked @@ fun st k ->
59+
st.pending_io <- st.pending_io + 1;
60+
fn (fun res -> enqueue_thread st k res; st.pending_io <- st.pending_io - 1)
61+
62+
(* Resume the next runnable fiber, if any. *)
63+
let rec schedule t : unit =
64+
match Lf_queue.pop t.run_q with
65+
| Some f -> f ()
66+
| None ->
67+
if t.pending_io = 0 then begin
68+
Option.iter G.stop_timer t.timeout;
69+
t.timeout <- None
70+
end
71+
else begin
72+
match t.timeout with
73+
| None ->
74+
let id = G.set_timeout ~ms:0 (fun () -> t.timeout <- None; schedule t) in
75+
t.timeout <- Some id;
76+
schedule t
77+
| Some _id -> ()
78+
end
79+
80+
module Timeout = struct
81+
let set_timeout ~ms f = ignore (G.set_timeout ~ms f)
82+
83+
let sleep ~ms =
84+
enter_io @@ set_timeout ~ms
85+
end
86+
87+
(* Largely based on the Eio_mock.Backend event loop. *)
88+
let run main =
89+
let run_q = Lf_queue.create () in
90+
let t = { run_q; pending_io = 0; timeout = None } in
91+
let rec fork ~new_fiber:fiber fn =
92+
Effect.Deep.match_with fn ()
93+
{ retc = (fun () -> Fiber_context.destroy fiber; schedule t);
94+
exnc = (fun ex ->
95+
let bt = Printexc.get_raw_backtrace () in
96+
Fiber_context.destroy fiber;
97+
Printexc.raise_with_backtrace ex bt
98+
);
99+
effc = fun (type a) (e : a Effect.t) : ((a, unit) Effect.Deep.continuation -> unit) option ->
100+
match e with
101+
| Eio.Private.Effects.Suspend f -> Some (fun k ->
102+
f fiber (function
103+
| Ok v -> Lf_queue.push t.run_q (fun () -> Effect.Deep.continue k v)
104+
| Error ex -> Lf_queue.push t.run_q (fun () -> Effect.Deep.discontinue k ex)
105+
);
106+
schedule t
107+
)
108+
| Enter_unchecked fn -> Some (fun k ->
109+
fn t { Suspended.k; fiber };
110+
schedule t
111+
)
112+
| Eio.Private.Effects.Fork (new_fiber, f) -> Some (fun k ->
113+
Lf_queue.push_head t.run_q (Effect.Deep.continue k);
114+
fork ~new_fiber f
115+
)
116+
| Eio.Private.Effects.Get_context -> Some (fun k ->
117+
Effect.Deep.continue k fiber
118+
)
119+
| _ -> None
120+
}
121+
in
122+
let new_fiber = Fiber_context.make_root () in
123+
let result, r = Fut.create () in
124+
let () = fork ~new_fiber (fun () -> r (main ())) in
125+
result

lib_eio_js/browser/eio_browser.mli

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module Timeout : sig
2+
val sleep : ms:int -> unit
3+
(** Non-blocking timeout that waits for [ms] millseconds. *)
4+
end
5+
6+
(** {1 Main loop} *)
7+
8+
val run : (unit -> 'a) -> 'a Fut.t
9+
(** [run main] runs [main] whose result is returned as a promise. *)

lib_eio_js/browser/test/dune

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
; We compile by hand because dune is a little broken w.r.t to
2+
; separate compilation, js_of_ocaml and effects.
3+
(executable
4+
(name index)
5+
(modes js)
6+
(js_of_ocaml (flags --target-env=nodejs --enable=effects --debug-info --source-map --pretty))
7+
(libraries eio_browser))
8+
9+
(rule
10+
(alias default)
11+
(deps index.html index.bc.js)
12+
(targets index.js)
13+
(action (copy index.bc.js index.js)))

lib_eio_js/browser/test/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Eio in the Browser</title>
8+
</head>
9+
<body>
10+
<script src="index.js"></script>
11+
</body>
12+
</html>

lib_eio_js/browser/test/index.ml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
open Eio
2+
3+
let () =
4+
let main =
5+
Eio_browser.run @@ fun () ->
6+
Fiber.both
7+
(fun () -> Eio_browser.Timeout.sleep ~ms:1000; traceln "World")
8+
(fun () -> traceln "Hello, ");
9+
"Done"
10+
in
11+
Fut.await main (fun v -> Brr.Console.log [ Jstr.v v ])

lib_eio_js/node/dune

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
(library
2+
(name eio_node)
3+
(public_name eio_node)
4+
(libraries eio unix num fmt eio.utils js_of_ocaml logs brr))

0 commit comments

Comments
 (0)