Skip to content

Commit 8007ece

Browse files
committed
Fix race when two process load the NIF at the same time
Interestingly, the unit test was able to segfault before this fix.
1 parent 9271dc4 commit 8007ece

File tree

2 files changed

+26
-7
lines changed

2 files changed

+26
-7
lines changed

lib/i2c/i2c_nif.ex

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
defmodule Circuits.I2C.Nif do
22
@moduledoc false
33

4-
defp load_nif() do
4+
defp load_nif_and_apply(fun, args) do
55
nif_binary = Application.app_dir(:circuits_i2c, "priv/i2c_nif")
6-
:erlang.load_nif(to_charlist(nif_binary), 0)
6+
7+
# Optimistically load the NIF. Handle the possible race.
8+
case :erlang.load_nif(to_charlist(nif_binary), 0) do
9+
:ok -> apply(__MODULE__, fun, args)
10+
{:error, {:reload, _}} -> apply(__MODULE__, fun, args)
11+
error -> error
12+
end
713
end
814

915
def open(device) do
10-
with :ok <- load_nif() do
11-
apply(__MODULE__, :open, [device])
12-
end
16+
load_nif_and_apply(:open, [device])
1317
end
1418

1519
def read(_ref, _address, _count, _retries), do: :erlang.nif_error(:nif_not_loaded)
@@ -21,7 +25,6 @@ defmodule Circuits.I2C.Nif do
2125
def close(_ref), do: :erlang.nif_error(:nif_not_loaded)
2226

2327
def info() do
24-
:ok = load_nif()
25-
apply(__MODULE__, :info, [])
28+
load_nif_and_apply(:info, [])
2629
end
2730
end

test/circuits_i2c_test.exs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,20 @@ defmodule Circuits.I2CTest do
8484
assert info.backend == Circuits.I2C.I2CDev
8585
assert info.test?
8686
end
87+
88+
test "racing to load the NIF" do
89+
# Make sure the NIF isn't loaded
90+
_ = :code.delete(Circuits.I2C.Nif)
91+
_ = :code.purge(Circuits.I2C.Nif)
92+
93+
# Try to hit the race by having 32 processes race to load the NIF
94+
tasks =
95+
for _ <- 0..31 do
96+
Task.async(fn ->
97+
_ = Circuits.I2C.info()
98+
end)
99+
end
100+
101+
Enum.each(tasks, &Task.await/1)
102+
end
87103
end

0 commit comments

Comments
 (0)