Skip to content

Commit

Permalink
Fix multiple concurrent calls to install_and_run/2 (#58) (#59)
Browse files Browse the repository at this point in the history
  • Loading branch information
azizk authored May 3, 2023
1 parent ab489be commit e2a24b4
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 4 deletions.
23 changes: 19 additions & 4 deletions lib/esbuild.ex
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ defmodule Esbuild do
:ok
end

Supervisor.start_link([], strategy: :one_for_one)
Supervisor.start_link([], strategy: :one_for_one, name: __MODULE__.Supervisor)
end

@doc false
Expand Down Expand Up @@ -182,15 +182,30 @@ defmodule Esbuild do
|> elem(1)
end

defp start_unique_install_worker() do
ref =
__MODULE__.Supervisor
|> Supervisor.start_child(
Supervisor.child_spec({Task, &install/0}, restart: :transient, id: __MODULE__.Installer)
)
|> case do
{:ok, pid} -> pid
{:error, {:already_started, pid}} -> pid
end
|> Process.monitor()

receive do
{:DOWN, ^ref, _, _, _} -> :ok
end
end

@doc """
Installs, if not available, and then runs `esbuild`.
Returns the same as `run/2`.
"""
def install_and_run(profile, args) do
unless File.exists?(bin_path()) do
install()
end
File.exists?(bin_path()) || start_unique_install_worker()

run(profile, args)
end
Expand Down
25 changes: 25 additions & 0 deletions test/esbuild_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,29 @@ defmodule EsbuildTest do
assert Esbuild.run(:default, ["--version"]) == 0
end) =~ @version
end

test "install and run multiple concurrently" do
bin_path = Esbuild.bin_path()

assert :ok = File.exists?(bin_path) && File.rm!(bin_path)

results =
[:extra1, :extra2, :extra3]
|> Enum.map(fn profile ->
Application.put_env(:esbuild, profile, args: ["--version"])

Task.async(fn ->
ret_code = Esbuild.install_and_run(profile, [])
# Let the first finished task set the binary file to read and execute only,
# so that the others will fail if they try to overwrite it.
File.chmod!(bin_path, 0o500)
ret_code == 0
end)
end)
|> Task.await_many(:infinity)

File.chmod!(bin_path, 0o700)

assert results |> Enum.all?()
end
end

0 comments on commit e2a24b4

Please sign in to comment.