File 2251-stdlib-Add-send_request-and-wait_response-to-generic.patch of Package erlang

From 9a4f7576f8b85e87686705d0b6cf8e778a5cb7a8 Mon Sep 17 00:00:00 2001
From: Dan Gudmundsson <dgud@erlang.org>
Date: Thu, 21 Sep 2017 13:43:52 +0200
Subject: [PATCH 1/2] stdlib: Add 'send_request' and 'wait_response' to generic
 behaviors

Simplify and encourage users to do more async work. The usage pattern
is already available in the 'rpc' module and similar usages are
available in other languages and standards.

Currently async calls can be implemented via cast or regular messages,
but the user needs to implement it in both client and server.

In the implementation in this commit the server does not need to know
that the client are making async calls.

`wait_response(Promise)` returns `{reply, Reply} | {error, Reason}`.

`wait_response(Promise, Timeout)` returns `{reply, Reply}` | {error,
 Reason} or `timeout` if there is a client-side timeout.

The reason for the `reply` tuple is that `Reply` may be positive or
negative answer. That is, `{reply, {ok, Value}}` and `{reply, {error,
Reason}}` looks better than `{ok, {error, Reason}}` or `{ok, {ok,
Value}}`. We also need to encapsulate the return value to
differentiate between client timeouts and the server response that may
be the atom `timeout`.

We don't want do `exit(timeout)` since in that case is is not possible
to do non-blocking wait_response without catching the call to `wait_response(Promise, 0)`.

`check_response(Message, ReqId)` returns `{reply, Reply}` if the
`Message` is a reply from the server associated with the handle
`ReqId`. Otherwise it returns `no_reply`.

`check_response/2` is introduced to be able to handle concurrent
async_calls in a receive loop or in a handle_info callback in another
gen_server.

```
Promise = gen:send_request(..),
  .
  .
  .
dispatch(State) ->
  receive
     Msg ->
        case gen:check_response(Msg, Promise) of
            {reply, Reply} ->
	       {Reply, State};
            no_reply ->
               State0 = handle_msg(Msg, State0),
               dispatch(State0)
         end
end.
```

Both wait_response and check_response leaves the monitor until a proper response
is received or the moniter fires. To be able to poll for response.

The functions in rpc are named async_call and yield but
OTB decided to use the names 'send_request', 'wait_response' and
'check_response' here instead.

Returns error tuples in case of server (or network) errors in
'check_response' and 'wait_response'.

For some cases, for example named process not existing, the
'send_request' part in 'gen:call' exited, this is now catched and a
'DOWN' message is faked so that all error handling can be done when
handling the response.

This enables to extend the functionality with
check_response_m(Msg, MapWithRequestIds)
---
 lib/stdlib/doc/src/gen_event.xml     | 130 +++++++++++++++++++++++++++++++++
 lib/stdlib/doc/src/gen_server.xml    | 138 ++++++++++++++++++++++++++++++++++-
 lib/stdlib/doc/src/gen_statem.xml    | 132 ++++++++++++++++++++++++++++++++-
 lib/stdlib/src/gen.erl               |  61 +++++++++++++++-
 lib/stdlib/src/gen_event.erl         |  25 ++++++-
 lib/stdlib/src/gen_server.erl        |  30 ++++++++
 lib/stdlib/src/gen_statem.erl        |  25 ++++++-
 lib/stdlib/test/gen_event_SUITE.erl  |  38 ++++++++--
 lib/stdlib/test/gen_server_SUITE.erl |  88 +++++++++++++++++++++-
 lib/stdlib/test/gen_statem_SUITE.erl |  10 +++
 lib/wx/src/wx_object.erl             |  32 ++++++++
 lib/wx/test/wx_basic_SUITE.erl       |  11 ++-
 12 files changed, 696 insertions(+), 24 deletions(-)

diff --git a/lib/stdlib/doc/src/gen_event.xml b/lib/stdlib/doc/src/gen_event.xml
index 2915c4f507..29a834a830 100644
--- a/lib/stdlib/doc/src/gen_event.xml
+++ b/lib/stdlib/doc/src/gen_event.xml
@@ -58,6 +58,7 @@ gen_event:add_sup_handler  ----->  Module:init/1
 gen_event:notify
 gen_event:sync_notify      ----->  Module:handle_event/2
 
+gen_event:send_request
 gen_event:call             ----->  Module:handle_call/2
 
 -                          ----->  Module:handle_info/2
@@ -126,6 +127,15 @@ gen_event:stop             ----->  Module:terminate/2
     <datatype>
       <name name="del_handler_ret"/>
     </datatype>
+    <datatype>
+      <name name="request_id"/>
+      <desc>
+	<p>
+	  A request handle, see <seealso marker="#send_request/3"> <c>send_request/3</c> </seealso>
+	  for details.
+	</p>
+      </desc>
+    </datatype>
   </datatypes>
 
   <funcs>
@@ -284,6 +294,40 @@ gen_event:stop             ----->  Module:terminate/2
       </desc>
     </func>
 
+
+    <func>
+      <name since="OTP-23">check_response(Msg, RequestId) -> Result</name>
+      <fsummary>Check if a message is a reply from a server.</fsummary>
+      <type>
+	<v>Msg = term()</v>
+	<v>RequestId = request_id()</v>
+	<v>Result = {reply, Reply} | no_reply | {error, Error}</v>
+	<v>Reply = Error = term()</v>
+      </type>
+      <desc>
+	<p>
+	  This function is used to check if a previously received
+	  message, for example by <c>receive</c> or
+	  <c>handle_info/2</c>, is a result of a request made with
+	  <seealso marker="#send_request/3"><c>send_request/3</c></seealso>.
+	  If <c>Msg</c> is a reply to the handle <c>RequestId</c>
+	  the result of the request is returned in <c>Reply</c>.
+	  Otherwise returns <c>no_reply</c> and no cleanup is done, and
+	  thus the function shall be invoked repeatedly until a reply
+	  is returned.
+	</p>
+	<p>
+	  If the specified event handler is not
+          installed, the function returns <c>{error,bad_module}</c>. If
+          the callback function fails with <c>Reason</c> or returns an
+          unexpected value <c>Term</c>, this function returns
+          <c>{error,{'EXIT',Reason}}</c> or <c>{error,Term}</c>,
+          respectively. If the event manager dies before or during the
+	  request this function returns <c>{error,{Reason, EventMgrRef}}</c>.
+	</p>
+      </desc>
+    </func>
+
     <func> 
       <name since="">delete_handler(EventMgrRef, Handler, Args) -> Result</name>
       <fsummary>Delete an event handler from a generic event manager.</fsummary>
