3
votes

Ignoring the absence of the Mix config file, I write the following:

defmodule Test.Supervisor do
    use Supervisor

    def start_link do
      #"name:" will show up in :observer...
        Supervisor.start_link(__MODULE__, [], [name: :"root_supervisor"])
    end

    def init(args) do
        children = [
            worker(Test.Method, [], [function: :start, id: "my_root_process"]),
        ]

        supervise(children, [strategy: :one_for_one, name: :root])
    end
end

defmodule Test do
    def start(_type, _args) do
        Test.Supervisor.start_link()
    end
end

defmodule Test.Method do
    def start do
        IO.puts("Expect to see me often... #{self}")
    end
end

Which crashes after the first run (iex -S mix) without restarting the application. The error message is:

=INFO REPORT==== 14-Jan-2016::22:34:04 ===
    application: logger
    exited: stopped
    type: temporary
** (Mix) Could not start application mememe: Test.start(:normal, {}) returned
an error: shutdown: failed to start child: "my_root_process"
    ** (EXIT) :ok

If however, I change Test.start() to call Test.Method.start() directly, like so:

defmodule Test do
    def start(_type, _args) do
        Test.Method.start()
    end
end

Then it runs fine, but then the code won't be supervised. I'm quite sure I'm making an elementary mistake either in implementation, or comprehension here, but what is that mistake exactly?

2

2 Answers

2
votes

There are couple of problems with your code. Firstly, you need a long running function to supervise. Something like:

def loop do
  receive do
    _anything -> IO.puts "Expect to see me often"
  end
  loop
end

Then in Test.Method module, you have to spawn it.

def start do
  IO.puts("Starting...")
  pid = spawn_link(&loop/0)
  {:ok, pid}
end

It is important, that start function returns tuple {:ok, pid_to_supervise}. It was crashing your app, because supervisor expected a process to monitor, but got only atom :ok returned by IO.puts. Worker specification does not spawn new process. It requires a function, that will return pid of spawned process.

You should also link supervisor to the supervised process, so in the end it might be good idea to rename the function to start_link, instead of start as @Jason Harrelson suggested.

This should be enough to properly start your project. Note, that you will not see your processes in observers Applications section. You are not using Application behaviour, so your root_supervisor will be floating somewhere. You can find it in Processes tab. my_root_process is an id to use withing supervisor, so it won't be visible even in Processes tab.

Spawning process this way is easy for educational purposes, but in real world system, you would like your processes to follow OTP design principles. It means reacting to system messages, better logging, tracing and debugging. Making process that meets all requirements is quite hard, but you don't have to do it manually. All behaviours implement those principles for you.

So instead of spawning a process with loop, try using GenServer.

2
votes

I would try changing the Test.Method.start function to a Test.Method.start_link function and stop using the function: :start in your opts to the worker function. The supervisor calls start_link by default and there is no reason to break these semantics as the supervisor will always link to the supervised process. If this does not work then at least we have ruled an issue in this area out.