summaryrefslogtreecommitdiff
path: root/lib/stdlib/src/gen.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib/src/gen.erl')
-rw-r--r--lib/stdlib/src/gen.erl72
1 files changed, 70 insertions, 2 deletions
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index 33af0aed8f..bb40798ba1 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.erl
@@ -28,7 +28,9 @@
%%%-----------------------------------------------------------------
-export([start/5, start/6, debug_options/2, hibernate_after/1,
name/1, unregister_name/1, get_proc_name/1, get_parent/0,
- call/3, call/4, reply/2, stop/1, stop/3]).
+ call/3, call/4, reply/2,
+ async_call/3, yield/1, yield/2,
+ stop/1, stop/3]).
-export([init_it/6, init_it/7]).
@@ -52,6 +54,11 @@
| {'spawn_opt', [proc_lib:spawn_option()]}.
-type options() :: [option()].
+-type server_ref() :: pid() | atom() | {atom(), node()}
+ | {global, term()} | {via, module(), term()}.
+
+-type promise() :: reference().
+
%%-----------------------------------------------------------------
%% Starts a generic process.
%% start(GenMod, LinkP, Mod, Args, Options)
@@ -138,7 +145,7 @@ init_it2(GenMod, Starter, Parent, Name, Mod, Args, Options) ->
%%-----------------------------------------------------------------
%% Makes a synchronous call to a generic process.
%% Request is sent to the Pid, and the response must be
-%% {Tag, _, Reply}.
+%% {Tag, Reply}.
%%-----------------------------------------------------------------
%%% New call function which uses the new monitor BIF
@@ -225,6 +232,67 @@ wait_resp(Node, Tag, Timeout) ->
exit(timeout)
end.
+-spec async_call(Name::server_ref(), Label::term(), Request::term()) -> promise().
+async_call(Process, Label, Request) when is_pid(Process); is_atom(Process) ->
+ do_async_call(Process, Label, Request);
+async_call({Name, _}=Process, Label, Request)
+ when is_atom(Name), Name =/= global ->
+ do_async_call(Process, Label, Request);
+async_call(Process, Label, Request) ->
+ try where(Process) of
+ Pid when is_pid(Pid) ->
+ do_async_call(Pid, Label, Request);
+ undefined ->
+ Ref = erlang:make_ref(),
+ self() ! {'DOWN', Ref, process, Process, noproc},
+ Ref
+ catch _:_ ->
+ error({badarg,Process})
+ end.
+
+do_async_call(Process, Label, Request) ->
+ try erlang:monitor(process, Process) of
+ Mref ->
+ %% If the monitor/2 call failed to set up a connection to a
+ %% remote node, we don't want the '!' operator to attempt
+ %% to set up the connection again. (If the monitor/2 call
+ %% failed due to an expired timeout, '!' too would probably
+ %% have to wait for the timeout to expire.) Therefore,
+ %% use erlang:send/3 with the 'noconnect' option so that it
+ %% will fail immediately if there is no connection to the
+ %% remote node.
+ catch erlang:send(Process, {Label, {self(), Mref}, Request},
+ [noconnect]),
+ Mref
+ catch
+ %% Do not support erl_interface or other systems which
+ %% non don't have monitor supported
+ error:_ -> error({badarg, Process})
+ end.
+
+%%
+%% Wait for a reply to the client.
+%% Note: if timeout is returned monitors are kept.
+
+-spec yield(Key::promise()) -> {ok, Reply::term()}.
+yield(Key) ->
+ yield(Key, infinity).
+
+-spec yield(Key::promise(), timeout()) -> {reply, Reply::term()} | 'timeout'.
+yield(Mref, Timeout) ->
+ receive
+ {Mref, Reply} ->
+ erlang:demonitor(Mref, [flush]),
+ {reply, Reply};
+ {'DOWN', Mref, _, Pid, noconnection} when is_pid(Pid) ->
+ exit({nodedown, node(Pid)});
+ {'DOWN', Mref, _, {_, Node}, noconnection} ->
+ exit({nodedown, Node});
+ {'DOWN', Mref, _, _, Reason} ->
+ exit(Reason)
+ after Timeout -> timeout
+ end.
+
%%
%% Send a reply to the client.
%%