Skip to content

Commit 6a94293

Browse files
committed
Add Event.from_computation and a Promise.completed event
1 parent 025c2a9 commit 6a94293

File tree

7 files changed

+99
-19
lines changed

7 files changed

+99
-19
lines changed

lib/picos_structured/dune

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
(library
22
(name picos_structured)
33
(public_name picos.structured)
4-
(libraries picos backoff multicore-magic))
4+
(libraries
5+
(re_export picos.sync)
6+
picos
7+
backoff
8+
multicore-magic))
59

610
(mdx
711
(enabled_if

lib/picos_structured/picos_structured.mli

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
]} *)
1414

1515
open Picos
16+
open Picos_sync
1617

1718
(** {1 Modules} *)
1819

@@ -171,6 +172,10 @@ module Promise : sig
171172
{{!Control.Terminate} [Terminate]} exception in case the promise has been
172173
canceled. *)
173174

175+
val completed : 'a t -> 'a Event.t
176+
(** [completed promise] returns an {{!Picos_sync.Event} event} that can be
177+
committed to once the promise has completed. *)
178+
174179
val is_running : 'a t -> bool
175180
(** [is_running promise] determines whether the completion of the promise is
176181
still pending. *)

lib/picos_structured/promise.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
open Picos
2+
open Picos_sync
23

34
type 'a t = 'a Computation.t
45

56
let of_value = Computation.returned
67
let await = Computation.await
8+
let completed = Event.from_computation
79
let is_running = Computation.is_running
810

911
let try_terminate ?callstack t =

lib/picos_sync/event.ml

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,52 +14,73 @@ type ('a, 'r) id = Yes : ('a, 'a) id | No : ('a, 'r) id
1414

1515
let rec request_1_as :
1616
type a r. (_ -> r) Computation.t -> (a -> r) -> (a, r) id -> a t -> _ =
17-
fun computation to_result id -> function
18-
| Request { request } -> request computation to_result
19-
| Choose ts -> request_n_as computation to_result id ts
17+
fun target to_result id -> function
18+
| Request { request } -> request target to_result
19+
| Choose ts -> request_n_as target to_result id ts
2020
| Wrap { event; fn } ->
2121
let to_result =
2222
match id with No -> fun x -> to_result (fn x) | Yes -> fn
2323
in
24-
request_1_as computation to_result No event
24+
request_1_as target to_result No event
2525

2626
and request_n_as :
2727
type a r. (_ -> r) Computation.t -> (a -> r) -> (a, r) id -> a t list -> _ =
28-
fun computation to_result id -> function
28+
fun target to_result id -> function
2929
| [] -> ()
3030
| t :: ts ->
31-
request_1_as computation to_result id t;
32-
request_n_as computation to_result id ts
31+
request_1_as target to_result id t;
32+
request_n_as target to_result id ts
3333