@@ -348,6 +392,49 @@ gen_event:stop             ----->  Module:terminate/2
       </desc>
     </func>
 
+
+    <func>
+      <name since="OTP-23">send_request(EventMgrRef, Handler, Request) -> RequestId</name>
+      <fsummary>Send a request to a generic event manager.</fsummary>
+      <type>
+        <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}</v>
+        <v>&nbsp;&nbsp;| {via,Module,ViaName} | pid()</v>
+        <v>&nbsp;Node = atom()</v>
+        <v>&nbsp;GlobalName = ViaName = term()</v>
+	<v>Handler = Module | {Module,Id}</v>
+        <v>&nbsp;Module = atom()</v>
+        <v>&nbsp;Id = term()</v>
+        <v>Request = term()</v>
+        <v>RequestId = request_id()</v>
+      </type>
+      <desc>
+        <p>
+	  Sends a request to event handler <c>Handler</c> installed in
+	  event manager <c>EventMgrRef</c> and returns a handle
+	  <c>RequestId</c>.  The return value <c>RequestId</c> shall
+	  later be used with <seealso marker="#wait_response/2">
+	  <c>wait_response/2</c></seealso> or <seealso
+	  marker="#check_response/2">
+	  <c>check_response/2</c></seealso> in the same process to
+	  fetch the actual result of the request.
+	</p>
+	<p>
+	  The call <c>gen_event:wait_response(gen_event:send_request(EventMgrRef,Handler,Request), Timeout)</c>
+	  can be seen as equivalent to
+	  <seealso marker="#call/3"><c>gen_event:call(EventMgrRef,Handler,Request,Timeout)</c></seealso>,
+	  ignoring the error handling.
+	</p>
+	<p>
+	  The event manager calls <seealso marker="#Module:handle_call/2">
+          <c>Module:handle_call/2</c></seealso> to handle the request.
+	</p>
+        <p>
+	  <c>Request</c> is any term that is passed as one of
+          the arguments to <c>Module:handle_call/3</c>.
+	</p>
+      </desc>
+    </func>
+
     <func>
       <name since="">start() -> Result</name>
       <name since="">start(EventMgrName | Options) -> Result</name>
@@ -545,6 +632,49 @@ gen_event:stop             ----->  Module:terminate/2
       </desc>
     </func>
 
+    <func>
+      <name since="OTP-23">wait_response(RequestId, Timeout) -> Result</name>
+      <fsummary>Wait for a reply from a server.</fsummary>
+      <type>
+	<v>RequestId = request_id()</v>
+	<v>Reply = term()</v>
+	<v>Timeout = timeout()</v>
+	<v>Result = {reply, Reply} | timeout | {error, Error}</v>
+	<v>Reply = Error = term()</v>
+      </type>
+      <desc>
+	<p>
+	This function is used to wait for a reply of a request made with
+	<seealso marker="#send_request/3"><c>send_request/3</c></seealso>
+	from the event manager. This function must be called from the same
+	process from which <seealso marker="#send_request/3"><c>send_request/3</c></seealso>
+	was made.
+	</p>
+	<p>
+	  <c>Timeout</c> is an integer greater then or equal to zero
+	  that specifies how many milliseconds to wait for an reply, or
+	  the atom <c>infinity</c> to wait indefinitely.
+	  If no reply is received within the specified
+	  time, the function returns <c>timeout</c> and no cleanup is
+	  done, and thus the function must be invoked repeatedly until a
+	  reply is returned.
+	</p>
+	<p>
+	  The return value <c>Reply</c> is defined in the return value
+	  of <c>Module:handle_call/3</c>.
+	</p>
+	<p>
+	  If the specified event handler is not
+          installed, the function returns <c>{error,bad_module}</c>. If
+          the callback function fails with <c>Reason</c> or returns an
+          unexpected value <c>Term</c>, this function returns
+          <c>{error,{'EXIT',Reason}}</c> or <c>{error,Term}</c>,
+          respectively. If the event manager dies before or during the
+	  request this function returns <c>{error,{Reason, EventMgrRef}}</c>.
+	</p>
+      </desc>
+    </func>
+
     <func>
       <name since="">which_handlers(EventMgrRef) -> [Handler]</name>
       <fsummary>Return all event handlers installed in a generic event manager.
diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml
index 4ff911fc96..bc9fc703d1 100644
--- a/lib/stdlib/doc/src/gen_server.xml
+++ b/lib/stdlib/doc/src/gen_server.xml
@@ -53,6 +53,7 @@ gen_server:start_link -----> Module:init/1
 gen_server:stop       -----> Module:terminate/2
 
 gen_server:call
+gen_server:send_request
 gen_server:multi_call -----> Module:handle_call/3
 
 gen_server:cast
@@ -155,8 +156,8 @@ gen_server:abcast     -----> Module:handle_cast/2
           <item><c>{via,Module,ViaName}</c>, if the <c>gen_server</c> process is
             registered through an alternative process registry</item>
         </list>
-        <p><c>Request</c> is any term that is passed as one of
-          the arguments to <c>Module:handle_call/3</c>.</p>
+        <p><c>Request</c> is any term that is passed as the
+          first argument to <c>Module:handle_call/3</c>.</p>
         <p><c>Timeout</c> is an integer greater than zero that
           specifies how many milliseconds to wait for a reply, or
           the atom <c>infinity</c> to wait indefinitely. Defaults to
@@ -199,6 +200,43 @@ gen_server:abcast     -----> Module:handle_cast/2
       </desc>
     </func>
 
