From 1e1a6976723b37c44b1eff5cc3f4df1a52b95e0b Mon Sep 17 00:00:00 2001 From: David Reiss Date: Thu, 4 Jun 2009 02:01:32 +0000 Subject: [PATCH] THRIFT-211. erlang: Support "tethered" clients Add a client option that causes clients to monitor their creators and terminate when the creator dies. This makes it possible to prevent client leaks without linking, because the latter causes application code to be killed when a transport error occurs and exits are not trapped. git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@781636 13f79535-47bb-0310-9956-ffa450edef68 --- lib/erl/src/thrift_client.erl | 23 +++++++++++++++++----- test/erl/src/test_tether.erl | 37 +++++++++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/lib/erl/src/thrift_client.erl b/lib/erl/src/thrift_client.erl index 92c531ab..d5bb146a 100644 --- a/lib/erl/src/thrift_client.erl +++ b/lib/erl/src/thrift_client.erl @@ -101,12 +101,14 @@ start(Host, Port, Service, Options) %% ProtocolFactory :: fun() -> thrift_protocol() start(ProtocolFactory, Service, ClientOpts) when is_function(ProtocolFactory), is_atom(Service) -> - Starter = + {Starter, Opts} = case lists:keysearch(monitor, 1, ClientOpts) of {value, {monitor, link}} -> - start_link; + {start_link, []}; + {value, {monitor, tether}} -> + {start, [{tether, self()}]}; _ -> - start + {start, []} end, Connect = @@ -119,7 +121,7 @@ start(ProtocolFactory, Service, ClientOpts) end, - Started = gen_server:Starter(?MODULE, [Service], []), + Started = gen_server:Starter(?MODULE, [Service, Opts], []), if Connect -> @@ -171,7 +173,13 @@ close(Client) when is_pid(Client) -> %% {stop, Reason} %% Description: Initiates the server %%-------------------------------------------------------------------- -init([Service]) -> +init([Service, Opts]) -> + case lists:keysearch(tether, 1, Opts) of + {value, {tether, Pid}} -> + erlang:monitor(process, Pid); + _Else -> + ok + end, {ok, #state{service = Service}}. %%-------------------------------------------------------------------- @@ -262,6 +270,11 @@ handle_cast(_Msg, State) -> %% {stop, Reason, State} %% Description: Handling all non call/cast messages %%-------------------------------------------------------------------- +handle_info({'DOWN', MonitorRef, process, Pid, _Info}, State) + when is_reference(MonitorRef), is_pid(Pid) -> + %% We don't actually verify the correctness of the DOWN message. + {stop, parent_died, State}; + handle_info(_Info, State) -> {noreply, State}. diff --git a/test/erl/src/test_tether.erl b/test/erl/src/test_tether.erl index 35794e63..3088cbdc 100644 --- a/test/erl/src/test_tether.erl +++ b/test/erl/src/test_tether.erl @@ -27,7 +27,16 @@ t() -> io:format("PASS. Linked owner is dead.~n") end, - check_extras(2), + Pid3 = erlang:spawn(?MODULE, test_tethered, []), + receive after 200 -> ok end, % Wait for completion. + case is_up(Pid3) of + true -> + io:format("PASS. Tethered owner still alive.~n"); + false -> + io:format("FAIL. Tethered owner is dead.~n") + end, + + check_extras(3), erlang:halt(). @@ -54,7 +63,11 @@ check_extras(N) -> {linked, true} -> io:format("FAIL. Linked client still alive.~n"); {linked, false} -> - io:format("PASS. Linked client dead.~n") + io:format("PASS. Linked client dead.~n"); + {tethered, true} -> + io:format("FAIL. Tethered client still alive.~n"); + {tethered, false} -> + io:format("PASS. Tethered client dead.~n") end, check_extras(N-1) after @@ -115,3 +128,23 @@ test_linked() -> %% Exit abnormally to kill our linked extra client. %% But we should never get here. exit(die). + +test_tethered() -> + {ok, Client1} = make_thrift_client([{connect, false}, {monitor, tether}]), + tester ! {client, tethered, Client1}, + {ok, Client2} = make_thrift_client([{connect, false}, {monitor, tether}]), + io:format("PASS. Tethered clients created.~n"), + try + gen_server:call(Client2, {connect, make_protocol_factory(2)}), + io:format("FAIL. Tethered client connected.~n", []) + catch + Kind:Info -> + io:format("PASS. Caught tethered error. ~p:~p~n", [Kind, Info]) + end, + receive after 100 -> + io:format("PASS. Still alive after tethered death.~n"), + % Hang around a little longer so our parent can verify. + receive after 200 -> ok end + end, + %% Exit abnormally to kill our tethered extra client. + exit(die). -- 2.17.1