Properly handle exceptions


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@666384 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/compiler/cpp/src/generate/t_alterl_generator.cc b/compiler/cpp/src/generate/t_alterl_generator.cc
index 9f75387..cb4c1af 100644
--- a/compiler/cpp/src/generate/t_alterl_generator.cc
+++ b/compiler/cpp/src/generate/t_alterl_generator.cc
@@ -535,10 +535,7 @@
   indent(f_service_) <<
     "function_info(" << name_atom << ", exceptions) ->" << endl;
   indent_up();
-
-  // TODO(todd) exceptions here are probably broken
-  indent(f_service_) << generate_type_term(xs, false) << ";" << endl;
-
+  indent(f_service_) << generate_type_term(xs, true) << ";" << endl;
   indent_down();
 
   // function_info(Function, is_async):
diff --git a/lib/alterl/src/thrift_processor.erl b/lib/alterl/src/thrift_processor.erl
index 14724aa..217c216 100644
--- a/lib/alterl/src/thrift_processor.erl
+++ b/lib/alterl/src/thrift_processor.erl
@@ -46,29 +46,92 @@
 
     {ok, Params} = thrift_protocol:read(IProto, InParams),
 
-    {Micro, Result} = timer:tc(Handler, handle_function, [Function, Params]),
-    error_logger:info_msg("Processed ~p(~p) in ~.4fms~n",
-                          [Function, Params, Micro/1000.0]),
-    
+    try
+        {Micro, Result} = better_timer(Handler, handle_function, [Function, Params]),
+        error_logger:info_msg("Processed ~p(~p) in ~.4fms~n",
+                              [Function, Params, Micro/1000.0]),
+        handle_success(State, Function, Result)
+    catch
+        throw:Exception when is_tuple(Exception), size(Exception) > 0 ->
+            error_logger:warning_msg("~p threw exception: ~p~n", [Function, Exception]),
+            handle_exception(State, Function, Exception),
+            ok % we still want to accept more requests from this client
+    end.
+
+handle_success(State = #state{out_protocol = OProto,
+                              service = Service},
+               Function,
+               Result) ->
     ReplyType = Service:function_info(Function, reply_type),
     StructName = atom_to_list(Function) ++ "_result",
     
     case Result of
         {reply, ReplyData} -> 
             Reply = {{struct, [{0, ReplyType}]}, {StructName, ReplyData}},
-            ok = send_reply(OProto, Function, Reply);
+            ok = send_reply(OProto, Function, ?tMessageType_REPLY, Reply);
 
         ok when ReplyType == {struct, []} ->
-            ok = send_reply(OProto, Function, {ReplyType, {StructName}})
+            ok = send_reply(OProto, Function, ?tMessageType_REPLY, {ReplyType, {StructName}})
     end,
     ok.
 
+handle_exception(State = #state{out_protocol = OProto,
+                                service = Service},
+                 Function,
+                 Exception) ->
+    ExceptionType = element(1, Exception),
+    % Fetch a structure like {struct, [{-2, {struct, {Module, Type}}},
+    %                                  {-3, {struct, {Module, Type}}}]}
 
-send_reply(OProto, Function, Reply) ->
+    ReplySpec = Service:function_info(Function, exceptions),
+    {struct, XInfo} = ReplySpec,
+
+    true = is_list(XInfo),
+    
+    % e.g.: [{-1, type0}, {-2, type1}, {-3, type2}]
+    XPairs = [{Fid, Type} || {Fid, {struct, {_Module, Type}}} <- XInfo],
+
+    Mapper = fun({Fid, Type}) ->
+                     case Type of
+                         ExceptionType ->
+                             Exception;
+                         _ ->
+                             undefined
+                     end
+             end,
+    % Assuming we had a type1 exception, we get: [undefined, Exception, undefined]
+    ExceptionList = lists:map(Mapper, XPairs),
+    ExceptionTuple = list_to_tuple([Function | ExceptionList]),
+    
+    % Make sure we got at least one defined
+    case lists:all(fun(X) -> X =:= undefined end, ExceptionList) of
+        true ->
+            ok = handle_unknown_exception(State, Function, Exception);
+        false ->
+            ok = send_reply(OProto, Function, ?tMessageType_REPLY, {ReplySpec, ExceptionTuple})
+    end.
+
+handle_unknown_exception(State, Function, Exception) ->
+    io:format("Unknown exception!~n"),
+    ok.
+
+
+send_reply(OProto, Function, ReplyMessageType, Reply) ->
     ok = thrift_protocol:write(OProto, #protocol_message_begin{
                                  name = atom_to_list(Function),
-                                 type = ?tMessageType_REPLY,
+                                 type = ReplyMessageType,
                                  seqid = 0}),
     ok = thrift_protocol:write(OProto, Reply),
     ok = thrift_protocol:write(OProto, message_end),
     ok.
+
+
+%%
+% This is the same as timer:tc except that timer:tc appears to catch
+% exceptions when it shouldn't!
+%%
+better_timer(Module, Function, Args) ->
+    T1 = erlang:now(),
+    Result = apply(Module, Function, Args),
+    T2 = erlang:now(),
+    {timer:now_diff(T2, T1), Result}.