+    <func>
+      <name since="OTP-23">check_response(Msg, RequestId) -> Result</name>
+      <fsummary>Check if a message is a reply from a server.</fsummary>
+      <type>
+	<v>RequestId = term()</v>
+	<v>Result = {reply, Reply} | no_reply | {error, {Reason, ServerRef}}</v>
+	<v>Msg = Reply = term()</v>
+	<v>Timeout = timeout()</v>
+	<v>Reason = term()</v>
+	<v>ServerRef = Name | {Name,Node} | {global,GlobalName}</v>
+        <v>&nbsp;&nbsp;| {via,Module,ViaName} | pid()</v>
+        <v>&nbsp;Node = atom()</v>
+        <v>&nbsp;GlobalName = ViaName = term()</v>
+      </type>
+      <desc>
+	<p>
+	  This function is used to check if a previously received
+	  message, for example by <c>receive</c> or
+	  <c>handle_info/2</c>, is a result of a request made with
+	  <seealso marker="#send_request/2"><c>send_request/2</c></seealso>.
+	  If <c>Msg</c> is a reply to the handle <c>RequestId</c>
+	  the result of the request is returned in <c>Reply</c>.
+	  Otherwise returns <c>no_reply</c> and no cleanup is done, and
+	  thus the function must be invoked repeatedly until a reply
+	  is returned.
+	</p>
+	<p>
+	  The return value <c>Reply</c> is defined in the return value
+	  of <c>Module:handle_call/3</c>.
+	</p>
+	<p>
+	  The function returns an error if the <c>gen_server</c>
+	  dies before or during this request.
+	</p>
+      </desc>
+    </func>
+
     <func>
       <name since="">enter_loop(Module, Options, State)</name>
       <name since="">enter_loop(Module, Options, State, ServerName)</name>
@@ -280,8 +318,8 @@ gen_server:abcast     -----> Module:handle_cast/2
           <c>[node()|nodes()]</c>.</p>
         <p><c>Name</c> is the locally registered name of each
           <c>gen_server</c> process.</p>
-        <p><c>Request</c> is any term that is passed as one of
-          the arguments to <c>Module:handle_call/3</c>.</p>
+        <p><c>Request</c> is any term that is passed as the first
+          argument to <c>Module:handle_call/3</c>.</p>
         <p><c>Timeout</c> is an integer greater than zero that
           specifies how many milliseconds to wait for each reply, or
           the atom <c>infinity</c> to wait indefinitely. Defaults
@@ -328,6 +366,55 @@ gen_server:abcast     -----> Module:handle_cast/2
       </desc>
     </func>
 
+    <func>
+      <name since="OTP-23">send_request(ServerRef, Request) -> RequestId</name>
+      <fsummary>Sends a request to a generic server.</fsummary>
+      <type>
+        <v>ServerRef = Name | {Name,Node} | {global,GlobalName}</v>
+        <v>&nbsp;&nbsp;| {via,Module,ViaName} | pid()</v>
+        <v>&nbsp;Node = atom()</v>
+        <v>&nbsp;GlobalName = ViaName = term()</v>
+        <v>RequestId = term()</v>
+        <v>Timeout = int()>0 | infinity</v>
+        <v>Request = term()</v>
+      </type>
+      <desc>
+        <p>
+	  Sends a request to the <c>ServerRef</c> of the
+          <c>gen_server</c> process and returns a handle <c>RequestId</c>.
+	  The return value <c>RequestId</c> shall later be used with
+          <seealso marker="#wait_response/2"> <c>wait_response/2</c></seealso> or
+	  <seealso marker="#check_response/2"> <c>check_response/2</c></seealso>
+	  to fetch the actual result of the request.
+	</p>
+	<p>
+	  The call <c>gen_server:wait_response(gen_server:send_request(ServerRef,Request), Timeout)</c>
+	  can be seen as equivalent to
+	  <seealso marker="#call/3"><c>gen_server:call(Server,Request,Timeout)</c></seealso>,
+	  ignoring the error handling.
+	</p>
+	<p>
+	  The <c>gen_server</c> process calls
+	  <seealso marker="#Module:handle_call/3"> <c>Module:handle_call/3</c></seealso>
+	  to handle the request.
+	</p>
+        <p><c>ServerRef</c> can be any of the following:</p>
+        <list type="bulleted">
+          <item>The pid</item>
+          <item><c>Name</c>, if the <c>gen_server</c> process is locally
+            registered</item>
+          <item><c>{Name,Node}</c>, if the <c>gen_server</c> process is locally
+            registered at another node</item>
+          <item><c>{global,GlobalName}</c>, if the <c>gen_server</c> process is
+            globally registered</item>
+          <item><c>{via,Module,ViaName}</c>, if the <c>gen_server</c> process is
+            registered through an alternative process registry</item>
+        </list>
+        <p><c>Request</c> is any term that is passed as the first
+        argument to <c>Module:handle_call/3</c>.</p>
+      </desc>
+    </func>
+
     <func>
       <name since="">start(Module, Args, Options) -> Result</name>
       <name since="">start(ServerName, Module, Args, Options) -> Result</name>
@@ -495,6 +582,49 @@ gen_server:abcast     -----> Module:handle_cast/2
           is raised.</p>
       </desc>
     </func>
+
+    <func>
+      <name since="OTP-23">wait_response(RequestId, Timeout) -> Result</name>
+      <fsummary>Wait for a reply from a server.</fsummary>
+      <type>
+	<v>RequestId = term()</v>
+	<v>Result = {reply, Reply} | timeout | {error, {Reason, ServerRef}}</v>
+	<v>Reply = term()</v>
+	<v>Timeout = timeout()</v>
+	<v>Reason = term()</v>
+	<v>ServerRef = Name | {Name,Node} | {global,GlobalName}</v>
+        <v>&nbsp;&nbsp;| {via,Module,ViaName} | pid()</v>
+        <v>&nbsp;Node = atom()</v>
+        <v>&nbsp;GlobalName = ViaName = term()</v>
+      </type>
+      <desc>
+	<p>
+	This function is used to wait for a reply of a request made with
+	<seealso marker="#send_request/2"><c>send_request/2</c></seealso>
+	from the <c>gen_server</c> process. This function must be called
+	from the same process from which
+	<seealso marker="#send_request/2"><c>send_request/2</c></seealso>
+	was made.
+	</p>
+	<p>
+	  <c>Timeout</c> is an integer greater then or equal to zero
+	  that specifies how many milliseconds to wait for an reply, or
+	  the atom <c>infinity</c> to wait indefinitely.
+	  If no reply is received within the specified
+	  time, the function returns <c>timeout</c> and no cleanup is
+	  done, and thus the function can be invoked repeatedly until a
+	  reply is returned.
+	</p>
+	<p>
+	  The return value <c>Reply</c> is defined in the return value
+	  of <c>Module:handle_call/3</c>.
+	</p>
+	<p>
+	  The function returns an error if the <c>gen_server</c>
+	  dies before or during this request.
+	</p>
+      </desc>
+    </func>
   </funcs>
 
   <section>
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
index dabce02b3d..9b6187a832 100644
--- a/lib/stdlib/doc/src/gen_statem.xml
+++ b/lib/stdlib/doc/src/gen_statem.xml
@@ -174,6 +174,7 @@ gen_statem:stop       -----> Module:terminate/3
 
 gen_statem:call
 gen_statem:cast
