alexandrubagu

alexandrubagu

Handle_info when using Task.Supervisor.async_nolink

Hi,

I want to handle the following exit example ( String.to_integer(input) is jus an example and can be replaced with raise). This happen when I call My.GenServer.some_method(My.GenServer, "test"). I will be grateful if someone has a suggestion for this.

10:26:42.321 [error] GenServer My.GenServer terminating
** (stop) exited in: Task.await(%Task{owner: #PID<0.124.0>, pid: #PID<0.128.0>, ref: #Reference<0.4181578440.4035444737.94083>}, 5000)
    ** (EXIT) an exception was raised:
        ** (ArgumentError) argument error
            :erlang.binary_to_integer("test")

Here’s the code:

defmodule My.Application do
  use Application

  def start(_type, _args) do
    import Supervisor.Spec, warn: false

    children = [
      worker(My.GenServer, []),
      supervisor(Task.Supervisor, [[name: My.Task.Supervisor, restart: :transient]]),
    ]
    
    opts = [strategy: :one_for_one, name: Test.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

defmodule My.GenServer do
    def start_link do
      GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
    end

    def init(:ok) do
      # Process.flag(:trap_exit, true)
      {:ok, []}
    end

    def some_method(pid, input) do
        GenServer.call(pid, {:some_method, input})
    end

    def handle_call({:some_method, input}, _from, state) do
      # because we don't want to terminate GenServer use Task.async_nolink
      task = Task.Supervisor.async_nolink(My.Task.Supervisor, fn ->
          # raise argument error when passing binary
          String.to_integer(input)
      end)
      {:reply, Task.await(task), state}
    end

    def handle_info({:EXIT, from, reason}, state) do
      IO.inspect "handle_info::exit"
      IO.inspect reason
      {:noreply, state}
    end

    def handle_info({_ref, result}, state) do
      IO.inspect "handle_info::result"
      IO.inspect result
      {:noreply, state}
    end

    def handle_info({:DOWN, _ref, :process, _pid, reason}, state) do
      IO.inspect "handle_info::down"
      IO.inspect reason
      {:noreply, state}
    end

    def handle_info(msg, state) do
      IO.inspect "handle_info"
      IO.inspect msg
      {:noreply, state}
    end
end

I’ve read the docs from here regarding Task.Supervisor.async_link: if you create a task using async_nolink inside an OTP behaviour like GenServer, you should match on the message coming from the task inside your GenServer.handle_info/2 callback.

My problem is that handle_info is not called, I try to debug using observer attaching a trace to My.GenServer and I see that My.GenServer is receiving a :DOWN message:

Any solution to this problem ?
Thanks

Most Liked

NobbZ

NobbZ

When using Task.await/1 it will handle the :DOWN-message from the Task. It will handle it by calling exit/1. So the GenServer will be exited because of that.

If you want to be more failsafe, you have to do the waiting for the result and the :DOWN-message completely on your own.

dom

dom

Oh, I see what you mean. Instead of using await you can store the task’s ref and the caller in your GenServer state, then use GenServer.reply in your handle_info to send back the response to the caller.

NobbZ

NobbZ

As it is now you gain nothing from the Task except for the timeout, you could do the work in the GenServer directly and it wouldn’t matter (as long as no timout would happen in the Task)

The more correct way were to put the returned task into your state, alongside the from and return a :noreturn tuple in handle_call/3.

Some time later you will receive a message with either the result of the computation done in the Task, which you then can GenServer(from, answer) to your caller.

Or you might receive a :DOWN, which you will need to find ways to tell your caller about. Most idiomatic way were to use an :error-tuple I think.

NobbZ

NobbZ

This will break when your GenServer will be hit by multiple calls before the result of the spawned task is sent back!

Each time you end up in your handle_cast you are overwriting the receiver of the answer.

Once you get an answer back, you will send it to the last one who asked for the info, letting all other processes starve. Also when subsequent answers from all the other tasks come in, you will send them into the mailbox of the same process (unless another one has asked in the meantime), spamming that processes mailbox with stuff that will probably never be read again.

Please make sure, that you map a task to a caller, and only send the answers/results back correspondingly.

Where Next?

Popular in Questions Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
tduccuong
Hi, is there any work on GUI with Elixir, that is similar to Electron/Javascript? My idea is to bundle Phoenix and BEAM into a single se...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
dokuzbir
Hello, I am trying to convert my lists to string without losing brackets.For start i have 3 map. They look like these buyer = %{ id: ...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

Other popular topics Top

Qqwy
Update: How to use the Blogs &amp; Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3268 119930 1237
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30048 115
New
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New

We're in Beta

About us Mission Statement