Skip to content

Commit 778d596

Browse files
committed
Remove deferred loading of NIF
It was possible to crash the BEAM by loading a completely trimmed down shared library with multiple processes. This converts the NIF loader back to the traditional `@on_load` way that's serialized by the code loader. The benefits of delaying the load time are less now that the shared library has been tested quite a bit.
1 parent 4412868 commit 778d596

File tree

2 files changed

+6
-32
lines changed

2 files changed

+6
-32
lines changed

lib/i2c/i2c_nif.ex

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,20 @@
11
defmodule Circuits.I2C.Nif do
22
@moduledoc false
33

4-
defp load_nif_and_apply(fun, args) do
5-
nif_binary = Application.app_dir(:circuits_i2c, "priv/i2c_nif")
4+
@on_load {:load_nif, 0}
5+
@compile {:autoload, false}
66

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
13-
end
14-
15-
def open(device) do
16-
load_nif_and_apply(:open, [device])
7+
def load_nif() do
8+
:erlang.load_nif(:code.priv_dir(:circuits_i2c) ++ ~c"/i2c_nif", 0)
179
end
1810

11+
def open(_device), do: :erlang.nif_error(:nif_not_loaded)
1912
def read(_ref, _address, _count, _retries), do: :erlang.nif_error(:nif_not_loaded)
2013
def write(_ref, _address, _data, _retries), do: :erlang.nif_error(:nif_not_loaded)
2114

2215
def write_read(_ref, _address, _write_data, _read_count, _retries),
2316
do: :erlang.nif_error(:nif_not_loaded)
2417

2518
def close(_ref), do: :erlang.nif_error(:nif_not_loaded)
26-
27-
def info() do
28-
load_nif_and_apply(:info, [])
29-
end
19+
def info(), do: :erlang.nif_error(:nif_not_loaded)
3020
end

test/circuits_i2c_test.exs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -84,20 +84,4 @@ 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
10387
end

0 commit comments

Comments
 (0)