Skip to content

Commit

Permalink
don't execute start functions when receiver is already started
Browse files Browse the repository at this point in the history
  • Loading branch information
msimonborg committed Mar 13, 2019
1 parent 26ccd79 commit dd821be
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 8 deletions.
22 changes: 14 additions & 8 deletions lib/receiver.ex
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ defmodule Receiver do
module: module,
receiver: atom,
name: atom | registered_name,
initial_state: term
args: args
}

@doc """
Expand Down Expand Up @@ -510,7 +510,7 @@ defmodule Receiver do
end

Agent
|> apply(start_function, [initialization_func(attrs), [name: attrs.name]])
|> apply(start_function, [initialization_func(self(), attrs), [name: attrs.name]])
|> invoke_handle_start_callback(module, attrs)
end

Expand Down Expand Up @@ -541,7 +541,12 @@ defmodule Receiver do
on_start | on_start_supervised
defp invoke_handle_start_callback(on_start_result, module, attrs) do
with {:ok, pid} <- on_start_result do
apply(module, :handle_start, [attrs.receiver, pid, attrs.initial_state])
initial_state =
receive do
{:initial_state, result} -> result
end

apply(module, :handle_start, [attrs.receiver, pid, initial_state])
{:ok, pid}
end
rescue
Expand All @@ -557,28 +562,29 @@ defmodule Receiver do

@spec get_start_attrs(module, args, options) :: start_attrs
defp get_start_attrs(module, args, opts) do
task = apply(Task.Supervisor, :async, [Receiver.TaskSup | args])
receiver = Keyword.get(opts, :as, :receiver)

%{
module: module,
receiver: receiver,
name: Keyword.get(opts, :name, registered_name(module, receiver)),
initial_state: Task.await(task)
args: args
}
end

@spec initialization_func(start_attrs) :: (() -> state)
defp initialization_func(attrs) do
@spec initialization_func(pid, start_attrs) :: (() -> state)
defp initialization_func(caller, attrs) do
# If an atom is provided as the `:name` option at `start*` it overrides the `:via` naming pattern,
# skipping registration with the `Registry`. In this case the process needs to be manually registered
# on initialization so the PID is associated with the receiver name and registered process name.
# If the process has already been registered with the `:via` pattern then `Registry.register/3` returns
# `{:error, {:already_registered, pid}}` and is effectively a noop. We do this from within the
# initialization function because the calling process will be the one registered. See `Registry.register/3`.
fn ->
task = apply(Task.Supervisor, :async, [Receiver.TaskSup | attrs.args])
Registry.register(Receiver.Registry, {attrs.module, attrs.receiver}, attrs.name)
attrs.initial_state
send(caller, {:initial_state, result = Task.await(task)})
result
end
end

Expand Down
31 changes: 31 additions & 0 deletions test/receiver_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,36 @@ defmodule ReceiverTest do
use ExUnitReceiver, as: :tester, name: Tester
doctest Receiver

describe "start functions" do
setup do
msg = "haven't sent any messages"
start_supervised({Receiver, [One, fn -> msg end]})
%{msg: msg}
end

property "aren't executed when receiver is already started", %{msg: msg} do
check all val <- term() do
me = self()
{:error, {:already_started, _}} =
Receiver.start(One, fn -> send(me, val) end)

refute_receive(^val, 5)

{:error, {:already_started, _}} =
Receiver.start_link(One, fn -> send(me, val) end)

refute_receive(^val, 5)

{:error, {:already_started, _}} =
Receiver.start_supervised(One, fn -> send(me, val) end)

refute_receive(^val, 5)

assert Receiver.get({One, :receiver}, &(&1)) == msg
end
end
end

describe "start/3" do
test "accepts a callback module and funtion" do
{:ok, pid} = Receiver.start(One, fn -> %{} end)
Expand All @@ -30,6 +60,7 @@ defmodule ReceiverTest do
)

assert Receiver.whereis({One, :wrong}) == nil
assert Receiver.whereis({One, :receiver}) == nil
end

property "returns an error tuple when a bad callback module is given" do
Expand Down

0 comments on commit dd821be

Please sign in to comment.