How to capture error for a monitored function that accepts arguments

I’ve seen this Approach 2, but I cannot work through adapting it for a function that accepts arguments.

I think it needs to pipe into spawn_monitor/3 rather than spawn_monitor/1.

Here’s the code for completeness. My only attempt doesn’t use the capture function or the wrapper. Just calls spawn_monitor( Module, :function, [args]) but then I cannot find a way of getting at the return value because, I guess, there’s no wrapper.


def capture(callback, timeout_ms) do
  {pid, monitor} = callback |> propagate_return_value_wrapper |> spawn_monitor
  receive do
    {:DOWN, ^monitor, :process, ^pid, :normal} ->
      receive do
        {__MODULE__, :response, response} -> {:ok, response}
    {:DOWN, ^monitor, :process, ^pid, reason}  ->
      Logger.error "#{__MODULE__}: Error in handled function: #{inspect reason}";
      {:error, reason}
  after timeout_ms ->
    pid |> Process.exit(:kill)
    Logger.error "#{__MODULE__}: Timeout..."
    {:error, {:timeout, timeout_ms}}

defp propagate_return_value_wrapper(callback) do
  caller_pid = self
  fn-> caller_pid |> send( {__MODULE__, :response, callback.()}) end

Have you considered using Task.async for this effect? You could execute your lambda on that task, and since Tasks are monitored by the parent processes that spawn them you would have access to all its errors and messages.

This feels rather clunky but it worked just to pass the arguments into the wrapper. I think I’m perhaps missing something with assigning the callback with args before calling capture.

  defp propagate_return_value_wrapper(callback, arg1, arg2) do
    caller_pid = self()
    fn -> caller_pid |> send( {__MODULE__, :response, callback.(arg1, arg2)}) end

Agreed, This seems like a complex re-implementation of Task.async. @shotleybuilder can you elaborate on what you’re trying to do here that can’t be achieved by Task.async?

1 Like

I will have a look at Task.async. I’ve not worked directly with processes before. Happy to update if it works.
I’m writing a test that I’d like to catch the errors from rather than have it just fall over.