3434
type ('a, _) tycon = Id : ('a, 'a t) tycon | List : ('a, 'a t list) tycon
3535

3636
let sync_as : type a n. n -> (a, n) tycon -> a =
3737
fun t n ->
38-
let computation = Computation.create ~mode:`LIFO () in
38+
let target = Computation.create ~mode:`LIFO () in
3939
match
4040
match n with
41-
| Id -> request_1_as computation Fun.id Yes t
42-
| List -> request_n_as computation Fun.id Yes t
41+
| Id -> request_1_as target Fun.id Yes t
42+
| List -> request_n_as target Fun.id Yes t
4343
with
4444
| () ->
45-
if Computation.is_running computation then begin
45+
if Computation.is_running target then begin
4646
let t = Trigger.create () in
47-
if Computation.try_attach computation t then
47+
if Computation.try_attach target t then
4848
match Trigger.await t with
4949
| None -> ()
5050
| Some exn_bt ->
51-
if Computation.try_cancel computation exn_bt then
52-
Exn_bt.raise exn_bt
51+
if Computation.try_cancel target exn_bt then Exn_bt.raise exn_bt
5352
end;
54-
Computation.await computation ()
53+
Computation.await target ()
5554
| exception exn ->
5655
let exn_bt = Exn_bt.get exn in
57-
Computation.cancel computation exn_bt;
56+
Computation.cancel target exn_bt;
5857
Exn_bt.raise exn_bt
5958

6059
let guard create_event =
61-
let request computation to_result =
62-
request_1_as computation to_result No (create_event ())
60+
let request target to_result =
61+
request_1_as target to_result No (create_event ())
62+
in
63+
Request { request }
64+
65+
let[@alert "-handler"] from_computation source =
66+
let request target to_result =
67+
let result () = to_result (Computation.await source) in
68+
if Computation.is_running source then begin
69+
let propagator =
70+
Trigger.from_action result target @@ fun _ result target ->
71+
Computation.return target result
72+
in
73+
if Computation.try_attach source propagator then begin
74+
let detacher =
75+
Trigger.from_action propagator source @@ fun _ propagator source ->
76+
Computation.detach source propagator
77+
in
78+
if not (Computation.try_attach target detacher) then
79+
Computation.detach source propagator
80+
end
81+
else Computation.return target result
82+
end
83+
else Computation.return target result
6384
in
6485
Request { request }
6586

lib/picos_sync/picos_sync.mli

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,13 @@ module Event : sig
254254
val from_request : 'a request -> 'a t
255255
(** [from_request { request }] creates an {{!Event} event} from the request
256256
function. *)
257+
258+
val from_computation : 'a Computation.t -> 'a t
259+
(** [from_computation source] creates an {{!Event} event} that can be
260+
committed to once the given [source] computation has completed.
261+
262+
ℹ️ Committing to some other event does not cancel the [source]
263+
computation. *)
257264
end
258265

259266
(** {1 Examples}

test/test_structured.ml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,17 @@ let test_can_wait_promises () =
161161
in
162162
assert (Promise.await promise = 42)
163163

164+
let test_can_select_promises () =
165+
Test_scheduler.run ~max_domains:2 @@ fun () ->
166+
Bundle.join_after @@ fun bundle ->
167+
let a =
168+
Bundle.fork_as_promise bundle @@ fun () ->
169+
Control.sleep ~seconds:0.1;
170+
42
171+
and b = Bundle.fork_as_promise bundle @@ fun () -> Event.select [] in
172+
assert (Event.select [ Promise.completed a; Promise.completed b ] = 42);
173+
Bundle.terminate bundle
174+
164175
let test_any_and_all_errors () =
165176
[ Run.all; Run.any ]
166177
|> List.iter @@ fun run_op ->
@@ -255,6 +266,7 @@ let () =
255266
Alcotest.test_case "error in promise terminates" `Quick
256267
test_error_in_promise_terminates;
257268
Alcotest.test_case "can wait promises" `Quick test_can_wait_promises;
269+
Alcotest.test_case "can select promises" `Quick test_can_select_promises;
258270
Alcotest.test_case "any and all errors" `Quick test_any_and_all_errors;
259271
Alcotest.test_case "any and all returns" `Quick test_any_and_all_returns;
260272
Alcotest.test_case "race any" `Quick test_race_any;

test/test_sync.ml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,34 @@ let test_lazy_cancelation () =
203203
| _ -> assert false
204204
| exception Exit -> Trigger.signal to_await
205205

206+
let test_event_basics () =
207+
Test_scheduler.run ~max_domains:2 @@ fun () ->
208+
assert (
209+
Event.select
210+
[
211+
Event.from_computation (Computation.create ());
212+
Event.from_computation (Computation.returned 51) |> Event.map (( + ) 50);
213+
]
214+
= 101);
215+
begin
216+
let c = Computation.create () in
217+
Computation.cancel_after c ~seconds:0.1 (Exn_bt.get_callstack 0 Not_found);
218+
match Event.sync (Event.from_computation c) with
219+
| () -> assert false
220+
| exception Not_found -> ()
221+
end;
222+
begin
223+
match
224+
[
225+
Event.guard (fun () -> raise Exit);
226+
Event.from_computation (Computation.returned 42);
227+
]
228+
|> Event.choose |> Event.sync
229+
with
230+
| _ -> assert false
231+
| exception Exit -> ()
232+
end
233+
206234
let () =
207235
[
208236
( "Mutex and Condition",
@@ -217,5 +245,6 @@ let () =
217245
Alcotest.test_case "basics" `Quick test_lazy_basics;
218246
Alcotest.test_case "cancelation" `Quick test_lazy_cancelation;
219247
] );
248+
("Event", [ Alcotest.test_case "basics" `Quick test_event_basics ]);
220249
]
221250
|> Alcotest.run "Picos_sync"

0 commit comments

Comments
 (0)