各种有用的erlang行为
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

128 lines
4.1 KiB

  1. -module(gen_call).
  2. -export([gcall/3, gcall/4, greply/2]).
  3. -define(default_timeout, 5000).
  4. %% We trust the arguments to be correct, i.e
  5. %% Process is either a local or remote pid,
  6. %% or a {Name, Node} tuple (of atoms) and in this
  7. %% case this node (node()) _is_ distributed and Node =/= node().
  8. -define(get_node(Process), case Process of {_S, N} -> N; _ -> node(Process) end).
  9. gcall(Process, Label, Request) ->
  10. gcall(Process, Label, Request, ?default_timeout).
  11. gcall(Process, Label, Request, Timeout) ->
  12. %%-----------------------------------------------------------------
  13. %% Map different specifications of a process to either Pid or
  14. %% {Name,Node}. Execute the given Fun with the process as only
  15. %% argument.
  16. %% -----------------------------------------------------------------
  17. case where(Process) of
  18. undefined ->
  19. exit(noproc);
  20. PidOrNameNode ->
  21. %Node = ?get_node(PidOrNameNode),
  22. try do_call(PidOrNameNode, Label, Request, Timeout)
  23. catch
  24. exit:{nodedown, _Node} ->
  25. %% A nodedown not yet detected by global, pretend that it was.
  26. exit(noproc)
  27. end
  28. end.
  29. -dialyzer({no_improper_lists, do_call/4}).
  30. do_call(Process, Label, Request, Timeout) ->
  31. CurNode = node(),
  32. case ?get_node(Process) of
  33. CurNode ->
  34. Mref = erlang:monitor(process, Process),
  35. %% Local without timeout; no need to use alias since we unconditionally
  36. %% will wait for either a reply or a down message which corresponds to
  37. %% the process being terminated (as opposed to 'noconnection')...
  38. case self() of
  39. Process ->
  40. exit(calling_self);
  41. _ ->
  42. Process ! {Label, {self(), Mref}, Request},
  43. end,
  44. receive
  45. {Mref, Reply} ->
  46. erlang:demonitor(Mref, [flush]),
  47. {ok, Reply};
  48. {'DOWN', Mref, _, _, Reason} ->
  49. exit(Reason)
  50. after Timeout ->
  51. erlang:demonitor(Mref, [flush]),
  52. receive
  53. {[alias | Mref], Reply} ->
  54. {ok, Reply}
  55. after 0 ->
  56. exit(timeout)
  57. end
  58. end;
  59. _PNode ->
  60. Mref = erlang:monitor(process, Process, [{alias, demonitor}]),
  61. Tag = [alias | Mref],
  62. %% OTP-24:
  63. %% Using alias to prevent responses after 'noconnection' and timeouts.
  64. %% We however still may call nodes responding via process identifier, so
  65. %% we still use 'noconnect' on send in order to try to send on the
  66. %% monitored connection, and not trigger a new auto-connect.
  67. %%
  68. erlang:send(Process, {Label, {self(), Tag}, Request}, [noconnect]),
  69. receive
  70. {[alias | Mref], Reply} ->
  71. erlang:demonitor(Mref, [flush]),
  72. {ok, Reply};
  73. {'DOWN', Mref, _, _, noconnection} ->
  74. exit({nodedown, _PNode});
  75. {'DOWN', Mref, _, _, Reason} ->
  76. exit(Reason)
  77. after Timeout ->
  78. erlang:demonitor(Mref, [flush]),
  79. receive
  80. {[alias | Mref], Reply} ->
  81. {ok, Reply}
  82. after 0 ->
  83. exit(timeout)
  84. end
  85. end
  86. end.
  87. where({global, Name}) -> global:whereis_name(Name);
  88. where({local, Name}) -> whereis(Name);
  89. where({via, Module, Name}) -> Module:whereis_name(Name);
  90. where({Name, Node} = Process) ->
  91. CurNode = node(),
  92. case CurNode of
  93. Node ->
  94. whereis(Name);
  95. nonode@nohost ->
  96. exit({nodedown, Node});
  97. _ when is_atom(Name), is_atom(Node) ->
  98. Process;
  99. _ ->
  100. undefined
  101. end;
  102. where(Name) ->
  103. if
  104. is_pid(Name) ->
  105. Name;
  106. is_atom(Name) ->
  107. whereis(Name);
  108. true ->
  109. undefined
  110. end.
  111. %%
  112. %% Send a reply to the client.
  113. %%
  114. greply({_To, [alias | Alias] = Tag}, Reply) when is_reference(Alias) ->
  115. Alias ! {Tag, Reply}, ok;
  116. greply({_To, [[alias | Alias] | _] = Tag}, Reply) when is_reference(Alias) ->
  117. Alias ! {Tag, Reply}, ok;
  118. greply({To, Tag}, Reply) ->
  119. try To ! {Tag, Reply}, ok catch _:_ -> ok end.