+gen_statem:send_request
 erlang:send
 erlang:'!'            -----> Module:StateName/3
                              Module:handle_event/4
@@ -669,11 +670,12 @@ handle_event(_, _, State, Data) ->
 	<p>
 	  External events are of 3 types:
 	  <c>{call,<anno>From</anno>}</c>, <c>cast</c>, or <c>info</c>.
-	  <seealso marker="#call/2">Calls</seealso>
-	  (synchronous) and
-	  <seealso marker="#cast/2">casts</seealso>
-	  originate from the corresponding API functions.
+	  Type <c>call</c> originates from the API functions
+	  <seealso marker="#call/2"><c>call/2</c></seealso>
+	  and <seealso marker="#send_request/2"><c>send_request/2</c></seealso>.
 	  For calls, the event contains whom to reply to.
+	  Type <c>cast</c> originates from the API function
+	  <seealso marker="#cast/2"><c>cast/2</c></seealso>.
 	  Type <c>info</c> originates from regular process messages sent
 	  to the <c>gen_statem</c>.
 	</p>
@@ -1487,6 +1489,16 @@ handle_event(_, _, State, Data) ->
 	</p>
       </desc>
     </datatype>
+
+    <datatype>
+      <name name="request_id"/>
+      <desc>
+	<p>
+	  A request handle, see <seealso marker="#send_request/2"> <c>send_request/2</c> </seealso>
+	  for details.
+	</p>
+      </desc>
+    </datatype>
   </datatypes>
 
   <funcs>
@@ -1587,6 +1599,37 @@ handle_event(_, _, State, Data) ->
       </desc>
     </func>
 
+    <func>
+      <name name="check_response" arity="2" since="OTP-23"/>
+      <fsummary>Check if a message is a reply from a server.</fsummary>
+      <desc>
+	<p>
+	  This function is used to check if a previously received
+	  message, for example by <c>receive</c> or
+	  <c>handle_info/2</c>, is a result of a request made with
+	  <seealso marker="#send_request/2"><c>send_request/2</c></seealso>.
+	  If <c>Msg</c> is a reply to the handle <c>RequestId</c>
+	  the result of the request is returned in <c>Reply</c>.
+	  Otherwise returns <c>no_reply</c> and no cleanup is done, and
+	  thus the function shall be invoked repeatedly until a reply
+	  is returned.
+	</p>
+	<p>
+	  The return value <c><anno>Reply</anno></c> is generated when a
+	  <seealso marker="#state callback"><em>state callback</em></seealso>
+	  returns with
+	  <c>{reply,From,<anno>Reply</anno>}</c> as one
+	  <seealso marker="#type-action"><c>action()</c></seealso>,
+	  and that <c><anno>Reply</anno></c> becomes the return value
+	  of this function.
+	</p>
+	<p>
+	  The function returns an error if the <c>gen_statem</c>
+	  dies before or during this request.
+	</p>
+      </desc>
+    </func>
+
     <func>
       <name name="enter_loop" arity="4" since="OTP 19.1"/>
       <fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary>
@@ -1715,6 +1758,48 @@ handle_event(_, _, State, Data) ->
       </desc>
     </func>
 
+    <func>
+      <name name="send_request" arity="2" since="OTP-23"/>
+      <fsummary>Send a request to a <c>gen_statem</c>.</fsummary>
+      <desc>
+        <p>
+	  Sends a request to the <c>gen_statem</c>
+	  <seealso marker="#type-server_ref"><c><anno>ServerRef</anno></c></seealso>
+	  and returns a handle <c><anno>RequestId</anno></c>.
+	</p>
+	<p>
+	  The return value <c><anno>RequestId</anno></c> shall later be used with
+          <seealso marker="#wait_response/2"> <c>wait_response/1,2</c></seealso> or
+	  <seealso marker="#check_response/2"> <c>check_response/2</c></seealso>
+	  to fetch the actual result of the request.
+	</p>
+	<p>
+	  The call <c>gen_statem:wait_response(gen_statem:send_request(ServerRef,Request), Timeout)</c>
+	  can be seen as equivalent to
+	  <seealso marker="#call/3"><c>gen_statem:call(Server,Request,Timeout)</c></seealso>,
+	  ignoring the error handling.
+	</p>
+	<p>
+	  The <c>gen_statem</c> calls the
+	  <seealso marker="#state callback"><em>state callback</em></seealso>
+	  with
+	  <seealso marker="#type-event_type"><c>event_type()</c></seealso>
+	  <c>{call,From}</c> and event content
+	  <c><anno>Request</anno></c>.
+	</p>
+	<p>
+	  A <c>Reply</c> is generated when a
+	  <seealso marker="#state callback"><em>state callback</em></seealso>
+	  returns with
+	  <c>{reply,From,Reply}</c> as one
+	  <seealso marker="#type-action"><c>action()</c></seealso>,
+	  and that <c>Reply</c> becomes the return value
+	  of <seealso marker="#wait_response/2"> <c>wait_response/1,2</c></seealso> or
+	  <seealso marker="#check_response/2"> <c>check_response/2</c></seealso> function.
+	</p>
+      </desc>
+    </func>
+
     <func>
       <name name="start" arity="3" since="OTP 19.0"/>
       <name name="start" arity="4" since="OTP 19.0"/>
@@ -1904,6 +1989,45 @@ handle_event(_, _, State, Data) ->
 	</p>
       </desc>
     </func>
