Skip to content

Commit c740f6a

Browse files
committed
Protect toolchain installation with flock
This prevents multiple concurrent instances of dune interfering with one another while installing toolchains. Signed-off-by: Stephen Sherratt <[email protected]>
1 parent 71bd436 commit c740f6a

File tree

1 file changed

+51
-13
lines changed

1 file changed

+51
-13
lines changed

src/dune_pkg/toolchain.ml

+51-13
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,10 @@ module Version = struct
185185

186186
let bin_dir t = Path.Outside_build_dir.relative (target_dir t) "bin"
187187
let is_installed t = Path.exists (Path.outside_build_dir (target_dir t))
188+
189+
let flock_path t =
190+
Path.Outside_build_dir.relative (toolchain_dir t) "lock" |> Path.outside_build_dir
191+
;;
188192
end
189193

190194
let handle_checksum_mismatch { Compiler_package.version; url; checksum } ~got_checksum =
@@ -289,18 +293,17 @@ let get ~log version =
289293
| `Never -> ()
290294
| _ -> User_message.print (User_message.make [ Pp.tag style pp ])
291295
in
292-
if Version.is_installed version
293-
then (
294-
(match log with
295-
| `Always ->
296-
log_print Success
297-
@@ Pp.textf
298-
"Version %s of the compiler toolchain is already installed in %s"
299-
(Version.to_string version)
300-
(Version.target_dir version |> Path.Outside_build_dir.to_string)
301-
| _ -> ());
302-
Fiber.return ())
303-
else (
296+
let print_already_installed_message () =
297+
match log with
298+
| `Always ->
299+
log_print Success
300+
@@ Pp.textf
301+
"Version %s of the compiler toolchain is already installed in %s"
302+
(Version.to_string version)
303+
(Version.target_dir version |> Path.Outside_build_dir.to_string)
304+
| _ -> ()
305+
in
306+
let download_build_install () =
304307
let compiler_package = Compiler_package.of_version version in
305308
log_print Details
306309
@@ Pp.textf
@@ -319,5 +322,40 @@ let get ~log version =
319322
@@ Pp.textf
320323
"Success! Compiler toolchain version %s installed to %s."
321324
(Version.to_string version)
322-
(Version.target_dir version |> Path.Outside_build_dir.to_string))
325+
(Version.target_dir version |> Path.Outside_build_dir.to_string)
326+
in
327+
if Version.is_installed version
328+
then (
329+
print_already_installed_message ();
330+
Fiber.return ())
331+
else
332+
Flock.with_flock
333+
(Version.flock_path version)
334+
~name_for_messages:(sprintf "toolchain version %s" (Version.to_string version))
335+
~timeout_s:infinity
336+
~f:(fun () ->
337+
(* Note that we deliberately check if the toolchain is
338+
installed before and after taking the flock.
339+
340+
The first check prevents us from trying to take the lock if
341+
the toolchain is installed. To build any package dune first
342+
checks if the necessary toolchain is installed, so to build
343+
a project with many dependencies, dune will check if the
344+
toolchain is installed many times. If this check required
345+
first taking a lock, multiple concurrent dune instances
346+
would sometimes contest the lock. This isn't too bad for
347+
performance as the lock is only held briefly, but when dune
348+
waits on a flock it prints a message, so freqeunt, brief
349+
lock acquisitions can lead to a lot of noise in the
350+
output.
351+
352+
The second check is to handle the case where the toolchain
353+
was installed in between the first check, and the flock
354+
being acquired.
355+
*)
356+
if Version.is_installed version
357+
then (
358+
print_already_installed_message ();
359+
Fiber.return ())
360+
else download_build_install ())
323361
;;

0 commit comments

Comments
 (0)