Skip to content

Commit 63c0c7b

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 4cc60bb commit 63c0c7b

File tree

2 files changed

+7
-28
lines changed

2 files changed

+7
-28
lines changed

lib/gpio/gpio_nif.ex

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,15 @@
55
defmodule Circuits.GPIO.Nif do
66
@moduledoc false
77

8-
defp load_nif_and_apply(fun, args) do
9-
nif_binary = Application.app_dir(:circuits_gpio, "priv/gpio_nif")
8+
@on_load {:load_nif, 0}
9+
@compile {:autoload, false}
1010

11-
# Optimistically load the NIF. Handle the possible race.
12-
case :erlang.load_nif(to_charlist(nif_binary), 0) do
13-
:ok -> apply(__MODULE__, fun, args)
14-
{:error, {:reload, _}} -> apply(__MODULE__, fun, args)
15-
error -> error
16-
end
11+
def load_nif() do
12+
:erlang.load_nif(:code.priv_dir(:circuits_gpio) ++ ~c"/gpio_nif", 0)
1713
end
1814

19-
def open(pin_number, pin_direction, initial_value, pull_mode) do
20-
load_nif_and_apply(:open, [pin_number, pin_direction, initial_value, pull_mode])
15+
def open(_pin_number, _pin_direction, _initial_value, _pull_mode) do
16+
:erlang.nif_error(:nif_not_loaded)
2117
end
2218

2319
def close(_gpio) do
@@ -49,6 +45,6 @@ defmodule Circuits.GPIO.Nif do
4945
end
5046

5147
def info() do
52-
load_nif_and_apply(:info, [])
48+
:erlang.nif_error(:nif_not_loaded)
5349
end
5450
end

test/circuits_gpio_test.exs

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -272,21 +272,4 @@ defmodule Circuits.GPIOTest do
272272
assert false == :code.purge(Circuits.GPIO)
273273
end
274274
end
275-
276-
test "racing to load the NIF" do
277-
# Make sure the NIF isn't loaded
278-
assert true == :code.delete(Circuits.GPIO.Nif)
279-
assert false == :code.purge(Circuits.GPIO.Nif)
280-
281-
# Try to hit the race by having 32 processes race to load the NIF
282-
tasks =
283-
for index <- 0..31 do
284-
Task.async(fn ->
285-
{:ok, gpio} = GPIO.open(index, :input)
286-
GPIO.close(gpio)
287-
end)
288-
end
289-
290-
Enum.each(tasks, &Task.await/1)
291-
end
292275
end

0 commit comments

Comments
 (0)