summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichal Muskala <michal@muskala.eu>2016-05-13 20:51:08 +0200
committerMichal Muskala <michal@muskala.eu>2016-05-13 21:25:14 +0200
commit01c4c42e117636cdba2d5278c851fbe7a3b39bc0 (patch)
treea8a6bbf1f15715e982c7d7c5aade7ac5593dc6f2
parentMerge pull request #43 from Annwenn/master (diff)
Monitor owner of the connection
The connection process should monitor the process that started it and die with the same reason that the owner process did. This should solve the issue of zombie connections laying around after the processes that started them die.
-rw-r--r--lib/exirc/client.ex13
-rw-r--r--lib/exirc/exirc.ex2
-rw-r--r--test/client_test.exs25
3 files changed, 34 insertions, 6 deletions
diff --git a/lib/exirc/client.ex b/lib/exirc/client.ex
index cde4f1a..81a2f0a 100644
--- a/lib/exirc/client.ex
+++ b/lib/exirc/client.ex
@@ -32,7 +32,8 @@ defmodule ExIrc.Client do
channels: [],
debug?: false,
retries: 0,
- inet: :inet
+ inet: :inet,
+ owner: nil
end
#################
@@ -55,6 +56,7 @@ defmodule ExIrc.Client do
"""
@spec start_link(options :: list() | nil, process_opts :: list() | nil) :: {:ok, pid} | {:error, term}
def start_link(options \\ [], process_opts \\ []) do
+ options = Keyword.put_new(options, :owner, self())
GenServer.start_link(__MODULE__, options, process_opts)
end
@doc """
@@ -285,17 +287,20 @@ defmodule ExIrc.Client do
def init(options \\ []) do
autoping = Keyword.get(options, :autoping, true)
debug = Keyword.get(options, :debug, false)
+ owner = Keyword.fetch!(options, :owner)
# Add event handlers
handlers =
Keyword.get(options, :event_handlers, [])
|> List.foldl([], &do_add_handler/2)
+ ref = Process.monitor(owner)
# Return initial state
{:ok, %ClientState{
event_handlers: handlers,
autoping: autoping,
logged_on?: false,
debug?: debug,
- channels: ExIrc.Channels.init()}}
+ channels: ExIrc.Channels.init(),
+ owner: {owner, ref}}}
end
@doc """
Handle calls from the external API. It is not recommended to call these directly.
@@ -497,6 +502,10 @@ defmodule ExIrc.Client do
def handle_info({:ssl, socket, data}, state) do
handle_info({:tcp, socket, data}, state)
end
+ # If the owner process dies, we should die as well
+ def handle_info({:DOWN, ref, _, pid, reason}, %{owner: {pid, ref}} = state) do
+ {:stop, reason, state}
+ end
# If an event handler process dies, remove it from the list of event handlers
def handle_info({:DOWN, _, _, pid, _}, state) do
handlers = do_remove_handler(pid, state.event_handlers)
diff --git a/lib/exirc/exirc.ex b/lib/exirc/exirc.ex
index cfa2654..72be33c 100644
--- a/lib/exirc/exirc.ex
+++ b/lib/exirc/exirc.ex
@@ -50,7 +50,7 @@ defmodule ExIrc do
@spec start_client! :: {:ok, pid} | {:error, term}
def start_client! do
# Start the client worker
- Supervisor.start_child(:exirc, [])
+ Supervisor.start_child(:exirc, [[owner: self()]])
end
##############
diff --git a/test/client_test.exs b/test/client_test.exs
index 6cff0b7..b7277b6 100644
--- a/test/client_test.exs
+++ b/test/client_test.exs
@@ -1,11 +1,30 @@
defmodule ExIrc.ClientTest do
use ExUnit.Case
-
test "start multiple clients" do
- {:ok, pid} = ExIrc.start_client!
- {:ok, pid2} = ExIrc.start_client!
+ assert {:ok, pid} = ExIrc.start_client!
+ assert {:ok, pid2} = ExIrc.start_client!
assert pid != pid2
end
+ test "client dies if owner process dies" do
+ test_pid = self()
+
+ pid = spawn_link(fn ->
+ assert {:ok, pid} = ExIrc.start_client!
+ send(test_pid, {:client, pid})
+ receive do
+ :stop -> :ok
+ end
+ end)
+
+ client_pid = receive do
+ {:client, pid} -> pid
+ end
+
+ assert Process.alive?(client_pid)
+ send(pid, :stop)
+ :timer.sleep(1)
+ refute Process.alive?(client_pid)
+ end
end