Skip to content
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

Unify and simplify the error handling #4718

Merged
merged 6 commits into from
Jun 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 24 additions & 21 deletions bin/build_cmd.ml
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
open Stdune
open Import

let run_build_system ?report_error ~common ~(request : unit Action_builder.t) ()
=
let run_build_system ~common ~(request : unit Action_builder.t) () =
let build_started = Unix.gettimeofday () in
Fiber.finalize
(fun () ->
Build_system.run ?report_error (fun () -> Build_system.build request))
(fun () -> Build_system.run (fun () -> Build_system.build request))
~finally:(fun () ->
(if Common.print_metrics common then
let gc_stat = Gc.quick_stat () in
Expand All @@ -22,30 +20,35 @@ let run_build_system ?report_error ~common ~(request : unit Action_builder.t) ()

let run_build_command_poll ~(common : Common.t) ~config ~request ~setup =
let open Fiber.O in
let every ~report_error () =
Cached_digest.invalidate_cached_timestamps ();
let* setup = setup () in
let* request =
match
let open Option.O in
let* rpc = Common.rpc common in
Dune_rpc_impl.Server.pending_build_action rpc
with
| None -> Fiber.return (request setup)
| Some (Build (targets, ivar)) ->
let+ () = Fiber.Ivar.fill ivar Accepted in
Target.interpret_targets (Common.root common) config setup targets
in
let+ () = run_build_system ~report_error ~common ~request () in
`Continue
let every =
Fiber.of_thunk (fun () ->
Cached_digest.invalidate_cached_timestamps ();
let* setup = setup () in
let* request =
match
let open Option.O in
let* rpc = Common.rpc common in
Dune_rpc_impl.Server.pending_build_action rpc
with
| None -> Fiber.return (request setup)
| Some (Build (targets, ivar)) ->
let+ () = Fiber.Ivar.fill ivar Accepted in
Target.interpret_targets (Common.root common) config setup targets
in
run_build_system ~common ~request ())
in
Scheduler.poll ~common ~config ~every ~finally:Hooks.End_of_build.run

let run_build_command_once ~(common : Common.t) ~config ~request ~setup =
let open Fiber.O in
let once () =
let* setup = setup () in
run_build_system ~common ~request:(request setup) ()
let+ res = run_build_system ~common ~request:(request setup) () in
match res with
| Error `Already_reported ->
(* to ensure non-zero exit code *)
raise Dune_util.Report_error.Already_reported
| Ok () -> ()
in
Scheduler.go ~common ~config once

Expand Down
2 changes: 1 addition & 1 deletion bin/exec.ml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ let term =
in
let* prog =
let open Memo.Build.O in
Build_system.run (fun () ->
Build_system.run_exn (fun () ->
match Filename.analyze_program_name prog with
| In_path -> (
Super_context.resolve_program sctx ~dir ~loc:None prog
Expand Down
7 changes: 4 additions & 3 deletions bin/import.ml
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,10 @@ module Scheduler = struct
let file_watcher = Common.file_watcher common in
let run =
let run () =
Scheduler.Run.poll (fun ~report_error () ->
Fiber.finalize (every ~report_error) ~finally:(fun () ->
Fiber.return (finally ())))
Scheduler.Run.poll
(Fiber.finalize
(fun () -> every)
~finally:(fun () -> Fiber.return (finally ())))
in
match Common.rpc common with
| None -> run
Expand Down
2 changes: 1 addition & 1 deletion bin/print_rules.ml
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ let term =
Scheduler.go ~common ~config (fun () ->
let open Fiber.O in
let* setup = Import.Main.setup () in
Build_system.run (fun () ->
Build_system.run_exn (fun () ->
let open Memo.Build.O in
let* request =
match targets with
Expand Down
2 changes: 1 addition & 1 deletion bin/printenv.ml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ let term =
User_error.raise
[ Pp.text "Environment is not defined in install dirs" ])
in
Build_system.run (fun () -> Build_system.build request) >>| function
Build_system.run_exn (fun () -> Build_system.build request) >>| function
| [ (_, env) ] -> Format.printf "%a" (pp ~fields) env
| l ->
List.iter l ~f:(fun (name, env) ->
Expand Down
2 changes: 1 addition & 1 deletion bin/top.ml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ let term =
Scheduler.go ~common ~config (fun () ->
let open Fiber.O in
let* setup = Import.Main.setup () in
Build_system.run (fun () ->
Build_system.run_exn (fun () ->
let open Memo.Build.O in
let sctx =
Dune_engine.Context_name.Map.find setup.scontexts ctx_name
Expand Down
2 changes: 1 addition & 1 deletion bin/utop.ml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ let term =
Scheduler.go ~common ~config (fun () ->
let open Fiber.O in
let* setup = Import.Main.setup () in
Build_system.run (fun () ->
Build_system.run_exn (fun () ->
let open Memo.Build.O in
let context = Import.Main.find_context_exn setup ~name:ctx_name in
let sctx = Import.Main.find_scontext_exn setup ~name:ctx_name in
Expand Down
63 changes: 45 additions & 18 deletions src/dune_engine/build_system.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2272,25 +2272,40 @@ let prefix_rules (prefix : unit Action_builder.t) ~f =
in
res

let report_early_exn ~report_error exn =
let caused_by_cancellation (exn : Exn_with_backtrace.t) =
match exn.exn with
| Scheduler.Run.Build_cancelled -> true
| Memo.Error.E err -> (
match Memo.Error.get err with
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid dealing with the optional wrapping, we could change Memo as follow:

  • make handle_error_no_raise take a Memo.Error.t and a backtrace rather than an Exn_with_backtrace.t
  • make Memo.Build.run* only raise Memo.Error.E

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is already the case that the top-level Memo.Build.run* only ever raise Memo.Error.E. We could probably make them return it instead of raising it, even.

For the first bullet-point, agreed. I was even thinking of including the backtrace into Memo.Error.t, but that's a separate idea.

(I'm assuming you're saying these as ideas for the future PRs, so I'm merging this one)

| Scheduler.Run.Build_cancelled -> true
| _ -> false)
| _ -> false

let report_early_exn exn =
let t = t () in
let error = { Error.exn; id = Error.Id.gen () } in
t.errors <- error :: t.errors;
(match !Clflags.report_errors_config with
| Early
| Twice ->
report_error exn
| Deterministic -> ());
t.handler.error [ Add error ]

let reraise_exn exn =
match caused_by_cancellation exn with
| true -> Fiber.return ()
| false ->
let error = { Error.exn; id = Error.Id.gen () } in
t.errors <- error :: t.errors;
(match !Clflags.report_errors_config with
| Early
| Twice ->
Dune_util.Report_error.report exn
| Deterministic -> ());
t.handler.error [ Add error ]

let handle_final_exns exns =
match !Clflags.report_errors_config with
| Early -> raise Dune_util.Report_error.Already_reported
| Early -> ()
| Twice
| Deterministic ->
Exn_with_backtrace.reraise exn
let report exn =
if not (caused_by_cancellation exn) then Dune_util.Report_error.report exn
in
List.iter exns ~f:report

let run ?(report_error = Dune_util.Report_error.report) f =
let run f =
let open Fiber.O in
Hooks.End_of_build.once Promotion.finalize;
let t = t () in
Expand All @@ -2307,15 +2322,27 @@ let run ?(report_error = Dune_util.Report_error.report) f =
let f () =
let* () = t.handler.build_event Start in
let* res =
Fiber.with_error_handler ~on_error:reraise_exn (fun () ->
Fiber.collect_errors (fun () ->
Memo.Build.run_with_error_handler (f ())
~handle_error_no_raise:(report_early_exn ~report_error))
~handle_error_no_raise:report_early_exn)
in
let+ () = t.handler.build_event Finish in
res
match res with
| Ok res ->
let+ () = t.handler.build_event Finish in
Ok res
| Error exns ->
handle_final_exns exns;
Fiber.return (Error `Already_reported)
in
Fiber.Mutex.with_lock t.build_mutex f

let run_exn f =
let open Fiber.O in
let+ res = run f in
match res with
| Ok res -> res
| Error `Already_reported -> raise Dune_util.Report_error.Already_reported

let build_file = build_file

let build_deps = build_deps
Expand Down
7 changes: 4 additions & 3 deletions src/dune_engine/build_system.mli
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,10 @@ val files_in_source_tree_to_delete : unit -> Path.Set.t
(** {2 Running a build} *)

val run :
?report_error:(Exn_with_backtrace.t -> unit)
-> (unit -> 'a Memo.Build.t)
-> 'a Fiber.t
(unit -> 'a Memo.Build.t) -> ('a, [ `Already_reported ]) Result.t Fiber.t

(** A variant of [run] that raises an [Already_reported] exception on error. *)
val run_exn : (unit -> 'a Memo.Build.t) -> 'a Fiber.t

(** {2 Misc} *)

Expand Down
34 changes: 9 additions & 25 deletions src/dune_engine/scheduler.ml
Original file line number Diff line number Diff line change
Expand Up @@ -611,13 +611,15 @@ let ignore_for_watch p =
let+ t = t () in
Event.Queue.ignore_next_file_change_event t.events p

exception Build_cancelled

let with_job_slot f =
let* t = t () in
let raise_if_cancelled () =
match t.status with
| Restarting_build _
| Shutting_down ->
raise (Memo.Non_reproducible (Failure "Build cancelled"))
raise (Memo.Non_reproducible Build_cancelled)
| Building -> ()
| Waiting_for_file_changes _ ->
(* At this stage, we're not running a build, so we shouldn't be running
Expand Down Expand Up @@ -825,6 +827,8 @@ module Worker = struct
end

module Run = struct
exception Build_cancelled = Build_cancelled

type file_watcher =
| Detect_external
| No_watcher
Expand All @@ -836,26 +840,7 @@ module Run = struct
let* t = t () in
let rec loop () : unit Fiber.t =
t.status <- Building;
let* res =
let report_error exn =
match t.status with
| Building -> Dune_util.Report_error.report exn
| Shutting_down
| Restarting_build _ ->
()
| Waiting_for_file_changes _ ->
(* We are inside a build, so we aren't waiting for a file change
event *)
assert false
in
let on_error exn =
report_error exn;
Fiber.return ()
in
Fiber.map_reduce_errors
(module Monoid.Unit)
~on_error (step ~report_error)
in
let* res = step in
match t.status with
| Waiting_for_file_changes _ ->
(* We just finished a build, so there's no way this was set *)
Expand All @@ -880,10 +865,9 @@ module Run = struct
Memo.reset invalidations;
t.handler t.config Source_files_changed;
match res with
| Error _
| Ok `Continue ->
loop ()
| Ok `Stop -> Fiber.return ()))
| Error `Already_reported
| Ok () ->
loop ()))
in
loop ()

Expand Down
8 changes: 3 additions & 5 deletions src/dune_engine/scheduler.mli
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,16 @@ module Run : sig
cases *)
exception Shutdown_requested

exception Build_cancelled

(** Runs [once] in a loop, executing [finally] after every iteration, even if
Fiber.Never was encountered.

If any source files change in the middle of iteration, it gets canceled.

If [shutdown] is called, the current build will be canceled and new builds
will not start. *)
val poll :
( report_error:(Exn_with_backtrace.t -> unit)
-> unit
-> [ `Continue | `Stop ] Fiber.t)
-> unit Fiber.t
val poll : (unit, [ `Already_reported ]) Result.t Fiber.t -> unit Fiber.t

val go :
Config.t
Expand Down