Skip to content

Commit d978ea0

Browse files
committed
feature: concurrency action
We add a (concurrent ) action which acts like (progn ) the difference being the actions contained within can be executed concurrently by Dune. <!-- ps-id: 28962076-9451-4253-be23-14d44a88eec0 --> Signed-off-by: Ali Caglayan <alizter@gmail.com>
1 parent 43c27fb commit d978ea0

File tree

22 files changed

+231
-15
lines changed

22 files changed

+231
-15
lines changed

CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ Unreleased
2828
- Bytecode executables built for JSOO are linked with `-noautolink` and no
2929
longer depend on the shared stubs of their dependent libraries (#7156, @nojb)
3030

31+
- Added a new user action `(concurrent )` which is like `(progn )` but runs the
32+
actions concurrently. (#6933, @Alizter)
33+
3134
3.7.0 (2023-02-17)
3235
------------------
3336

doc/concepts.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,10 @@ The following constructions are available:
789789
``setenv``, ``ignore-<outputs>``, ``with-stdin-from`` and
790790
``with-<outputs>-to``. This action is available since Dune 2.0.
791791
- ``(progn <DSL>...)`` to execute several commands in sequence
792+
- ``(concurrent <DSL>...)``` to execute several commands concurrently.
793+
**Warning:** This is limited by the number of available jobs to Dune.
794+
Therefore care must be taken when writing actions that require a concurrent
795+
run, such as running a server-client test with `-j 1`.
792796
- ``(echo <string>)`` to output a string on stdout
793797
- ``(write-file <file> <string>)`` writes ``<string>`` to ``<file>``
794798
- ``(cat <file> ...)`` to sequentially print the contents of files to stdout

src/dune_engine/action.ml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ struct
6161
| Ignore (outputs, r) ->
6262
List [ atom (sprintf "ignore-%s" (Outputs.to_string outputs)); encode r ]
6363
| Progn l -> List (atom "progn" :: List.map l ~f:encode)
64+
| Concurrent l -> List (atom "concurrent" :: List.map l ~f:encode)
6465
| Echo xs -> List (atom "echo" :: List.map xs ~f:string)
6566
| Cat xs -> List (atom "cat" :: List.map xs ~f:path)
6667
| Copy (x, y) -> List [ atom "copy"; path x; target y ]
@@ -118,6 +119,8 @@ struct
118119

119120
let progn ts = Progn ts
120121

122+
let concurrent ts = Concurrent ts
123+
121124
let echo s = Echo s
122125

123126
let cat ps = Cat ps
@@ -289,7 +292,7 @@ let fold_one_step t ~init:acc ~f =
289292
| Redirect_in (_, _, t)
290293
| Ignore (_, t)
291294
| With_accepted_exit_codes (_, t) -> f acc t
292-
| Progn l | Pipe (_, l) -> List.fold_left l ~init:acc ~f
295+
| Progn l | Pipe (_, l) | Concurrent l -> List.fold_left l ~init:acc ~f
293296
| Run _
294297
| Dynamic_run _
295298
| Echo _
@@ -337,7 +340,7 @@ let rec is_dynamic = function
337340
| Redirect_in (_, _, t)
338341
| Ignore (_, t)
339342
| With_accepted_exit_codes (_, t) -> is_dynamic t
340-
| Progn l | Pipe (_, l) -> List.exists l ~f:is_dynamic
343+
| Progn l | Pipe (_, l) | Concurrent l -> List.exists l ~f:is_dynamic
341344
| Run _
342345
| System _
343346
| Bash _
@@ -386,7 +389,7 @@ let is_useful_to distribute memoize =
386389
| Redirect_out (_, _, _, t) -> memoize || loop t
387390
| Redirect_in (_, _, t) -> loop t
388391
| Ignore (_, t) | With_accepted_exit_codes (_, t) -> loop t
389-
| Progn l | Pipe (_, l) -> List.exists l ~f:loop
392+
| Progn l | Pipe (_, l) | Concurrent l -> List.exists l ~f:loop
390393
| Echo _ -> false
391394
| Cat _ -> memoize
392395
| Copy _ -> memoize

src/dune_engine/action_exec.ml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,15 @@ type done_or_more_deps =
6767
subdirectories that contains targets having the same name. *)
6868
| Need_more_deps of (DAP.Dependency.Set.t * Dynamic_dep.Set.t)
6969

70+
let done_or_more_deps_union x y =
71+
match (x, y) with
72+
| Done, Done -> Done
73+
| Done, Need_more_deps x | Need_more_deps x, Done -> Need_more_deps x
74+
| Need_more_deps (deps1, dyn_deps1), Need_more_deps (deps2, dyn_deps2) ->
75+
Need_more_deps
76+
( DAP.Dependency.Set.union deps1 deps2
77+
, Dynamic_dep.Set.union dyn_deps1 dyn_deps2 )
78+
7079
type exec_context =
7180
{ targets : Targets.Validated.t option
7281
; context : Build_context.t option
@@ -273,6 +282,9 @@ let rec exec t ~display ~ectx ~eenv =
273282
| Ignore (outputs, t) ->
274283
redirect_out t ~display ~ectx ~eenv ~perm:Normal outputs Config.dev_null
275284
| Progn ts -> exec_list ts ~display ~ectx ~eenv
285+
| Concurrent ts ->
286+
Fiber.parallel_map ts ~f:(exec ~display ~ectx ~eenv)
287+
>>| List.fold_left ~f:done_or_more_deps_union ~init:Done
276288
| Echo strs ->
277289
let+ () = exec_echo eenv.stdout_to (String.concat strs ~sep:" ") in
278290
Done

src/dune_engine/action_intf.ml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ module type Ast = sig
4141
| Redirect_in of Inputs.t * path * t
4242
| Ignore of Outputs.t * t
4343
| Progn of t list
44+
| Concurrent of t list
4445
| Echo of string list
4546
| Cat of path list
4647
| Copy of path * target
@@ -91,6 +92,8 @@ module type Helpers = sig
9192

9293
val progn : t list -> t
9394

95+
val concurrent : t list -> t
96+
9497
val echo : string list -> t
9598

9699
val cat : path list -> t

src/dune_engine/action_mapper.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ module Make (Src : Action_intf.Ast) (Dst : Action_intf.Ast) = struct
3030
Redirect_in (inputs, f_path ~dir fn, f t ~dir)
3131
| Ignore (outputs, t) -> Ignore (outputs, f t ~dir)
3232
| Progn l -> Progn (List.map l ~f:(fun t -> f t ~dir))
33+
| Concurrent l -> Concurrent (List.map l ~f:(fun t -> f t ~dir))
3334
| Echo xs -> Echo (List.map xs ~f:(f_string ~dir))
3435
| Cat xs -> Cat (List.map xs ~f:(f_path ~dir))
3536
| Copy (x, y) -> Copy (f_path ~dir x, f_target ~dir y)

src/dune_engine/action_to_sh.ml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ module Simplified = struct
1515
| Redirect_in of t list * Inputs.t * source
1616
| Pipe of t list list * Outputs.t
1717
| Sh of string
18+
| Concurrent of t list list
1819
end
1920

2021
open Simplified
@@ -55,6 +56,7 @@ let simplify act =
5556
| Ignore (outputs, act) ->
5657
Redirect_out (block act, outputs, Dev_null) :: acc
5758
| Progn l -> List.fold_left l ~init:acc ~f:(fun acc act -> loop act acc)
59+
| Concurrent l -> Concurrent (List.map ~f:block l) :: acc
5860
| Echo xs -> echo (String.concat xs ~sep:"")
5961
| Cat x -> cat x :: acc
6062
| Copy (x, y) -> Run ("cp", [ x; y ]) :: acc
@@ -172,6 +174,27 @@ and pp = function
172174
; Pp.concat ~sep:(Pp.verbatim " | ") (List.map l ~f:block)
173175
; Pp.verbatim end_
174176
]))
177+
| Concurrent t -> (
178+
match t with
179+
| [] -> Pp.verbatim "true"
180+
| [ x ] -> block x
181+
| x :: l ->
182+
Pp.hovbox ~indent:2
183+
(Pp.concat
184+
[ Pp.char '('
185+
; Pp.space
186+
; block x
187+
; Pp.space
188+
; Pp.char '&'
189+
; Pp.space
190+
; Pp.concat ~sep:(Pp.verbatim "&") (List.map l ~f:block)
191+
; Pp.space
192+
; Pp.char '&'
193+
; Pp.space
194+
; Pp.verbatim "wait"
195+
; Pp.space
196+
; Pp.verbatim ")"
197+
]))
175198

176199
let rec pp_seq = function
177200
| [] -> Pp.verbatim "true"

src/dune_lang/action.ml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ type t =
8585
| Redirect_in of Inputs.t * String_with_vars.t * t
8686
| Ignore of Outputs.t * t
8787
| Progn of t list
88+
| Concurrent of t list
8889
| Echo of String_with_vars.t list
8990
| Cat of String_with_vars.t list
9091
| Copy of String_with_vars.t * String_with_vars.t
@@ -210,6 +211,9 @@ let decode =
210211
; ("ignore-stderr", t >>| fun t -> Ignore (Stderr, t))
211212
; ("ignore-outputs", t >>| fun t -> Ignore (Outputs, t))
212213
; ("progn", repeat t >>| fun l -> Progn l)
214+
; ( "concurrent"
215+
, Syntax.since Stanza.syntax (3, 8) >>> repeat t >>| fun l ->
216+
Concurrent l )
213217
; ( "echo"
214218
, let+ x = sw
215219
and+ xs = repeat sw in
@@ -299,6 +303,7 @@ let rec encode =
299303
| Ignore (outputs, r) ->
300304
List [ atom (sprintf "ignore-%s" (Outputs.to_string outputs)); encode r ]
301305
| Progn l -> List (atom "progn" :: List.map l ~f:encode)
306+
| Concurrent l -> List (atom "concurrent" :: List.map l ~f:encode)
302307
| Echo xs -> List (atom "echo" :: List.map xs ~f:sw)
303308
| Cat xs -> List (atom "cat" :: List.map xs ~f:sw)
304309
| Copy (x, y) -> List [ atom "copy"; sw x; sw y ]
@@ -355,7 +360,7 @@ let ensure_at_most_one_dynamic_run ~loc action =
355360
| Mkdir _
356361
| Diff _
357362
| Cram _ -> false
358-
| Pipe (_, ts) | Progn ts ->
363+
| Pipe (_, ts) | Progn ts | Concurrent ts ->
359364
List.fold_left ts ~init:false ~f:(fun acc t ->
360365
let have_dyn = loop t in
361366
if acc && have_dyn then
@@ -383,6 +388,7 @@ let rec map_string_with_vars t ~f =
383388
| Redirect_in (i, sw, t) -> Redirect_in (i, f sw, t)
384389
| Ignore (o, t) -> Ignore (o, map_string_with_vars t ~f)
385390
| Progn xs -> Progn (List.map xs ~f:(map_string_with_vars ~f))
391+
| Concurrent xs -> Concurrent (List.map xs ~f:(map_string_with_vars ~f))
386392
| Echo xs -> Echo xs
387393
| Cat xs -> Cat (List.map ~f xs)
388394
| Copy (sw1, sw2) -> Copy (f sw1, f sw2)

src/dune_lang/action.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ type t =
7878
| Redirect_in of Inputs.t * String_with_vars.t * t
7979
| Ignore of Outputs.t * t
8080
| Progn of t list
81+
| Concurrent of t list
8182
| Echo of String_with_vars.t list
8283
| Cat of String_with_vars.t list
8384
| Copy of String_with_vars.t * String_with_vars.t

src/dune_rules/action_unexpanded.ml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,9 @@ let rec expand (t : Dune_lang.Action.t) ~context : Action.t Action_expander.t =
417417
| Progn l ->
418418
let+ l = A.all (List.map l ~f:expand) in
419419
O.Progn l
420+
| Concurrent l ->
421+
let+ l = A.all (List.map l ~f:expand) in
422+
O.Concurrent l
420423
| Echo xs ->
421424
let+ l = A.all (List.map xs ~f:E.strings) in
422425
let l = List.concat l in

0 commit comments

Comments
 (0)