+
+    <func>
+      <name name="wait_response" arity="1" since="OTP-23"/>
+      <name name="wait_response" arity="2" since="OTP-23"/>
+      <fsummary>Wait for a reply from a server.</fsummary>
+      <desc>
+	<p>
+	  This function is used to wait for a reply of a request made with
+	  <seealso marker="#send_request/2"><c>send_request/2</c></seealso>
+	  from the <c>gen_statem</c> process. This function must be called
+	  from the same process from which
+	  <seealso marker="#send_request/2"><c>send_request/2</c></seealso>
+	  was made.
+	</p>
+	<p>
+	  <c>Timeout</c> is an integer greater then or equal to zero
+	  that specifies how many milliseconds to wait for an reply, or
+	  the atom <c>infinity</c> to wait indefinitely. Defaults to
+	  <c>infinity</c>.
+	  If no reply is received within the specified
+	  time, the function returns <c>timeout</c> and no cleanup is
+	  done, and thus the function can be invoked repeatedly until a
+	  reply is returned.
+	</p>
+	<p>
+	  The return value <c><anno>Reply</anno></c> is generated when a
+	  <seealso marker="#state callback"><em>state callback</em></seealso>
+	  returns with
+	  <c>{reply,From,<anno>Reply</anno>}</c> as one
+	  <seealso marker="#type-action"><c>action()</c></seealso>,
+	  and that <c><anno>Reply</anno></c> becomes the return value
+	  of this function.
+	</p>
+	<p>
+	  The function returns an error if the <c>gen_statem</c>
+	  dies before or during this function call.
+	</p>
+      </desc>
+    </func>
   </funcs>
 
   <section>
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index a7f743bd4c..34b87e9025 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,
+         send_request/3, wait_response/2, check_response/2,
+         stop/1, stop/3]).
 
 -export([init_it/6, init_it/7]).
 
@@ -53,6 +55,11 @@
 		    | {'spawn_opt', [proc_lib:spawn_option()]}.
 -type options()    :: [option()].
 
+-type server_ref() :: pid() | atom() | {atom(), node()}
+                    | {global, term()} | {via, module(), term()}.
+
+-type request_id() :: term().
+
 %%-----------------------------------------------------------------
 %% Starts a generic process.
 %% start(GenMod, LinkP, Mod, Args, Options)
@@ -139,7 +146,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
@@ -192,6 +199,56 @@ get_node(Process) ->
 	    node(Process)
     end.
 
+-spec send_request(Name::server_ref(), Label::term(), Request::term()) -> request_id().
+send_request(Process, Label, Request) when is_pid(Process) ->
+    do_send_request(Process, Label, Request);
+send_request(Process, Label, Request) ->
+    Fun = fun(Pid) -> do_send_request(Pid, Label, Request) end,
+    try do_for_proc(Process, Fun)
+    catch exit:Reason ->
+            %% Make send_request async and fake a down message
+            Mref = erlang:make_ref(),
+            self() ! {'DOWN', Mref, process, Process, Reason},
+            Mref
+    end.
+
+do_send_request(Process, Label, Request) ->
+    Mref = erlang:monitor(process, Process),
+    erlang:send(Process, {Label, {self(), Mref}, Request}, [noconnect]),
+    Mref.
+
+%%
+%% Wait for a reply to the client.
+%% Note: if timeout is returned monitors are kept.
+
+-spec wait_response(RequestId::request_id(), timeout()) ->
+        {reply, Reply::term()} | 'timeout' | {error, {term(), server_ref()}}.
+wait_response(Mref, Timeout)
+  when is_reference(Mref) ->
+    receive
+        {Mref, Reply} ->
+            erlang:demonitor(Mref, [flush]),
+            {reply, Reply};
+        {'DOWN', Mref, _, Object, Reason} ->
+            {error, {Reason, Object}}
+    after Timeout ->
+            timeout
+    end.
+
+-spec check_response(RequestId::term(), Key::request_id()) ->
+        {reply, Reply::term()} | 'no_reply' | {error, {term(), server_ref()}}.
+check_response(Msg, Mref)
+  when is_reference(Mref) ->
+    case Msg of
+        {Mref, Reply} ->
+            erlang:demonitor(Mref, [flush]),
+            {reply, Reply};
+        {'DOWN', Mref, _, Object, Reason} ->
+            {error, {Reason, Object}};
+        _ ->
+            no_reply
+    end.
+
 %%
 %% Send a reply to the client.
 %%
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index 8213282867..8c1ef35e4d 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -37,7 +37,9 @@
          stop/1, stop/3,
 	 notify/2, sync_notify/2,
 	 add_handler/3, add_sup_handler/3, delete_handler/3, swap_handler/3,
