Rollback a few recent Erlang changes to fix blame data

My combined patch for THRIFT-599 was committed, but it is preferable
commit the individual patches to preserve the more detailed log and
blame data.  I'll recommit r987018 as a sequence of patches and r988722
as its own rev.

git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@990957 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/test/erl/Makefile b/test/erl/Makefile
index a6b5ae6..2126037 100644
--- a/test/erl/Makefile
+++ b/test/erl/Makefile
@@ -29,7 +29,7 @@
 ALL_INCLUDEDIR=$(GEN_INCLUDEDIR) $(INCLUDEDIR) ../../lib/erl/include
 INCLUDEFLAGS=$(patsubst %,-I%, ${ALL_INCLUDEDIR})
 
-MODULES = stress_server test_server test_client test_disklog test_membuffer
+MODULES = stress_server test_server test_disklog test_membuffer test_tether
 
 INCLUDES = 
 TARGETS = $(patsubst %,${TARGETDIR}/%.beam,${MODULES})
@@ -55,11 +55,11 @@
 ${GEN_TARGETDIR}/: ${GENDIR}/
 	rm -rf ${GEN_TARGETDIR}
 	mkdir -p ${GEN_TARGETDIR}
-	erlc ${ERLC_FLAGS} ${INCLUDEFLAGS} -o ${GEN_TARGETDIR} ${GEN_SRCDIR}/*.erl
+	erlc ${INCLUDEFLAGS} -o ${GEN_TARGETDIR} ${GEN_SRCDIR}/*.erl
 
 $(TARGETS): ${TARGETDIR}/%.beam: ${SRCDIR}/%.erl ${GEN_INCLUDEDIR}/ ${HEADERS}
 	mkdir -p ${TARGETDIR}
-	erlc ${ERLC_FLAGS} ${INCLUDEFLAGS} -o ${TARGETDIR} $<
+	erlc ${INCLUDEFLAGS} -o ${TARGETDIR} $<
 
 clean:
 	rm -f ${TARGETDIR}/*.beam
diff --git a/test/erl/src/test_client.erl b/test/erl/src/test_client.erl
deleted file mode 100644
index a26467f..0000000
--- a/test/erl/src/test_client.erl
+++ /dev/null
@@ -1,132 +0,0 @@
-%%
-%% Licensed to the Apache Software Foundation (ASF) under one
-%% or more contributor license agreements. See the NOTICE file
-%% distributed with this work for additional information
-%% regarding copyright ownership. The ASF licenses this file
-%% to you under the Apache License, Version 2.0 (the
-%% "License"); you may not use this file except in compliance
-%% with the License. You may obtain a copy of the License at
-%%
-%%   http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing,
-%% software distributed under the License is distributed on an
-%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-%% KIND, either express or implied. See the License for the
-%% specific language governing permissions and limitations
-%% under the License.
-%%
-
--module(test_client).
-
--export([start/0, start/1]).
-
--include("thriftTest_types.hrl").
-
--record(options, {port = 9090,
-                  client_opts = []}).
-
-parse_args(Args) -> parse_args(Args, #options{}).
-parse_args([], Opts) -> Opts;
-parse_args([Head | Rest], Opts) ->
-    NewOpts =
-        case catch list_to_integer(Head) of
-            Port when is_integer(Port) ->
-                Opts#options{port = Port};
-            _Else ->
-                case Head of
-                    "framed" ->
-                        Opts#options{client_opts = [{framed, true} | Opts#options.client_opts]};
-                    "" ->
-                        Opts;
-                    _Else ->
-                        erlang:error({bad_arg, Head})
-                end
-        end,
-    parse_args(Rest, NewOpts).
-
-
-start() -> start([]).
-start(Args) ->
-  #options{port = Port, client_opts = ClientOpts} = parse_args(Args),
-  {ok, Client0} = thrift_client_util:new(
-    "127.0.0.1", Port, thriftTest_thrift, ClientOpts),
-
-  DemoXtruct = #xtruct{
-    string_thing = <<"Zero">>,
-    byte_thing = 1,
-    i32_thing = 9128361,
-    i64_thing = 9223372036854775807},
-
-  DemoNest = #xtruct2{
-    byte_thing = 7,
-    struct_thing = DemoXtruct,
-    % Note that we don't set i32_thing, it will come back as undefined
-    % from the Python server, but 0 from the C++ server, since it is not
-    % optional
-    i32_thing = 2},
-
-  % Is it safe to match these things?
-  DemoDict = dict:from_list([ {Key, Key-10} || Key <- lists:seq(0,10) ]),
-  DemoSet = sets:from_list([ Key || Key <- lists:seq(-3,3) ]),
-
-  %DemoInsane = #insanity{
-  %  userMap = dict:from_list([{?thriftTest_FIVE, 5000}]),
-  %  xtructs = [#xtruct{ string_thing = <<"Truck">>, byte_thing = 8, i32_thing = 8, i64_thing = 8}]},
-
-  {Client01, {ok, ok}} = thrift_client:call(Client0, testVoid, []),
-
-  {Client02, {ok, <<"Test">>}}      = thrift_client:call(Client01, testString, ["Test"]),
-  {Client03, {ok, <<"Test">>}}      = thrift_client:call(Client02, testString, [<<"Test">>]),
-  {Client04, {ok, 63}}              = thrift_client:call(Client03, testByte, [63]),
-  {Client05, {ok, -1}}              = thrift_client:call(Client04, testI32, [-1]),
-  {Client06, {ok, 0}}               = thrift_client:call(Client05, testI32, [0]),
-  {Client07, {ok, -34359738368}}    = thrift_client:call(Client06, testI64, [-34359738368]),
-  {Client08, {ok, -5.2098523}}      = thrift_client:call(Client07, testDouble, [-5.2098523]),
-  {Client09, {ok, DemoXtruct}}      = thrift_client:call(Client08, testStruct, [DemoXtruct]),
-  {Client10, {ok, DemoNest}}        = thrift_client:call(Client09, testNest, [DemoNest]),
-  {Client11, {ok, DemoDict}}        = thrift_client:call(Client10, testMap, [DemoDict]),
-  {Client12, {ok, DemoSet}}         = thrift_client:call(Client11, testSet, [DemoSet]),
-  {Client13, {ok, [-1,2,3]}}        = thrift_client:call(Client12, testList, [[-1,2,3]]),
-  {Client14, {ok, 1}}               = thrift_client:call(Client13, testEnum, [?thriftTest_ONE]),
-  {Client15, {ok, 309858235082523}} = thrift_client:call(Client14, testTypedef, [309858235082523]),
-
-  % No python implementation, but works with C++ and Erlang.
-  %{Client16, {ok, InsaneResult}}    = thrift_client:call(Client15, testInsanity, [DemoInsane]),
-  %io:format("~p~n", [InsaneResult]),
-  Client16 = Client15,
-
-  {Client17, {ok, #xtruct{string_thing = <<"Message">>}}} =
-    thrift_client:call(Client16, testMultiException, ["Safe", "Message"]),
-
-  Client18 =
-    try
-      {ClientS1, Result1} = thrift_client:call(Client17, testMultiException, ["Xception", "Message"]),
-      io:format("Unexpected return! ~p~n", [Result1]),
-      ClientS1
-    catch
-      throw:{ClientS2, {exception, ExnS1 = #xception{}}} ->
-        #xception{errorCode = 1001, message = <<"This is an Xception">>} = ExnS1,
-        ClientS2;
-      throw:{ClientS2, {exception, _ExnS1 = #xception2{}}} ->
-        io:format("Wrong exception type!~n", []),
-        ClientS2
-    end,
-
-  Client19 =
-    try
-      {ClientS3, Result2} = thrift_client:call(Client18, testMultiException, ["Xception2", "Message"]),
-      io:format("Unexpected return! ~p~n", [Result2]),
-      ClientS3
-    catch
-      throw:{ClientS4, {exception, _ExnS2 = #xception{}}} ->
-        io:format("Wrong exception type!~n", []),
-        ClientS4;
-      throw:{ClientS4, {exception, ExnS2 = #xception2{}}} ->
-        #xception2{errorCode = 2002,
-                   struct_thing = #xtruct{
-                     string_thing = <<"This is an Xception2">>}} = ExnS2,
-        ClientS4
-    end,
-
-  thrift_client:close(Client19).
diff --git a/test/erl/src/test_disklog.erl b/test/erl/src/test_disklog.erl
index fc0dcf8..7b0be72 100644
--- a/test/erl/src/test_disklog.erl
+++ b/test/erl/src/test_disklog.erl
@@ -29,21 +29,20 @@
            {size, {1024*1024, 10}}]),
     {ok, ProtocolFactory} = thrift_binary_protocol:new_protocol_factory(
                               TransportFactory, []),
-    {ok, Proto} = ProtocolFactory(),
-    {ok, Client0} = thrift_client:new(Proto, thriftTest_thrift),
+    {ok, Client} = thrift_client:start_link(ProtocolFactory, thriftTest_thrift),
 
     io:format("Client started~n"),
 
     % We have to make oneway calls into this client only since otherwise it will try
     % to read from the disklog and go boom.
-    {Client1, {ok, ok}} = thrift_client:call(Client0, testOneway, [16#deadbeef]),
+    {ok, ok} = thrift_client:call(Client, testOneway, [16#deadbeef]),
     io:format("Call written~n"),
 
     % Use the send_call method to write a non-oneway call into the log
-    {Client2, ok} = thrift_client:send_call(Client1, testString, [<<"hello world">>]),
+    ok = thrift_client:send_call(Client, testString, [<<"hello world">>]),
     io:format("Non-oneway call sent~n"),
 
-    {_Client3, ok} = thrift_client:close(Client2),
+    ok = thrift_client:close(Client),
     io:format("Client closed~n"),
 
     ok.
@@ -62,22 +61,21 @@
         thrift_buffered_transport:new_transport_factory(B64Factory),
     {ok, ProtocolFactory} = thrift_binary_protocol:new_protocol_factory(
                               BufFactory, []),
-    {ok, Proto} = ProtocolFactory(),
-    {ok, Client0} = thrift_client:new(Proto, thriftTest_thrift),
+    {ok, Client} = thrift_client:start_link(ProtocolFactory, thriftTest_thrift),
 
     io:format("Client started~n"),
 
     % We have to make oneway calls into this client only since otherwise it will try
     % to read from the disklog and go boom.
-    {Client1, {ok, ok}} = thrift_client:call(Client0, testOneway, [16#deadbeef]),
+    {ok, ok} = thrift_client:call(Client, testOneway, [16#deadbeef]),
     io:format("Call written~n"),
 
     % Use the send_call method to write a non-oneway call into the log
-    {Client2, ok} = thrift_client:send_call(Client1, testString, [<<"hello world">>]),
+    ok = thrift_client:send_call(Client, testString, [<<"hello world">>]),
     io:format("Non-oneway call sent~n"),
 
-    {_Client3, ok} = thrift_client:close(Client2),
+    ok = thrift_client:close(Client),
     io:format("Client closed~n"),
 
     ok.
-
+    
diff --git a/test/erl/src/test_membuffer.erl b/test/erl/src/test_membuffer.erl
index 19ac527..7bd23a0 100644
--- a/test/erl/src/test_membuffer.erl
+++ b/test/erl/src/test_membuffer.erl
@@ -30,12 +30,12 @@
 
 t1() ->
     {ok, Transport} = thrift_memory_buffer:new(),
-    {ok, Protocol0} = thrift_binary_protocol:new(Transport),
+    {ok, Protocol} = thrift_binary_protocol:new(Transport),
     TestData = test_data(),
-		{Protocol1, ok} = thrift_protocol:write(Protocol0,
+    ok = thrift_protocol:write(Protocol,
 			       {{struct, element(2, thriftTest_types:struct_info('xtruct'))},
 				TestData}),
-		{_Protocol2, {ok, Result}} = thrift_protocol:read(Protocol1,
+    {ok, Result} = thrift_protocol:read(Protocol,
 					{struct, element(2, thriftTest_types:struct_info('xtruct'))},
 					'xtruct'),
 
@@ -44,12 +44,12 @@
 
 t2() ->
     {ok, Transport} = thrift_memory_buffer:new(),
-    {ok, Protocol0} = thrift_binary_protocol:new(Transport),
+    {ok, Protocol} = thrift_binary_protocol:new(Transport),
     TestData = test_data(),
-		{Protocol1, ok} = thrift_protocol:write(Protocol0,
+    ok = thrift_protocol:write(Protocol,
 			       {{struct, element(2, thriftTest_types:struct_info('xtruct'))},
 				TestData}),
-		{_Protocol2, {ok, Result}} = thrift_protocol:read(Protocol1,
+    {ok, Result} = thrift_protocol:read(Protocol,
 					{struct, element(2, thriftTest_types:struct_info('xtruct3'))},
 					'xtruct3'),
 
@@ -61,12 +61,12 @@
 
 t3() ->
     {ok, Transport} = thrift_memory_buffer:new(),
-    {ok, Protocol0} = thrift_binary_protocol:new(Transport),
+    {ok, Protocol} = thrift_binary_protocol:new(Transport),
     TestData = #bools{im_true = true, im_false = false},
-		{Protocol1, ok} = thrift_protocol:write(Protocol0,
+    ok = thrift_protocol:write(Protocol,
 			       {{struct, element(2, thriftTest_types:struct_info('bools'))},
 				TestData}),
-		{_Protocol2, {ok, Result}} = thrift_protocol:read(Protocol1,
+    {ok, Result} = thrift_protocol:read(Protocol,
 					{struct, element(2, thriftTest_types:struct_info('bools'))},
 					'bools'),
 
@@ -74,23 +74,8 @@
     true = TestData#bools.im_false =:= Result#bools.im_false.
 
 
-t4() ->
-    {ok, Transport} = thrift_memory_buffer:new(),
-    {ok, Protocol0} = thrift_binary_protocol:new(Transport),
-    TestData = #insanity{xtructs=[]},
-		{Protocol1, ok} = thrift_protocol:write(Protocol0,
-			       {{struct, element(2, thriftTest_types:struct_info('insanity'))},
-				TestData}),
-		{_Protocol2, {ok, Result}} = thrift_protocol:read(Protocol1,
-					{struct, element(2, thriftTest_types:struct_info('insanity'))},
-					'insanity'),
-
-    TestData = Result.
-
-
 t() ->
     t1(),
     t2(),
-    t3(),
-    t4().
+    t3().
 
diff --git a/test/erl/src/test_server.erl b/test/erl/src/test_server.erl
index 28d47b1..cd439cc 100644
--- a/test/erl/src/test_server.erl
+++ b/test/erl/src/test_server.erl
@@ -19,42 +19,12 @@
 
 -module(test_server).
 
--export([go/0, go/1, start_link/2, handle_function/2]).
+-export([start_link/1, handle_function/2]).
 
 -include("thriftTest_types.hrl").
 
--record(options, {port = 9090,
-                  server_opts = []}).
-
-parse_args(Args) -> parse_args(Args, #options{}).
-parse_args([], Opts) -> Opts;
-parse_args([Head | Rest], Opts) ->
-    NewOpts =
-        case catch list_to_integer(Head) of
-            Port when is_integer(Port) ->
-                Opts#options{port = Port};
-            _Else ->
-                case Head of
-                    "framed" ->
-                        Opts#options{server_opts = [{framed, true} | Opts#options.server_opts]};
-                    "" ->
-                        Opts;
-                    _Else ->
-                        erlang:error({bad_arg, Head})
-                end
-        end,
-    parse_args(Rest, NewOpts).
-
-go() -> go([]).
-go(Args) ->
-    #options{port = Port, server_opts = ServerOpts} = parse_args(Args),
-    spawn(fun() -> start_link(Port, ServerOpts), receive after infinity -> ok end end).
-
-start_link(Port, ServerOpts) ->
-    thrift_socket_server:start([{handler, ?MODULE},
-                                {service, thriftTest_thrift},
-                                {port, Port}] ++
-                               ServerOpts).
+start_link(Port) ->
+    thrift_server:start_link(Port, thriftTest_thrift, ?MODULE).
 
 
 handle_function(testVoid, {}) ->
@@ -154,12 +124,12 @@
                                {?thriftTest_THREE, Crazy}]),
 
     SecondMap = dict:from_list([{?thriftTest_SIX, Looney}]),
-
+    
     Insane = dict:from_list([{1, FirstMap},
                              {2, SecondMap}]),
-
+    
     io:format("Return = ~p~n", [Insane]),
-
+    
     {reply, Insane};
 
 handle_function(testMulti, Args = {Arg0, Arg1, Arg2, _Arg3, Arg4, Arg5})
@@ -180,7 +150,7 @@
     case String of
         <<"Xception">> ->
             throw(#xception{errorCode = 1001,
-                            message = String});
+                            message = <<"This is an Xception">>});
         _ ->
             ok
     end;
diff --git a/test/erl/src/test_tether.erl b/test/erl/src/test_tether.erl
new file mode 100644
index 0000000..dc11a9a
--- /dev/null
+++ b/test/erl/src/test_tether.erl
@@ -0,0 +1,186 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%%   http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+%% Tests the behavior of clients in the face of transport errors.
+%% Makes sure start, start_linked, and start_tethered work as expected.
+
+-module(test_tether).
+
+-compile(export_all).
+
+
+t() ->
+    io:format("Beginning transport error test.~n"),
+    Pid1 = erlang:spawn(?MODULE, t_sub, [2]),
+    wait_for(Pid1),
+    io:format("Beginning protocol error test.~n"),
+    Pid2 = erlang:spawn(?MODULE, t_sub, [22]),
+    wait_for(Pid2),
+    ok.
+
+t_sub(Port) ->
+    io:format("Starting.~n", []),
+    register(tester, self()),
+
+    Pid1 = erlang:spawn(?MODULE, test_start, [Port]),
+    receive after 200 -> ok end,  % Wait for completion.
+    case is_up(Pid1) of
+        true ->
+            io:format("PASS.  Unlinked owner still alive.~n");
+        false ->
+            io:format("FAIL.  Unlinked owner is dead.~n")
+    end,
+
+    Pid2 = erlang:spawn(?MODULE, test_linked, [Port]),
+    receive after 200 -> ok end,  % Wait for completion.
+    case is_up(Pid2) of
+        true ->
+            io:format("FAIL.  Linked owner still alive.~n");
+        false ->
+            io:format("PASS.  Linked owner is dead.~n")
+    end,
+
+    Pid3 = erlang:spawn(?MODULE, test_tethered, [Port]),
+    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).
+
+is_up(Pid) ->
+    MonitorRef = erlang:monitor(process, Pid),
+    receive
+        {'DOWN', MonitorRef, process, Pid, _Info} ->
+            false
+    after
+        50 ->
+            erlang:demonitor(MonitorRef),
+            true
+    end.
+
+wait_for(Pid) ->
+    MonitorRef = erlang:monitor(process, Pid),
+    receive
+        {'DOWN', MonitorRef, process, Pid, _Info} ->
+            ok
+    end.
+
+check_extras(0) -> ok;
+check_extras(N) ->
+    receive
+        {client, Type, Pid} ->
+            case {Type, is_up(Pid)} of
+                {unlinked, true} ->
+                    io:format("PASS.  Unlinked client still alive.~n");
+                {unlinked, false} ->
+                    io:format("FAIL.  Unlinked client dead.~n");
+                {linked, true} ->
+                    io:format("FAIL.  Linked client still alive.~n");
+                {linked, false} ->
+                    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
+        500 ->
+            io:format("FAIL.  Expected ~p more clients.~n", [N])
+    end.
+
+make_thrift_client(Opts) ->
+     thrift_client:start(fun()->ok end, thriftTest_thrift, Opts).
+
+make_protocol_factory(Port) ->
+    {ok, TransportFactory} =
+        thrift_socket_transport:new_transport_factory(
+          "127.0.0.1", Port, []),
+    {ok, ProtocolFactory} =
+        thrift_binary_protocol:new_protocol_factory(
+          TransportFactory, []),
+    ProtocolFactory.
+
+
+test_start(Port) ->
+    {ok, Client1} = make_thrift_client([{connect, false}]),
+    tester ! {client, unlinked, Client1},
+    {ok, Client2} = make_thrift_client([{connect, false}]),
+    io:format("PASS.  Unlinked clients created.~n"),
+    try
+        gen_server:call(Client2, {connect, make_protocol_factory(Port)}),
+        thrift_client:call(Client2, testVoid, []),
+        io:format("FAIL.  Unlinked client connected and called.~n", [])
+    catch
+        Kind:Info ->
+            io:format("PASS.  Caught unlinked error.  ~p:~p~n", [Kind, Info])
+    end,
+    receive after 100 ->
+                    io:format("PASS.  Still alive after unlinked death.~n"),
+                    %% Hang around a little longer so our parent can verify.
+                    receive after 200 -> ok end
+    end,
+    %% Exit abnormally to not kill our unlinked extra client.
+    exit(die).
+
+test_linked(Port) ->
+    {ok, Client1} = make_thrift_client([{connect, false}, {monitor, link}]),
+    tester ! {client, linked, Client1},
+    {ok, Client2} = make_thrift_client([{connect, false}, {monitor, link}]),
+    io:format("PASS.  Linked clients created.~n"),
+    try
+        gen_server:call(Client2, {connect, make_protocol_factory(Port)}),
+        thrift_client:call(Client2, testVoid, []),
+        io:format("FAIL.  Linked client connected and called.~n", [])
+    catch
+        Kind:Info ->
+            io:format("FAIL.  Caught linked error.  ~p:~p~n", [Kind, Info])
+    end,
+    receive after 100 ->
+                    io:format("FAIL.  Still alive after linked death.~n"),
+                    % Hang around a little longer so our parent can verify.
+                    receive after 200 -> ok end
+    end,
+    %% Exit abnormally to kill our linked extra client.
+    %% But we should never get here.
+    exit(die).
+
+test_tethered(Port) ->
+    {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(Port)}),
+        thrift_client:call(Client2, testVoid, []),
+        io:format("FAIL.  Tethered client connected and called.~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).