Skip to content

Commit

Permalink
First cut at error handling.
Browse files Browse the repository at this point in the history
Attempt to catch errors in `run_ping` and send them back to the parent
so we can produce error messages in a controlled way.

There is still a problem where this occasionally fails and hangs, so I
still have some work to do to handle some of the problems.  In that case
there are errors like this happen:

    19:54:07.013 [error] Task #PID<0.135.0> started from #PID<0.48.0> terminating
    Function: &Ping.ping_async/2
        Args: ["192.168.1.79", #PID<0.48.0>]
    ** (exit) an exception was raised:
        ** (UndefinedFunctionError) undefined function:
    ErlangError.normalize/2
            (elixir) ErlangError.normalize(:emfile, nil)
            09-ping.exs:46: Ping.run_ping/1
            09-ping.exs:31: Ping.ping_async/2
            (elixir) lib/task/supervised.ex:74: Task.Supervised.do_apply/2
            (stdlib) proc_lib.erl:237: :proc_lib.init_p_do_apply/3

    19:54:07.016 [error] 'File operation error: emfile. Target: /usr/local/Cellar/elixir/1.0.5/bin/../lib/elixir/ebin/Elixir.ErlangError.beam.  Function: get_file. Process: code_server.'
  • Loading branch information
mikestok committed Jul 26, 2015
1 parent af2be5c commit f38224a
Showing 1 changed file with 26 additions and 10 deletions.
36 changes: 26 additions & 10 deletions 09-ping.exs
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,33 @@ defmodule Ping do
"""

@doc """
Ping an IP asynchronously and send the {ip, exists} tuple to the parent
Ping an IP asynchronously and send a tuple back to the parent saying what
has happened:
`{:ok, ip, pingable?}` where `pingable?` tells us if `ip` is pingable.
`{:error, ip, error}` when some error caused us to not be able to run the
ping command.
"""
def ping_async(ip, parent) do
send parent, {ip, ping(ip)}
send parent, run_ping(ip)
end

@doc """
Ping a single IP address and return true if there is a response."
Ping a single IP address returning a tuple which `ping_async` can return.
"""
def ping(ip) do
# return code should be handled somehow with pattern matching
{cmd_output, _} = System.cmd("ping", ping_args(ip))
not Regex.match?(~r/100(\.0)?% packet loss/, cmd_output)
def run_ping(ip) do
# This is a Ruby-ish way of dealing with failure...
# TODO: Discover the "Elixir way"
try do
# return code should be handled somehow with pattern matching
{cmd_output, _} = System.cmd("ping", ping_args(ip))
alive? = not Regex.match?(~r/100(\.0)?% packet loss/, cmd_output)
{:ok, ip, alive?}
rescue
e -> {:error, ip, e}
end
end

def ping_args(ip) do
Expand Down Expand Up @@ -69,10 +83,12 @@ defmodule Subnet do
defp wait(dict, 0), do: dict
defp wait(dict, remaining) do
receive do
{ip, exists} ->
dict = Dict.put(dict, ip, exists)
wait dict, remaining-1
{:ok, ip, pingable?} ->
dict = Dict.put(dict, ip, pingable?)
{:error, ip, error} ->
IO.puts "#{inspect error} for #{ip}"
end
wait dict, remaining - 1
end
end

Expand Down

0 comments on commit f38224a

Please sign in to comment.