-	 swap_sup_handler/3, which_handlers/1, call/3, call/4, wake_hib/5]).
+	 swap_sup_handler/3, which_handlers/1, call/3, call/4,
+         send_request/3, wait_response/2, check_response/2,
+         wake_hib/5]).
 
 -export([init_it/6,
 	 system_continue/3,
@@ -133,6 +135,7 @@
 -type emgr_ref()  :: atom() | {atom(), atom()} |  {'global', term()}
                    | {'via', atom(), term()} | pid().
 -type start_ret() :: {'ok', pid()} | {'error', term()}.
+-type request_id() :: term().
 
 %%---------------------------------------------------------------------------
 
@@ -213,6 +216,26 @@ call(M, Handler, Query) -> call1(M, Handler, Query).
 -spec call(emgr_ref(), handler(), term(), timeout()) -> term().
 call(M, Handler, Query, Timeout) -> call1(M, Handler, Query, Timeout).
 
+-spec send_request(emgr_ref(), handler(), term()) -> request_id().
+send_request(M, Handler, Query) ->
+    gen:send_request(M, self(), {call, Handler, Query}).
+
+-spec wait_response(RequestId::request_id(), timeout()) ->
+        {reply, Reply::term()} | 'timeout' | {error, {Reason::term(), emgr_ref()}}.
+wait_response(RequestId, Timeout) ->
+    case gen:wait_response(RequestId, Timeout) of
+        {reply, {error, _} = Err} -> Err;
+        Return -> Return
+    end.
+
+-spec check_response(Msg::term(), RequestId::request_id()) ->
+        {reply, Reply::term()} | 'no_reply' | {error, {Reason::term(), emgr_ref()}}.
+check_response(Msg, RequestId) ->
+    case gen:check_response(Msg, RequestId)  of
+        {reply, {error, _} = Err} -> Err;
+        Return -> Return
+    end.
+
 -spec delete_handler(emgr_ref(), handler(), term()) -> term().
 delete_handler(M, Handler, Args) -> rpc(M, {delete_handler, Handler, Args}).
 
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index 974aeb6032..3bde9c92bf 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -91,6 +91,7 @@
 	 start_link/3, start_link/4,
 	 stop/1, stop/3,
 	 call/2, call/3,
+         send_request/2, wait_response/2, check_response/2,
 	 cast/2, reply/2,
 	 abcast/2, abcast/3,
 	 multi_call/2, multi_call/3, multi_call/4,
@@ -116,6 +117,16 @@
    STACKTRACE(),
    element(2, erlang:process_info(self(), current_stacktrace))).
 
+
+-type server_ref() ::
+        pid()
+      | (LocalName :: atom())
+      | {Name :: atom(), Node :: atom()}
+      | {'global', GlobalName :: term()}
+      | {'via', RegMod :: module(), ViaName :: term()}.
+
+-type request_id() :: term().
+
 %%%=========================================================================
 %%%  API
 %%%=========================================================================
@@ -223,6 +234,25 @@ call(Name, Request, Timeout) ->
 	    exit({Reason, {?MODULE, call, [Name, Request, Timeout]}})
     end.
 
+%% -----------------------------------------------------------------
+%% Send a request to a generic server and return a Key which should be
+%% used with wait_response/2 or check_response/2 to fetch the
+%% result of the request.
+
+-spec send_request(Name::server_ref(), Request::term()) -> request_id().
+send_request(Name, Request) ->
+    gen:send_request(Name, '$gen_call', Request).
+
+-spec wait_response(RequestId::request_id(), timeout()) ->
+        {reply, Reply::term()} | 'timeout' | {error, {Reason::term(), server_ref()}}.
+wait_response(RequestId, Timeout) ->
+    gen:wait_response(RequestId, Timeout).
+
+-spec check_response(Msg::term(), RequestId::request_id()) ->
+        {reply, Reply::term()} | 'no_reply' | {error, {Reason::term(), server_ref()}}.
+check_response(Msg, RequestId) ->
+    gen:check_response(Msg, RequestId).
+
 %% -----------------------------------------------------------------
 %% Make a cast to a generic server.
 %% -----------------------------------------------------------------
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index 105b2a4577..02a24e50f1 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -26,6 +26,7 @@
    [start/3,start/4,start_link/3,start_link/4,
     stop/1,stop/3,
     cast/2,call/2,call/3,
+    send_request/2,wait_response/1,wait_response/2,check_response/2,
     enter_loop/4,enter_loop/5,enter_loop/6,
     reply/1,reply/2]).
 
@@ -58,7 +59,8 @@
     event_handler_result/1,
     reply_action/0,
     enter_action/0,
-    action/0]).
+    action/0
+   ]).
 %% Old types, not advertised
 -export_type(
    [state_function_result/0,
@@ -256,6 +258,7 @@
 	 Replies :: [reply_action()] | reply_action(),
 	 NewData :: data()}.
 
+-type request_id() :: term().
 
 %% The state machine init function.  It is called only once and
 %% the server is not running until this function has returned
@@ -549,6 +552,26 @@ call(ServerRef, Request, {_, _} = Timeout) ->
 call(ServerRef, Request, Timeout) ->
     call_clean(ServerRef, Request, Timeout, Timeout).
 
+-spec send_request(ServerRef::server_ref(), Request::term()) ->
+        RequestId::request_id().
+send_request(Name, Request) ->
+    gen:send_request(Name, '$gen_call', Request).
+
+-spec wait_response(RequestId::request_id()) ->
+        {reply, Reply::term()} | {error, {term(), server_ref()}}.
+wait_response(RequestId) ->
+    gen:wait_response(RequestId, infinity).
+
+-spec wait_response(RequestId::request_id(), timeout()) ->
+        {reply, Reply::term()} | 'timeout' | {error, {term(), server_ref()}}.
+wait_response(RequestId, Timeout) ->
+    gen:wait_response(RequestId, Timeout).
+
+-spec check_response(Msg::term(), RequestId::request_id()) ->
+        {reply, Reply::term()} | 'no_reply' | {error, {term(), server_ref()}}.
+check_response(Msg, RequestId) ->
+    gen:check_response(Msg, RequestId).
+
 %% Reply from a state machine callback to whom awaits in call/2
 -spec reply([reply_action()] | reply_action()) -> ok.
 reply({reply,From,Reply}) ->
diff --git a/lib/stdlib/test/gen_event_SUITE.erl b/lib/stdlib/test/gen_event_SUITE.erl
index 880b10117c..624ec2c562 100644
--- a/lib/stdlib/test/gen_event_SUITE.erl
+++ b/lib/stdlib/test/gen_event_SUITE.erl
@@ -763,27 +763,49 @@ sync_notify(Config) when is_list(Config) ->
     ok.
 
 call(Config) when is_list(Config) ->
+    Async = fun(Mgr,H,Req) ->
+                    try
+                        Promise = gen_event:send_request(Mgr,H,Req),
+                        gen_event:wait_response(Promise, infinity)
+                    catch _:Reason ->
+                            {'did_exit', Reason}
+                    end
+            end,
     {ok,_} = gen_event:start({local, my_dummy_handler}),
     ok = gen_event:add_handler(my_dummy_handler, dummy_h, [self()]),
     ok = gen_event:add_handler(my_dummy_handler, {dummy_h, 1}, [self()]),
     [{dummy_h, 1}, dummy_h] = gen_event:which_handlers(my_dummy_handler),
     {'EXIT',_} = (catch gen_event:call(non_exist, dummy_h, hejsan)),
-    {error, bad_module} =
-	gen_event:call(my_dummy_handler, bad_h, hejsan),
+    {error, _} = Async(non_exist, dummy_h, hejsan),
+    {error, bad_module} = gen_event:call(my_dummy_handler, bad_h, hejsan),
+    {error, bad_module} = Async(my_dummy_handler, bad_h, hejsan),
+
     {ok, hejhopp} = gen_event:call(my_dummy_handler, dummy_h, hejsan),
-    {ok, hejhopp} = gen_event:call(my_dummy_handler, {dummy_h, 1},
-				   hejsan),
-    {ok, hejhopp} = gen_event:call(my_dummy_handler, dummy_h, hejsan,
-				   10000),
+    {reply, {ok, hejhopp}} = Async(my_dummy_handler, dummy_h, hejsan),
+    {ok, hejhopp} = gen_event:call(my_dummy_handler, {dummy_h, 1}, hejsan),
+    {reply, {ok, hejhopp}} = Async(my_dummy_handler, {dummy_h, 1}, hejsan),
+    {ok, hejhopp} = gen_event:call(my_dummy_handler, {dummy_h, 1}, hejsan),
+    {reply, {ok, hejhopp}} = Async(my_dummy_handler, {dummy_h, 1}, hejsan),
+    {ok, hejhopp} = gen_event:call(my_dummy_handler, dummy_h, hejsan, 10000),
     {'EXIT', {timeout, _}} =
 	(catch gen_event:call(my_dummy_handler, dummy_h, hejsan, 0)),
     flush(),
+    P1 = gen_event:send_request(my_dummy_handler, dummy_h, hejsan),
+    timeout = gen_event:wait_response(P1, 0),
+    {reply, {ok, hejhopp}} = gen_event:wait_response(P1, infinity),
+
+    flush(),
+    P2 = gen_event:send_request(my_dummy_handler, dummy_h, hejsan),
+    no_reply = gen_event:check_response({other,msg}, P2),
+    {reply, {ok, hejhopp}} = receive Msg -> gen_event:check_response(Msg, P2)
+                             after 1000 -> exit(tmo) end,
+
     ok = gen_event:delete_handler(my_dummy_handler, {dummy_h, 1}, []),
     {ok, swapped} = gen_event:call(my_dummy_handler, dummy_h,
 				   {swap_call,dummy1_h,swap}),
     [dummy1_h] = gen_event:which_handlers(my_dummy_handler),
-    {error, bad_module} =
-	gen_event:call(my_dummy_handler, dummy_h, hejsan),
+    {error, bad_module} = gen_event:call(my_dummy_handler, dummy_h, hejsan),
+    {error, bad_module} = Async(my_dummy_handler, dummy_h, hejsan),
     ok = gen_event:call(my_dummy_handler, dummy1_h, delete_call),
     receive
 	{dummy1_h, removed} ->
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index e29195e895..788fa5b93c 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -26,7 +26,7 @@
 
 -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
 	 init_per_group/2,end_per_group/2]).
