mszmurlo
mszmurlo

Reputation: 1339

Handling :DOWN and #Reference<...> messages after Task timeout

I'm spawning a function with Task.async/3 and get the result with Task.yield/2 with a timeout. When the function completes, the calling process receives two messages:

{#Reference<0.2781211517.3250323457.144521>, the_result_of_the_function}

where the#Reference is the reference of the monitor returned by Task.async/3, and

{:DOWN, #Reference<0.2781211517.3250323457.144521>, :process, #PID<0.24500.0>, :normal}

which is sent by the monitor to inform that the monitored process (the task) has shutdown.

If the task finishes before the timeout, Task.yield/2 takes these messages out of the mailbox and returns the proper result. However, if the task finishes after the timeout, Task.yield/2 returns nil and the program flow is already somewhere else when these two messages arrive.

I implemented two handle_info handlers to get these messages out of the mailbox. Yet, it's probably a bad idea because as I have no way to get only messages from expired tasks, (I don't see any way to "register" the #Reference of the task that has timeout-ed), I will probably remove valid massages before they are read by Task.yield/2.

What am I missing and how to get around this problem ?

Upvotes: 0

Views: 321

Answers (1)

Patrick Oscity
Patrick Oscity

Reputation: 54684

The documentation for Task.yield/2 suggests the following approach:

If the time runs out before a message from the task is received, this function will return nil and the monitor will remain active. […] If you intend to shut the task down if it has not responded within timeout milliseconds, you should chain this together with shutdown/1, like so:

case Task.yield(task, timeout) || Task.shutdown(task) do
  {:ok, result} ->
    result
  nil ->
    Logger.warn "Failed to get a result in #{timeout}ms"
    nil
end

Upvotes: 1

Related Questions