diff --git a/src/emqx_router.erl b/src/emqx_router.erl index 7288d2e19..75b567666 100644 --- a/src/emqx_router.erl +++ b/src/emqx_router.erl @@ -266,28 +266,38 @@ maybe_trans(Fun, Args) -> end, []) end. +%% The created fun only terminates with explicit exception +-dialyzer({nowarn_function, [trans/2]}). + -spec(trans(function(), list(any())) -> ok | {error, term()}). trans(Fun, Args) -> - %% trigger selective receive optimization of compiler, - %% ideal for handling bursty traffic. - Ref = erlang:make_ref(), - Owner = self(), - {WPid, RefMon} = spawn_monitor( - fun() -> - Res = case mnesia:transaction(Fun, Args) of - {atomic, Ok} -> Ok; - {aborted, Reason} -> {error, Reason} - end, - Owner ! {Ref, Res} - end), + {WPid, RefMon} = + spawn_monitor( + %% NOTE: this is under the assumption that crashes in Fun + %% are caught by mnesia:transaction/2. + %% Future changes should keep in mind that this process + %% always exit with database write result. + fun() -> + Res = case mnesia:transaction(Fun, Args) of + {atomic, Ok} -> Ok; + {aborted, Reason} -> {error, Reason} + end, + exit({shutdown, Res}) + end), + %% Receive a 'shutdown' exit to pass result from the short-lived process. + %% so the receive below can be receive-mark optimized by the compiler. + %% + %% If the result is sent as a regular message, we'll have to + %% either demonitor (with flush which is essentially a 'receive' since + %% the process is no longer alive after the result has been received), + %% or use a plain 'receive' to drain the normal 'DOWN' message. + %% However the compiler does not optimize this second 'receive'. receive - {Ref, TransRes} -> - receive - {'DOWN', RefMon, process, WPid, normal} -> ok - end, - TransRes; {'DOWN', RefMon, process, WPid, Info} -> - {error, {trans_crash, Info}} + case Info of + {shutdown, Result} -> Result; + _ -> {error, {trans_crash, Info}} + end end. lock_router() ->