3
votes

Writing a few small experiments to familiarise myself with the language, but have run into an issue which I'm guessing is elementary.

I have a simple supervisor, with 3 simple workers:

def init do 
    Supervisor.start_link(
       [
          worker(__MODULE__, [:"process-1"], [function: :test, id: :"p-1"]),
          worker(__MODULE__, [:"process-2"], [function: :test, id: :"p-2"]),
          worker(__MODULE__, [:"process-3"], [function: :test, id: :"p-3"])
       ],
       strategy: :one_for_one   
    )
end

":test" looks like this:

def test(name) do
    flag(:trap_exit, true)

    IO.puts "Testing: #{name} == #{inspect self}"

    register(self, name)

    receive do
        { :death } -> 
            IO.puts("I WOZ MURDERED!")
            exit(self, "Ex process...")
        { :life } -> 
            IO.puts("#{inspect self} is listening...") 
            __MODULE__.test(name)
        { :EXIT, pid, reason } ->
            IO.puts "REASON: #{inspect reason} - PROCESS: #{inspect pid}"
    end
end

This compiles, but it only ever spawns one process, and hangs/blocks iex.

In contrast, when I use a simple chain of 'spawn_link'ed' processes, all three (or however many) processes start concurrently and return control to the iex shell so I can send the registered processes messages from the command line.

My intention, for now, is to create an OTP supervisor, run and register three (or however many) workers processes and attach them to the supervisor, send a simple message to kill a given worker, and then have the supervisor restart it.

What am I doing wrong?

1

1 Answers

6
votes

The problem is the function: you're giving as part of the worker specification doesn't do what OTP expects.

From http://www.erlang.org/doc/man/supervisor.html

The start function must create and link to the child process, and should return {ok,Child} or {ok,Child,Info} where Child is the pid of the child process and Info an arbitrary term which is ignored by the supervisor.

Your code doesn't spawn a child but goes into a receive loop. You could perhaps use Elixir's Task module to do something similar to what it looks like you want:

worker(Task, [__MODULE__, :test, [:"process-1"]], id: :"p-1"),
worker(Task, [__MODULE__, :test, [:"process-2"]], id: :"p-2"),
worker(Task, [__MODULE__, :test, [:"process-3"]], id: :"p-3")

However if you're looking to learn more about what OTP does then having a go at implementing your own GenServer might be a better option.