--export([start/1, crash/1, call/1, cast/1, cast_fast/1,
+-export([start/1, crash/1, call/1, send_request/1, cast/1, cast_fast/1,
 	 continue/1, info/1, abcast/1, multicall/1, multicall_down/1,
 	 call_remote1/1, call_remote2/1, call_remote3/1,
 	 call_remote_n1/1, call_remote_n2/1, call_remote_n3/1, spec_init/1,
@@ -61,7 +61,7 @@ suite() ->
      {timetrap,{minutes,1}}].
 
 all() -> 
-    [start, {group,stop}, crash, call, cast, cast_fast, info, abcast,
+    [start, {group,stop}, crash, call, send_request, cast, cast_fast, info, abcast,
      continue, multicall, multicall_down, call_remote1, call_remote2,
      call_remote3, call_remote_n1, call_remote_n2,
      call_remote_n3, spec_init,
@@ -104,7 +104,8 @@ init_per_testcase(Case, Config) when Case == call_remote1;
 				     Case == call_remote3;
 				     Case == call_remote_n1;
 				     Case == call_remote_n2;
-				     Case == call_remote_n3 ->
+				     Case == call_remote_n3;
+                                     Case == send_request ->
     {ok,N} = start_node(hubba),
     [{node,N} | Config];
 
@@ -458,6 +459,87 @@ call(Config) when is_list(Config) ->
     process_flag(trap_exit, OldFl),
     ok.
 
+%% --------------------------------------
+%% Test gen_server:send_request.
+%% --------------------------------------
+
+send_request(Config) when is_list(Config) ->
+    OldFl = process_flag(trap_exit, true),
+
+    {ok, Pid} = gen_server:start_link({local, my_test_name},
+                                      gen_server_SUITE, [], []),
+
+    Async = fun(Process, Req) ->
+                    try
+                        Promise = gen_server:send_request(Process, Req),
+                        gen_server:wait_response(Promise, infinity)
+                    catch _:Reason:ST ->
+                            {'did_exit', Reason, ST}
+                    end
+            end,
+    {reply,ok} = Async(my_test_name, started_p),
+
+    {reply,delayed} = Async(Pid, {delayed_answer,1}),
+
+    %% two requests within a specified time.
+    Promise1 = gen_server:send_request(my_test_name, {call_within, 1000}),
+    Promise2 = gen_server:send_request(my_test_name, next_call),
+    {reply, ok} = gen_server:wait_response(Promise1, infinity),
+    {reply, ok} = gen_server:wait_response(Promise2, infinity),
+
+    Promise3 = gen_server:send_request(my_test_name, {call_within, 1000}),
+    no_reply = gen_server:check_response({foo, bar}, Promise3),
+    receive {Ref,_} = Msg when is_reference(Ref) ->
+            {reply, ok} = gen_server:check_response(Msg, Promise3)
+    after 1000 -> exit(api_changed)
+    end,
+    timer:sleep(1500),
+
+    {reply, false} = Async(my_test_name, next_call),
+
+    %% timeout
+    Promise5 = gen_server:send_request(my_test_name, {delayed_answer,50}),
+    timeout = gen_server:wait_response(Promise5, 0),
+    {reply, delayed} = gen_server:wait_response(Promise5, infinity),
+
+    %% bad return value in the gen_server loop from handle_call.
+    {error,{{bad_return_value, badreturn},_}} = Async(my_test_name, badreturn),
+
+    %% Test other error cases
+    {error, {noproc,_}} = Async(Pid, started_p),
+    {error, {noproc,_}} = Async(my_test_name, started_p),
+    {error, {noconnection, _}} = Async({my_test_name, foo@node}, started_p),
+
+    {error, {noproc,_}} = Async({global, non_existing}, started_p),
+    catch exit(whereis(dummy_via), foo),
+    {'EXIT', {badarg,_}} =
+        (catch gen_server:send_request({via, dummy_via, non_existing}, started_p)),
+
+    %% Remote nodes
+    Via = dummy_via:reset(),
+    Remote = proplists:get_value(node,Config),
+    {ok, RPid} = rpc:call(Remote, gen_server, start, [{global, remote}, ?MODULE, [], []]),
+    dummy_via:register_name(remote, RPid),
+    {reply, ok} = Async(RPid, started_p),
+    {reply, ok} = Async({global, remote}, started_p),
+    {reply, ok} = Async({via, dummy_via, remote}, started_p),
+    {error, {shutdown, _}} = Async({global, remote}, stop_shutdown),
+    {error, {noproc, _}} = Async({global, remote}, started_p),
+    {error, {noproc, _}} = Async({via, dummy_via, remote}, started_p),
+    {error, {noproc, _}} = Async({via, dummy_via, non_existing}, started_p),
+
+    {ok, _} = rpc:call(Remote, gen_server, start, [{local, remote}, ?MODULE, [], []]),
+    {reply, ok} = Async({remote, Remote}, started_p),
+    {error, {shutdown, _}} = Async({remote, Remote}, stop_shutdown),
+    {error, {noproc, _}} = Async({remote, Remote}, started_p),
+
+    %% Cleanup
+    catch exit(Via, foo2),
+    receive {'EXIT', Via, foo2} -> ok end,
+    process_flag(trap_exit, OldFl),
+    ok.
+
+
 %% --------------------------------------
 %% Test handle_continue.
 %% --------------------------------------
diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl
index aa4d258cbf..5519fac52f 100644
--- a/lib/stdlib/test/gen_statem_SUITE.erl
+++ b/lib/stdlib/test/gen_statem_SUITE.erl
@@ -1815,7 +1815,17 @@ do_func_test(STM) ->
     wfor(yes),
     ok = do_disconnect(STM),
     ok = gen_statem:cast(STM, {'alive?',self()}),
+    P0 = gen_statem:send_request(STM, 'alive?'),
+    timeout = gen_statem:wait_response(P0, 0),
     wfor(yes),
+    {reply, yes} = gen_statem:wait_response(P0, infinity),
+    _ = flush(),
+    P1 = gen_statem:send_request(STM, 'alive?'),
+    receive Msg ->
+            no_reply = gen_statem:check_response(Msg, P0),
+            {reply, yes} = gen_statem:check_response(Msg, P1)
+    after 1000 -> exit(timeout)
+    end,
     ok.
 
 
diff --git a/lib/wx/src/wx_object.erl b/lib/wx/src/wx_object.erl
index 181206c762..81d188b26a 100644
--- a/lib/wx/src/wx_object.erl
+++ b/lib/wx/src/wx_object.erl
@@ -117,12 +117,16 @@
 	 start_link/3, start_link/4,
 	 stop/1, stop/3,
 	 call/2, call/3,
+         send_request/2, wait_response/1, wait_response/2, check_response/2,
 	 cast/2,
 	 reply/2,
 	 get_pid/1,
 	 set_pid/2
 	]).
 
+-type request_id() :: term().
+-type server_ref() :: Obj::wx:wx_object()|atom()|pid().
+
 %% -export([behaviour_info/1]).
 -callback init(Args :: term()) ->
     {#wx_ref{}, State :: term()} | {#wx_ref{}, State :: term(), timeout() | 'hibernate'} |
@@ -317,6 +321,34 @@ call(Name, Request, Timeout) when is_atom(Name) orelse is_pid(Name) ->
             erlang:error({Reason, {?MODULE, call, [Name, Request, Timeout]}})
     end.
 
+%% @doc Make an send_request to a generic server.
+%% and return a RequestId which can/should be used with wait_response/[1|2].
+%% Invokes handle_call(Request, From, State) in server.
+-spec send_request(Obj, Request::term()) -> request_id() when
+      Obj::wx:wx_object()|atom()|pid().
+send_request(#wx_ref{state=Pid}, Request) ->
+    gen:send_request(Pid, '$gen_call', Request);
+send_request(Pid, Request) when is_atom(Pid) orelse is_pid(Pid) ->
+    gen:send_request(Pid, '$gen_call', Request).
+
+%% @doc Wait infinitely for a reply from a generic server.
+-spec wait_response(RequestId::request_id()) ->
+        {reply, Reply::term()} | {error, {term(), server_ref()}}.
+wait_response(RequestId) ->
+    gen:wait_response(RequestId, infinity).
+
+%% @doc Wait 'timeout' for a reply from a generic server.
+-spec wait_response(Key::request_id(), timeout()) ->
+        {reply, Reply::term()} | 'timeout' | {error, {term(), server_ref()}}.
+wait_response(RequestId, Timeout) ->
+    gen:wait_response(RequestId, Timeout).
+
+%% @doc Check if a received message was a reply to a RequestId
+-spec check_response(Msg::term(), Key::request_id()) ->
+        {reply, Reply::term()} | 'false' | {error, {term(), server_ref()}}.
+check_response(Msg, RequestId) ->
+    gen:check_response(Msg, RequestId).
+
 %% @doc Make a cast to a wx_object server.
 %% Invokes handle_cast(Request, State) in the server
 -spec cast(Obj, Request) -> ok when
diff --git a/lib/wx/test/wx_basic_SUITE.erl b/lib/wx/test/wx_basic_SUITE.erl
index ad03a378de..16b531be6c 100644
--- a/lib/wx/test/wx_basic_SUITE.erl
+++ b/lib/wx/test/wx_basic_SUITE.erl
@@ -394,10 +394,19 @@ wx_object(Config) ->
     {call, {Frame,Panel}, _} = wx_object:call(Frame, fun(US) -> US end),
     ?m(false, wxWindow:getParent(Panel) =:= Frame),
     ?m(true, wx:equal(wxWindow:getParent(Panel),Frame)),
+    flush(),
+    ReqId = wx_object:send_request(Frame, fun(_US) -> timer:sleep(10), yes end),
+    timeout = wx_object:wait_response(ReqId, 0),
+    {reply, {call, yes, {Me,ReqId}}} = wx_object:wait_response(ReqId, 1000),
+    ReqId2 = wx_object:send_request(Frame, yes),
+    [Msg] = flush(),
+    no_reply = wx_object:check_response(Msg, ReqId),
+    {reply, {call, yes, {Me,ReqId2}}} = wx_object:check_response(Msg, ReqId2),
+
     FramePid = wx_object:get_pid(Frame),
     io:format("wx_object pid ~p~n",[FramePid]),
     FramePid ! foo3,
-    ?m([{info, foo3}|_], flush()),
+    ?m([{info, foo3}], flush()),
 
     ?m(ok, wx_object:cast(Frame, fun(_) -> hehe end)),
     ?m([{cast, hehe}|_], flush()),
-- 
2.16.4

openSUSE Build Service is sponsored by