File 2891-Improve-response-handling-for-asynchronous-gen-reque.patch of Package erlang

From 4975e1ecccb64c480857dbd498f16279a8138321 Mon Sep 17 00:00:00 2001
From: Rickard Green <rickard@erlang.org>
Date: Tue, 23 Nov 2021 22:05:04 +0100
Subject: [PATCH 1/2] Improve response handling for asynchronous 'gen' requests

receive_response/3, wait_response/3, and check_response/3 in 'gen_server',
'gen_statem', and 'gen_event' can now take a collection of request
identifiers as argument and handle any responses corresponding a request
identifier in the collection.
---
 lib/stdlib/doc/src/gen_event.xml     | 559 ++++++++++++++++++++++-----
 lib/stdlib/doc/src/gen_server.xml    | 534 +++++++++++++++++++++----
 lib/stdlib/doc/src/gen_statem.xml    | 488 +++++++++++++++++++++--
 lib/stdlib/src/gen.erl               | 251 ++++++++++--
 lib/stdlib/src/gen_event.erl         | 193 ++++++++-
 lib/stdlib/src/gen_server.erl        | 218 +++++++++--
 lib/stdlib/src/gen_statem.erl        | 225 +++++++++--
 lib/stdlib/test/dummy_h.erl          |   3 +
 lib/stdlib/test/gen_event_SUITE.erl  | 329 +++++++++++++++-
 lib/stdlib/test/gen_server_SUITE.erl | 327 +++++++++++++++-
 lib/stdlib/test/gen_statem_SUITE.erl | 309 ++++++++++++++-
 11 files changed, 3103 insertions(+), 333 deletions(-)

diff --git a/lib/stdlib/doc/src/gen_event.xml b/lib/stdlib/doc/src/gen_event.xml
index bb891fce56..676d643072 100644
--- a/lib/stdlib/doc/src/gen_event.xml
+++ b/lib/stdlib/doc/src/gen_event.xml
@@ -119,24 +119,89 @@ gen_event:stop             ----->  Module:terminate/2
     <datatype>
       <name name="handler"/>
     </datatype>
+
     <datatype>
       <name name="handler_args"/>
     </datatype>
+
     <datatype>
       <name name="add_handler_ret"/>
     </datatype>
+
     <datatype>
       <name name="del_handler_ret"/>
     </datatype>
+
+    <datatype>
+      <name name="emgr_ref"/>
+    </datatype>    
+    
     <datatype>
       <name name="request_id"/>
       <desc>
 	<p>
-	  A request handle, see <seemfa marker="#send_request/3"> <c>send_request/3</c> </seemfa>
+	  An opaque request identifier. See
+          <seemfa marker="#send_request/3"><c>send_request/3</c></seemfa>
 	  for details.
 	</p>
       </desc>
     </datatype>
+
+    <datatype>
+      <name name="request_id_collection"/>
+      <desc>
+        <p>
+	  An opaque collection of request identifiers
+          (<seetype marker="#request_id"><c>request_id()</c></seetype>)
+	  where each request identifier can be associated with a label
+          chosen by the user. For more information see
+          <seemfa marker="#reqids_new/0"><c>reqids_new/0</c></seemfa>.
+	</p>
+      </desc>
+    </datatype>
+
+    <datatype>
+      <name name="response_timeout"/>
+      <desc>
+        <p>
+          Used to set a time limit on how long to wait for a response using
+          either 
+          <seemfa marker="#receive_response/2"><c>receive_response/2</c></seemfa>,
+          <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>,
+          <seemfa marker="#wait_response/2"><c>wait_response/2</c></seemfa>,
+          or
+          <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>.
+          The time unit used is <c>millisecond</c>. Currently valid values:
+        </p>
+        <taglist>
+          <tag><c>0..4294967295</c></tag>
+          <item><p>
+            Timeout relative to current time in milliseconds.
+          </p></item>
+          <tag><c>infinity</c></tag>
+          <item><p>
+            Infinite timeout. That is, the operation will never time out.
+          </p></item>
+          <tag><c>{abs, Timeout}</c></tag>
+          <item><p>
+            An absolute
+            <seemfa marker="erts:erlang#monotonic_time/1">Erlang monotonic time</seemfa>
+            timeout in milliseconds. That is, the operation will time out when
+            <seemfa marker="erts:erlang#monotonic_time/1"><c>erlang:monotonic_time(millisecond)</c></seemfa>
+            returns a value larger than or equal to <c>Timeout</c>. <c>Timeout</c>
+            is not allowed to identify a time further into the future than <c>4294967295</c>
+            milliseconds. Identifying the timeout using an absolute timeout value
+            is especially handy when you have a deadline for responses corresponding
+            to a complete collection of requests
+            (<seetype marker="#request_id_collection"><c>request_id_collection()</c></seetype>)
+,
+            since you do not have to recalculate the relative time until the deadline
+            over and over again.
+	  </p></item>
+        </taglist>
+      </desc>
+    </datatype>
+
   </datatypes>
 
   <funcs>
@@ -312,28 +377,24 @@ gen_event:stop             ----->  Module:terminate/2
       </desc>
     </func>
 
-
     <func>
-      <name since="OTP 23.0">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>
+      <name name="check_response" arity="2" since="OTP 23.0"/>
+      <fsummary>Check if a message is a response to an asynchronous call request
+      to a generic event manager.</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
+	  Check if <c><anno>Msg</anno></c> is a response corresponding
+          to the request identifier <c><anno>ReqId</anno></c>. The request
+          must have been made by
 	  <seemfa marker="#send_request/3"><c>send_request/3</c></seemfa>.
-	  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 <c><anno>Msg</anno></c> is a response corresponding to
+          <c><anno>ReqId</anno></c> the response is returned; otherwise,
+          <c>no_reply</c> is returned and no cleanup is done, and
+	  thus the function must be invoked repeatedly until a response
+	  is returned.
+        </p>
 	<p>
 	  If the specified event handler is not
           installed, the function returns <c>{error,bad_module}</c>. If
@@ -346,6 +407,74 @@ gen_event:stop             ----->  Module:terminate/2
       </desc>
     </func>
 
+    <func>
+      <name name="check_response" arity="3" since="OTP 25.0"/>
+      <fsummary>Check if a message is a response to an asynchronous call request
+      to a generic event manager.</fsummary>
+      <desc>
+	<p>
+	  Check if <c><anno>Msg</anno></c> is a response corresponding
+          to a request identifier saved in <c><anno>ReqIdCollection</anno></c>.
+          All request identifiers of <c><anno>ReqIdCollection</anno></c>
+          must correspond to requests that have been made using
+	  <seemfa marker="#send_request/3"><c>send_request/3</c></seemfa> or
+          <seemfa marker="#send_request/5"><c>send_request/5</c></seemfa>,
+          and all request must have been made by the process calling this
+          function.
+	</p>
+        <p>
+          The <c><anno>Label</anno></c> in the response equals the
+          <c><anno>Label</anno></c> associated with the request identifier
+          that the response corresponds to. The <c><anno>Label</anno></c>
+          of a request identifier is associated when
+          <seemfa marker="#reqids_add/3">saving the request id</seemfa>
+          in a request identifier collection, or when sending the request using
+          <seemfa marker="#send_request/5"><c>send_request/5</c></seemfa>.
+        </p>
+        <p>
+          Compared to
+          <seemfa marker="#check_response/2"><c>check_response/2</c></seemfa>,
+          the returned result associated with a specific request identifier
+          or an exception associated with a specific request identifier will
+          be wrapped in a 3-tuple. The first element of this tuple equals the
+          value that would have been produced by <c>check_response/2</c>,
+          the second element equals the <c><anno>Label</anno></c> associated
+          with the specific request identifier, and the third element
+          <c><anno>NewReqIdCollection</anno></c> is a possibly  modified
+          request identifier collection.
+        </p>
+        <p>
+          If <c><anno>ReqIdCollection</anno></c> is empty, the atom
+          <c>no_request</c> will be returned. If <c><anno>Msg</anno></c>
+          does not correspond to any of the request identifiers in
+          <c><anno>ReqIdCollection</anno></c>, the atom
+          <c>no_reply</c> is returned.
+        </p>
+        <p>
+          If <c><anno>Delete</anno></c> equals <c>true</c>, the association
+          with <c><anno>Label</anno></c> will have been deleted from
+          <c><anno>ReqIdCollection</anno></c> in the resulting
+          <c><anno>NewReqIdCollection</anno></c>. If
+          <c><anno>Delete</anno></c> equals <c>false</c>,
+          <c><anno>NewReqIdCollection</anno></c> will equal
+          <c><anno>ReqIdCollection</anno></c>. Note that deleting an
+          association is not for free and that a collection containing
+          already handled requests can still be used by subsequent calls to
+          <c>check_response/3</c>,
+ 	  <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>,
+          and
+	  <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>.
+          However, without deleting handled associations, the above calls will
+          not be able to detect when there are no more outstanding requests to
+          handle, so you will have to keep track of this some other way than
+          relying on a <c>no_request</c> return. Note that if you pass a
+          collection only containing associations of already handled or
+          abandoned requests to <c>check_response/3</c>, it will always
+          return <c>no_reply</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>
@@ -409,34 +538,29 @@ gen_event:stop             ----->  Module:terminate/2
           does not exist, unless it is specified as <c>Name</c>.</p>
       </desc>
     </func>
-
+    
     <func>
-      <name since="OTP 24.0">receive_response(RequestId, Timeout) -> Result</name>
-      <fsummary>Receive 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>
+      <name name="receive_response" arity="2" clause_i="1" since="OTP 24.0"/>
+      <fsummary>Receive a response to an asynchronous call request
+      to a generic event manager.</fsummary>
       <desc>
 	<p>
-	This function is used to receive for a reply of a request made with
-	<seemfa marker="#send_request/3"><c>send_request/3</c></seemfa>
-	to the event manager. This function must be called from the same
-	process from which <seemfa marker="#send_request/3"><c>send_request/3</c></seemfa>
-	was made.
+	  Receive a response corresponding to the request identifier
+          <c><anno>ReqId</anno></c>- The request must have been made by
+	  <seemfa marker="#send_request/3"><c>send_request/3</c></seemfa>
+	  to the <c>gen_statem</c> process. This function must be called
+	  from the same process from which
+	  <seemfa marker="#send_request/3"><c>send_request/3</c></seemfa>
+	  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>. Assuming that the
+	  <c><anno>Timeout</anno></c> specifies how long to wait for
+          a response. If no response is received within the specified time,
+          the function returns <c>timeout</c>. Assuming that the
 	  server executes on a node supporting aliases (introduced in
-	  OTP 24) no response will be received after a timeout. Otherwise,
-	  a garbage response might be received at a later time.
+	  OTP 24) the request will also be abandoned. That is, no
+          response will be received after a timeout. Otherwise, a
+          stray response might be received at a later time.
 	</p>
 	<p>
 	  The return value <c>Reply</c> is defined in the return value
@@ -453,44 +577,197 @@ gen_event:stop             ----->  Module:terminate/2
 	</p>
 	<p>
 	  The difference between
-	  <seemfa marker="#wait_response/2"><c>wait_response()</c></seemfa>
-	  and <c>receive_response()</c> is that <c>receive_response()</c>
+	  <seemfa marker="#wait_response/2"><c>wait_response/2</c></seemfa>
+	  and <c>receive_response/2</c> is that <c>receive_response/2</c>
 	  abandons the request at timeout so that a potential future
-	  response is ignored, while <c>wait_response()</c> does not.
+	  response is ignored, while <c>wait_response/2</c> does not.
 	</p>
       </desc>
     </func>
 
     <func>
-      <name since="OTP 23.0">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>
+      <name name="receive_response" arity="3" since="OTP 25.0"/>
+      <fsummary>Receive a response to an asynchronous call request
+      to a generic event manager.</fsummary>
+      <desc>
+	<p>
+	  Receive a response corresponding to a request identifier saved
+          in <c><anno>ReqIdCollection</anno></c>. All request identifiers
+          of <c><anno>ReqIdCollection</anno></c> must correspond to requests
+          that have been made using
+	  <seemfa marker="#send_request/3"><c>send_request/3</c></seemfa> or
+          <seemfa marker="#send_request/5"><c>send_request/5</c></seemfa>,
+          and all request must have been made by the process calling this
+          function.
+	</p>
+        <p>
+          The <c><anno>Label</anno></c> in the response equals the
+          <c><anno>Label</anno></c> associated with the request identifier
+          that the response corresponds to. The <c><anno>Label</anno></c>
+          of a request identifier is associated when
+          <seemfa marker="#reqids_add/3">adding the request id</seemfa>
+          in a request identifier collection, or when sending the request using
+          <seemfa marker="#send_request/5"><c>send_request/5</c></seemfa>.
+        </p>
+        <p>
+          Compared to
+          <seemfa marker="#receive_response/2"><c>receive_response/2</c></seemfa>,
+          the returned result associated with a specific request identifier
+          will be wrapped in a 3-tuple. The first element of this tuple equals
+          the value that would have been produced by <c>receive_response/2</c>,
+          the second element equals the <c><anno>Label</anno></c> associated
+          with the specific request identifier, and the third element
+          <c><anno>NewReqIdCollection</anno></c> is a possibly modified
+          request identifier collection.
+        </p>
+        <p>
+          If <c><anno>ReqIdCollection</anno></c> is empty, the atom
+          <c>no_request</c> will be returned.
+        </p>
+	<p>
+	  <c><anno>Timeout</anno></c> specifies how long to wait for
+          a response. If no response is received within the specified time,
+          the function returns <c>timeout</c>. Assuming that the
+	  server executes on a node supporting aliases (introduced in
+	  OTP 24) all requests identified by <c><anno>ReqIdCollection</anno></c>
+          will also be abandoned. That is, no responses will be received
+          after a timeout. Otherwise, stray responses might be received
+          at a later time.
+	</p>
+	<p>
+	  The difference between <c>receive_response/3</c> and
+          <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>
+          is that <c>receive_response/3</c> abandons the requests at timeout
+          so that potential future responses are ignored, while
+          <c>wait_response/3</c> does not.
+	</p>
+        <p>
+          If <c><anno>Delete</anno></c> equals <c>true</c>, the association
+          with <c><anno>Label</anno></c> will have been deleted from
+          <c><anno>ReqIdCollection</anno></c> in the resulting
+          <c><anno>NewReqIdCollection</anno></c>. If
+          <c><anno>Delete</anno></c> equals <c>false</c>,
+          <c><anno>NewReqIdCollection</anno></c> will equal
+          <c><anno>ReqIdCollection</anno></c>. Note that deleting an
+          association is not for free and that a collection containing
+          already handled requests can still be used by subsequent calls to
+          <c>receive_response/3</c>,
+ 	  <seemfa marker="#check_response/3"><c>check_response/3</c></seemfa>,
+          and
+	  <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>.
+          However, without deleting handled associations, the above calls will
+          not be able to detect when there are no more outstanding requests to
+          handle, so you will have to keep track of this some other way than
+          relying on a <c>no_request</c> return. Note that if you pass a
+          collection only containing associations of already handled or
+          abandoned requests to <c>receive_response/3</c>, it will always block
+          until a timeout determined by <c><anno>Timeout</anno></c> is
+          triggered.
+        </p>
+      </desc>
+    </func>
+    
+    <func>
+      <name name="reqids_add" arity="3" since="OTP 25.0"/>
+      <fsummary>Save a request identifier.</fsummary>
+      <desc>
+	<p>
+          Saves <c><anno>ReqId</anno></c> and associates a <c><anno>Label</anno></c>
+          with the request identifier by adding this information to
+          <c><anno>ReqIdCollection</anno></c> and returning the
+          resulting request identifier collection.
+        </p>
+      </desc>
+    </func>
+
+    <func>
+      <name name="reqids_new" arity="0" since="OTP 25.0"/>
+      <fsummary>Create a new empty request identifier collection.</fsummary>
       <desc>
+	<p>
+          Returns a new empty request identifier collection. A 
+          request identifier collection can be utilized in order
+          the handle multiple outstanding requests.
+        </p>
+        <p>
+          Request identifiers of requests made by
+	  <seemfa marker="#send_request/3"><c>send_request/3</c></seemfa>
+          can be saved in a request identifier collection using 
+	  <seemfa marker="#reqids_add/3"><c>reqids_add/3</c></seemfa>.
+          Such a collection of request identifiers can later be used in
+          order to get one response corresponding to a request in the
+          collection by passing the collection as argument to
+          <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>,
+	  <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>,
+	  or,
+	  <seemfa marker="#check_response/3"><c>check_response/3</c></seemfa>.
+        </p>
         <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 <seemfa marker="#receive_response/2">
-	  <c>receive_response/2</c></seemfa>, <seemfa marker="#wait_response/2">
-	  <c>wait_response/2</c></seemfa>, or <seemfa
-	  marker="#check_response/2">
-	  <c>check_response/2</c></seemfa> in the same process to
-	  fetch the actual result of the request.
+          <seemfa marker="#reqids_size/1"><c>reqids_size/1</c></seemfa>
+          can be used to determine the amount of request identifiers in a
+          request identifier collection.
+        </p>
+      </desc>
+    </func>
+    
+    <func>
+      <name name="reqids_size" arity="1" since="OTP 25.0"/>
+      <fsummary>Get size of a request identifier collection.</fsummary>
+      <desc>
+	<p>
+          Returns the amount of request identifiers saved in
+          <c><anno>ReqIdCollection</anno></c>.
+        </p>
+      </desc>
+    </func>
+
+    <func>
+      <name name="reqids_to_list" arity="1" since="OTP 25.0"/>
+      <fsummary>List a request identifiers.</fsummary>
+      <desc>
+	<p>
+          Returns a list of <c>{<anno>ReqId</anno>, <anno>Label</anno>}</c>
+          tuples which corresponds to all request identifiers with their
+          associated labels present in the <c><anno>ReqIdCollection</anno></c>
+          collection.
+        </p>
+      </desc>
+    </func>
+
+    <func>
+      <name name="send_request" arity="3" since="OTP 23.0"/>
+      <fsummary>Send an asyncronous call request to a generic event manager.</fsummary>
+      <desc>
+        <p>
+          Sends an asynchronous <c>call</c> request <c><anno>Request</anno></c> to
+          event handler <c><anno>Handler</anno></c> installed in the event manager
+          identified by <c><anno>EventMgrRef</anno></c> and returns a request
+          identifier <c>ReqId</c>. The return value <c><anno>ReqId</anno></c>
+          shall later be used with
+          <seemfa marker="#receive_response/2"><c>receive_response/2</c></seemfa>,
+          <seemfa marker="#wait_response/2"><c>wait_response/2</c></seemfa>, or
+	  <seemfa marker="#check_response/2"><c>check_response/2</c></seemfa>
+	  to fetch the actual result of the request. Besides passing
+          the request identifier directly to these functions, it can also be
+          saved in a request identifier collection using 
+	  <seemfa marker="#reqids_add/3"><c>reqids_add/3</c></seemfa>.
+          Such a collection of request identifiers can later be used in
+          order to get one response corresponding to a request in the
+          collection by passing the collection as argument to
+          <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>,
+	  <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>, or
+	  <seemfa marker="#check_response/3"><c>check_response/3</c></seemfa>.
+          If you are about to save the request identifier in a request identifier
+          collection, you may want to consider using
+          <seemfa marker="#send_request/5"><c>send_request/5</c></seemfa>
+          instead.
 	</p>
 	<p>
-	  The call <c>gen_event:wait_response(gen_event:send_request(EventMgrRef,Handler,Request), Timeout)</c>
+	  The call <c>gen_event:receive_response(gen_event:send_request(<anno>EventMgrRef</anno>,
+          <anno>Handler</anno>, <anno>Request</anno>), Timeout)</c>
 	  can be seen as equivalent to
-	  <seemfa marker="#call/3"><c>gen_event:call(EventMgrRef,Handler,Request,Timeout)</c></seemfa>,
+	  <seemfa marker="#call/3"><c>gen_event:call(<anno>EventMgrRef</anno>,
+          <anno>Handler</anno>, <anno>Request</anno>, Timeout)</c></seemfa>,
 	  ignoring the error handling.
 	</p>
 	<p>
@@ -504,6 +781,38 @@ gen_event:stop             ----->  Module:terminate/2
       </desc>
     </func>
 
+
+    <func>
+      <name name="send_request" arity="5" since="OTP 25.0"/>
+      <fsummary>Sends a request to a generic server.</fsummary>
+      <desc>
+	<p>
+          Sends an asynchronous <c>call</c> request <c><anno>Request</anno></c> to
+          event handler <c><anno>Handler</anno></c> installed in the event manager
+          identified by <c><anno>EventMgrRef</anno></c>.
+          The <c><anno>Label</anno></c> will be associated with the request
+          identifier of the operation and added to the returned request
+          identifier collection <c><anno>NewReqIdCollection</anno></c>.
+          The collection can later be used in order to get one response
+          corresponding to a request in the collection by passing the
+          collection as argument to
+	  <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>,
+	  <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>,
+	  or,
+	  <seemfa marker="#check_response/3"><c>check_response/3</c></seemfa>.
+        </p>
+
+        <p>
+          The same as calling
+          <seemfa marker="#reqids_add/3"><c>gen_event:reqids_add</c></seemfa>(<seemfa
+          marker="#send_request/3"><c>gen_event:send_request</c></seemfa><c>(<anno>EventMgrRef</anno>,
+          <anno>Handler</anno>, <anno>Request</anno>), <anno>Label</anno>,
+          <anno>ReqIdCollection</anno>)</c>, but calling <c>send_request/5</c>
+          is slightly more efficient.
+        </p>
+      </desc>
+    </func>
+
     <func>
       <name since="">start() -> Result</name>
       <name since="">start(EventMgrName | Options) -> Result</name>
@@ -742,30 +1051,24 @@ gen_event:stop             ----->  Module:terminate/2
     </func>
 
     <func>
-      <name since="OTP 23.0">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>
+      <name name="wait_response" arity="2" since="OTP 23.0"/>
+      <fsummary>Wait or poll for a response to an asynchronous call request
+      to a generic event manager.</fsummary>
       <desc>
 	<p>
-	This function is used to wait for a reply of a request made with
-	<seemfa marker="#send_request/3"><c>send_request/3</c></seemfa>
-	to the event manager. This function must be called from the same
-	process from which <seemfa marker="#send_request/3"><c>send_request/3</c></seemfa>
-	was made.
+	  Wait for a response corresponding to the request identifier
+          <c><anno>ReqId</anno></c>. The request must have been made by
+	  <seemfa marker="#send_request/3"><c>send_request/3</c></seemfa>
+	  to the <c>gen_statem</c> process. This function must be called
+	  from the same process from which
+	  <seemfa marker="#send_request/3"><c>send_request/3</c></seemfa>
+	  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
+	  <c><anno>WaitTime</anno></c> specifies how long to wait for
+          a response. If no response 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
+	  done, and thus the function can be invoked repeatedly until a
 	  reply is returned.
 	</p>
 	<p>
@@ -783,11 +1086,89 @@ gen_event:stop             ----->  Module:terminate/2
 	</p>
 	<p>
 	  The difference between
-	  <seemfa marker="#receive_response/2"><c>receive_response()</c></seemfa>
-	  and <c>wait_response()</c> is that <c>receive_response()</c>
+	  <seemfa marker="#receive_response/2"><c>receive_response/2</c></seemfa>
+	  and <c>wait_response/2</c> is that <c>receive_response/2</c>
 	  abandons the request at timeout so that a potential future
-	  response is ignored, while <c>wait_response()</c> does not.
+	  response is ignored, while <c>wait_response/2</c> does not.
+	</p>
+      </desc>
+    </func>
+
+    <func>
+      <name name="wait_response" arity="3" since="OTP 25.0"/>
+      <fsummary>Wait or poll for a response to an asynchronous call request
+      to a generic event manager.</fsummary>
+      <desc>
+	<p>
+	  Wait for a response corresponding to a request identifier saved
+          in <c><anno>ReqIdCollection</anno></c>. All request identifiers
+          of <c><anno>ReqIdCollection</anno></c> must correspond to requests
+          that have been made using
+	  <seemfa marker="#send_request/3"><c>send_request/3</c></seemfa> or
+          <seemfa marker="#send_request/5"><c>send_request/5</c></seemfa>,
+          and all request must have been made by the process calling this
+          function.
 	</p>
+        <p>
+          The <c><anno>Label</anno></c> in the response equals the
+          <c><anno>Label</anno></c> associated with the request identifier
+          that the response corresponds to. The <c><anno>Label</anno></c>
+          of a request identifier is associated when
+          <seemfa marker="#reqids_add/3">saving the request id</seemfa> in
+          a request identifier collection, or when sending the request using
+          <seemfa marker="#send_request/5"><c>send_request/5</c></seemfa>.
+        </p>
+        <p>
+          Compared to
+          <seemfa marker="#wait_response/2"><c>wait_response/2</c></seemfa>,
+          the returned result associated with a specific request identifier
+          or an exception associated with a specific request identifier will
+          be wrapped in a 3-tuple. The first element of this tuple equals the
+          value that would have been produced by <c>wait_response/2</c>,
+          the second element equals the <c><anno>Label</anno></c> associated
+          with the specific request identifier, and the third element
+          <c><anno>NewReqIdCollection</anno></c> is a possibly  modified
+          request identifier collection.
+        </p>
+        <p>
+          If <c><anno>ReqIdCollection</anno></c> is empty, <c>no_request</c>
+          will be returned. If no response is received before the
+          <c><anno>WaitTime</anno></c> timeout has triggered, the atom
+          <c>timeout</c> is returned. It is valid to continue waiting for a
+          response as many times as needed up until a response has been received
+          and completed by <c>check_response()</c>, <c>receive_response()</c>,
+          or <c>wait_response()</c>.
+        </p>
+	<p>
+	  The difference between
+          <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>
+	  and <c>wait_response/3</c> is that <c>receive_response/3</c>
+	  abandons requests at timeout so that a potential future
+	  responses are ignored, while <c>wait_response/3</c> does not.
+	</p>
+        <p>
+          If <c><anno>Delete</anno></c> equals <c>true</c>, the association
+          with <c><anno>Label</anno></c> will have been deleted from
+          <c><anno>ReqIdCollection</anno></c> in the resulting
+          <c><anno>NewReqIdCollection</anno></c>. If
+          <c><anno>Delete</anno></c> equals <c>false</c>,
+          <c><anno>NewReqIdCollection</anno></c> will equal
+          <c><anno>ReqIdCollection</anno></c>. Note that deleting an
+          association is not for free and that a collection containing
+          already handled requests can still be used by subsequent calls to
+          <c>wait_response/3</c>,
+ 	  <seemfa marker="#check_response/3"><c>check_response/3</c></seemfa>,
+          and
+	  <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>.
+          However, without deleting handled associations, the above calls will
+          not be able to detect when there are no more outstanding requests to
+          handle, so you will have to keep track of this some other way than
+          relying on a <c>no_request</c> return. Note that if you pass a
+          collection only containing associations of already handled or
+          abandoned requests to <c>wait_response/3</c>, it will always block
+          until a timeout determined by <c><anno>WaitTime</anno></c> is
+          triggered and then return <c>no_reply</c>.
+        </p>
       </desc>
     </func>
 
diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml
index 8f07adc1ac..d11799f2ab 100644
--- a/lib/stdlib/doc/src/gen_server.xml
+++ b/lib/stdlib/doc/src/gen_server.xml
@@ -412,12 +412,67 @@ gen_server:abcast     -----> Module:handle_cast/2
       <name name="request_id"/>
       <desc>
 	<p>
-	  A request handle, see
-          <seemfa marker="#send_request/2"> <c>send_request/2</c> </seemfa>
+	  An opaque request identifier. See
+          <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>
 	  for details.
 	</p>
       </desc>
     </datatype>
+
+    <datatype>
+      <name name="request_id_collection"/>
+      <desc>
+        <p>
+	  An opaque collection of request identifiers
+          (<seetype marker="#request_id"><c>request_id()</c></seetype>)
+	  where each request identifier can be associated with a label
+          chosen by the user. For more information see
+          <seemfa marker="#reqids_new/0"><c>reqids_new/0</c></seemfa>.
+	</p>
+      </desc>
+    </datatype>
+
+    <datatype>
+      <name name="response_timeout"/>
+      <desc>
+        <p>
+          Used to set a time limit on how long to wait for a response using
+          either 
+          <seemfa marker="#receive_response/2"><c>receive_response/2</c></seemfa>,
+          <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>,
+          <seemfa marker="#wait_response/2"><c>wait_response/2</c></seemfa>,
+          or
+          <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>.
+          The time unit used is <c>millisecond</c>. Currently valid values:
+        </p>
+        <taglist>
+          <tag><c>0..4294967295</c></tag>
+          <item><p>
+            Timeout relative to current time in milliseconds.
+          </p></item>
+          <tag><c>infinity</c></tag>
+          <item><p>
+            Infinite timeout. That is, the operation will never time out.
+          </p></item>
+          <tag><c>{abs, Timeout}</c></tag>
+          <item><p>
+            An absolute
+            <seemfa marker="erts:erlang#monotonic_time/1">Erlang monotonic time</seemfa>
+            timeout in milliseconds. That is, the operation will time out when
+            <seemfa marker="erts:erlang#monotonic_time/1"><c>erlang:monotonic_time(millisecond)</c></seemfa>
+            returns a value larger than or equal to <c>Timeout</c>. <c>Timeout</c>
+            is not allowed to identify a time further into the future than <c>4294967295</c>
+            milliseconds. Identifying the timeout using an absolute timeout value
+            is especially handy when you have a deadline for responses corresponding
+            to a complete collection of requests
+            (<seetype marker="#request_id_collection"><c>request_id_collection()</c></seetype>)
+,
+            since you do not have to recalculate the relative time until the deadline
+            over and over again.
+	  </p></item>
+        </taglist>
+      </desc>
+    </datatype>
   </datatypes>
 
 
@@ -607,31 +662,101 @@ gen_server:abcast     -----> Module:handle_cast/2
     </func>
 
     <func>
-      <name name="check_response" arity="2" since="OTP-23"/>
-      <fsummary>Check if a message is a reply from a server.</fsummary>
+      <name name="check_response" arity="2" since="OTP 23.0"/>
+      <fsummary>Check if a message is a response 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
-	  <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>.
-	  If <c><anno>Msg</anno></c> is a reply
-          to the handle <c><anno>RequestId</anno></c>
-	  the result of the request is returned in <c><anno>Reply</anno></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.
+	  Check if <c><anno>Msg</anno></c> is a response corresponding
+          to the request identifier <c><anno>ReqId</anno></c>. The request
+          must have been made by
+	  <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>,
+	  and it must have been made by the same process calling
+          this function.
 	</p>
+        <p>
+          If <c><anno>Msg</anno></c> is a response corresponding to
+          <c><anno>ReqId</anno></c> the response is returned; otherwise,
+          <c>no_reply</c> is returned and no cleanup is done, and
+	  thus the function must be invoked repeatedly until a response
+	  is returned.
+        </p>
 	<p>
-	  The return value <c>Reply</c> is passed from the return value
-	  of <c>Module:handle_call/3</c>.
+	  The return value <c><anno>Reply</anno></c> is passed from 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.
+	  died before a reply was sent.
 	</p>
       </desc>
     </func>
+    
+    <func>
+      <name name="check_response" arity="3" since="OTP 25.0"/>
+      <fsummary>Check if a message is a response from a server.</fsummary>
+      <desc>
+	<p>
+	  Check if <c><anno>Msg</anno></c> is a response corresponding
+          to a request identifier saved in <c><anno>ReqIdCollection</anno></c>.
+          All request identifiers of <c><anno>ReqIdCollection</anno></c>
+          must correspond to requests that have been made using
+	  <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa> or
+          <seemfa marker="#send_request/4"><c>send_request/4</c></seemfa>,
+          and all request must have been made by the process calling this
+          function.
+	</p>
+        <p>
+          The <c><anno>Label</anno></c> in the response equals the
+          <c><anno>Label</anno></c> associated with the request identifier
+          that the response corresponds to. The <c><anno>Label</anno></c>
+          of a request identifier is associated when
+          <seemfa marker="#reqids_add/3">saving the request id</seemfa>
+          in a request identifier collection, or when sending the request using
+          <seemfa marker="#send_request/4"><c>send_request/4</c></seemfa>.
+        </p>
+        <p>
+          Compared to
+          <seemfa marker="#check_response/2"><c>check_response/2</c></seemfa>,
+          the returned result associated with a specific request identifier
+          or an exception associated with a specific request identifier will
+          be wrapped in a 3-tuple. The first element of this tuple equals the
+          value that would have been produced by <c>check_response/2</c>,
+          the second element equals the <c><anno>Label</anno></c> associated
+          with the specific request identifier, and the third element
+          <c><anno>NewReqIdCollection</anno></c> is a possibly  modified
+          request identifier collection.
+        </p>
+        <p>
+          If <c><anno>ReqIdCollection</anno></c> is empty, the atom
+          <c>no_request</c> will be returned. If <c><anno>Msg</anno></c>
+          does not correspond to any of the request identifiers in
+          <c><anno>ReqIdCollection</anno></c>, the atom
+          <c>no_reply</c> is returned.
+        </p>
+        <p>
+          If <c><anno>Delete</anno></c> equals <c>true</c>, the association
+          with <c><anno>Label</anno></c> will have been deleted from
+          <c><anno>ReqIdCollection</anno></c> in the resulting
+          <c><anno>NewReqIdCollection</anno></c>. If
+          <c><anno>Delete</anno></c> equals <c>false</c>,
+          <c><anno>NewReqIdCollection</anno></c> will equal
+          <c><anno>ReqIdCollection</anno></c>. Note that deleting an
+          association is not for free and that a collection containing
+          already handled requests can still be used by subsequent calls to
+          <c>check_response/3</c>,
+ 	  <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>,
+          and
+	  <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>.
+          However, without deleting handled associations, the above calls will
+          not be able to detect when there are no more outstanding requests to
+          handle, so you will have to keep track of this some other way than
+          relying on a <c>no_request</c> return. Note that if you pass a
+          collection only containing associations of already handled or
+          abandoned requests to <c>check_response/3</c>, it will always
+          return <c>no_reply</c>.
+        </p>
+      </desc>
+    </func>
 
     <func>
       <name name="enter_loop" arity="3" since=""/>
@@ -781,45 +906,123 @@ gen_server:abcast     -----> Module:handle_cast/2
 
     <func>
       <name name="receive_response" arity="2" since="OTP 24.0"/>
-      <fsummary>Receive a reply from a server.</fsummary>
+      <fsummary>Receive a response from a server.</fsummary>
       <desc>
 	<p>
-	  This function is used to receive a reply to a request made with
-	  <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>
-	  to a <c>gen_server</c> process.
-          This function must be called by the same process that called
-	  <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>.
+	  Receive a response corresponding to the request identifier
+          <c><anno>ReqId</anno></c>. The request must have been made by
+	  <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>,
+	  and it must have been made by the same process calling
+          this function.
 	</p>
 	<p>
-	  <c><anno>Timeout</anno></c> is an integer
-          that specifies how many milliseconds
-          to wait for a 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>.
-          If the server executes on a node supporting aliases
-          (introduced in OTP 24) no response will be received
-          after a time-out. Otherwise, a garbage response
-          might be received at a later time.
+	  <c><anno>Timeout</anno></c> specifies how long to wait for
+          a response. If no response is received within the specified time,
+          the function returns <c>timeout</c>. Assuming that the
+	  server executes on a node supporting aliases (introduced in
+	  OTP 24) the request will also be abandoned. That is, no
+          response will be received after a timeout. Otherwise, a
+          stray response might be received at a later time.
 	</p>
 	<p>
-	  A returned <c><anno>Reply</anno></c> is passed from
-          the return value of <c>Module:handle_call/3</c>.
+	  The return value <c><anno>Reply</anno></c> is passed from 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.
+	  died before a reply was sent.
 	</p>
 	<p>
-	  The difference between
-	  <seemfa marker="#wait_response/2"><c>wait_response()</c></seemfa>
-	  and <c>receive_response()</c> is that <c>receive_response()</c>
-	  abandons the request at time-out so that a potential late
-	  response is ignored, while <c>wait_response()</c> does not.
+	  The difference between <c>receive_response/2</c> and
+          <seemfa marker="#wait_response/2"><c>wait_response/2</c></seemfa>
+	  is that <c>receive_response/2</c> abandons the request at
+          timeout so that a potential future response is ignored, while
+          <c>wait_response/2</c> does not.
 	</p>
       </desc>
     </func>
 
+    <func>
+      <name name="receive_response" arity="3" since="OTP 25.0"/>
+      <fsummary>Receive a response from a server.</fsummary>
+      <desc>
+	<p>
+	  Receive a response corresponding to a request identifier saved
+          in <c><anno>ReqIdCollection</anno></c>. All request identifiers
+          of <c><anno>ReqIdCollection</anno></c> must correspond to requests
+          that have been made using
+	  <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa> or
+          <seemfa marker="#send_request/4"><c>send_request/4</c></seemfa>,
+          and all request must have been made by the process calling this
+          function.
+	</p>
+        <p>
+          The <c><anno>Label</anno></c> in the response equals the
+          <c><anno>Label</anno></c> associated with the request identifier
+          that the response corresponds to. The <c><anno>Label</anno></c>
+          of a request identifier is associated when
+          <seemfa marker="#reqids_add/3">adding the request id</seemfa>
+          in a request identifier collection, or when sending the request using
+          <seemfa marker="#send_request/4"><c>send_request/4</c></seemfa>.
+        </p>
+        <p>
+          Compared to
+          <seemfa marker="#receive_response/2"><c>receive_response/2</c></seemfa>,
+          the returned result associated with a specific request identifier
+          will be wrapped in a 3-tuple. The first element of this tuple equals
+          the value that would have been produced by <c>receive_response/2</c>,
+          the second element equals the <c><anno>Label</anno></c> associated
+          with the specific request identifier, and the third element
+          <c><anno>NewReqIdCollection</anno></c> is a possibly modified
+          request identifier collection.
+        </p>
+        <p>
+          If <c><anno>ReqIdCollection</anno></c> is empty, the atom
+          <c>no_request</c> will be returned.
+        </p>
+	<p>
+	  <c><anno>Timeout</anno></c> specifies how long to wait for
+          a response. If no response is received within the specified time,
+          the function returns <c>timeout</c>. Assuming that the
+	  server executes on a node supporting aliases (introduced in
+	  OTP 24) all requests identified by <c><anno>ReqIdCollection</anno></c>
+          will also be abandoned. That is, no responses will be received
+          after a timeout. Otherwise, stray responses might be received
+          at a later time.
+	</p>
+	<p>
+	  The difference between <c>receive_response/3</c> and
+          <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>
+          is that <c>receive_response/3</c> abandons the requests at timeout
+          so that potential future responses are ignored, while
+          <c>wait_response/3</c> does not.
+	</p>
+        <p>
+          If <c><anno>Delete</anno></c> equals <c>true</c>, the association
+          with <c><anno>Label</anno></c> will have been deleted from
+          <c><anno>ReqIdCollection</anno></c> in the resulting
+          <c><anno>NewReqIdCollection</anno></c>. If
+          <c><anno>Delete</anno></c> equals <c>false</c>,
+          <c><anno>NewReqIdCollection</anno></c> will equal
+          <c><anno>ReqIdCollection</anno></c>. Note that deleting an
+          association is not for free and that a collection containing
+          already handled requests can still be used by subsequent calls to
+          <c>receive_response/3</c>,
+ 	  <seemfa marker="#check_response/3"><c>check_response/3</c></seemfa>,
+          and
+	  <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>.
+          However, without deleting handled associations, the above calls will
+          not be able to detect when there are no more outstanding requests to
+          handle, so you will have to keep track of this some other way than
+          relying on a <c>no_request</c> return. Note that if you pass a
+          collection only containing associations of already handled or
+          abandoned requests to <c>receive_response/3</c>, it will always block
+          until a timeout determined by <c><anno>Timeout</anno></c> is
+          triggered.
+        </p>
+      </desc>
+    </func>
+    
     <func>
       <name name="reply" arity="2" since=""/>
       <fsummary>Send a reply to a client.</fsummary>
@@ -843,29 +1046,105 @@ gen_server:abcast     -----> Module:handle_cast/2
       </desc>
     </func>
 
+    <func>
+      <name name="reqids_add" arity="3" since="OTP 25.0"/>
+      <fsummary>Save a request identifier.</fsummary>
+      <desc>
+	<p>
+          Saves <c><anno>ReqId</anno></c> and associates a <c><anno>Label</anno></c>
+          with the request identifier by adding this information to
+          <c><anno>ReqIdCollection</anno></c> and returning the
+          resulting request identifier collection.
+        </p>
+      </desc>
+    </func>
+    
+    <func>
+      <name name="reqids_new" arity="0" since="OTP 25.0"/>
+      <fsummary>Create a new empty request identifier collection.</fsummary>
+      <desc>
+	<p>
+          Returns a new empty request identifier collection. A 
+          request identifier collection can be utilized in order
+          the handle multiple outstanding requests.
+        </p>
+        <p>
+          Request identifiers of requests made by
+	  <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>
+          can be saved in a request identifier collection using 
+	  <seemfa marker="#reqids_add/3"><c>reqids_add/3</c></seemfa>.
+          Such a collection of request identifiers can later be used in
+          order to get one response corresponding to a request in the
+          collection by passing the collection as argument to
+          <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>,
+	  <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>,
+	  or,
+	  <seemfa marker="#check_response/3"><c>check_response/3</c></seemfa>.
+        </p>
+        <p>
+          <seemfa marker="#reqids_size/1"><c>reqids_size/1</c></seemfa>
+          can be used to determine the amount of request identifiers in a
+          request identifier collection.
+        </p>
+      </desc>
+    </func>
+
+    <func>
+      <name name="reqids_size" arity="1" since="OTP 25.0"/>
+      <fsummary>Get size of a request identifier collection.</fsummary>
+      <desc>
+	<p>
+          Returns the amount of request identifiers saved in
+          <c><anno>ReqIdCollection</anno></c>.
+        </p>
+      </desc>
+    </func>
+
+    <func>
+      <name name="reqids_to_list" arity="1" since="OTP 25.0"/>
+      <fsummary>List a request identifiers.</fsummary>
+      <desc>
+	<p>
+          Returns a list of <c>{<anno>ReqId</anno>, <anno>Label</anno>}</c>
+          tuples which corresponds to all request identifiers with their
+          associated labels present in the <c><anno>ReqIdCollection</anno></c>
+          collection.
+        </p>
+      </desc>
+    </func>
+
     <func>
       <name name="send_request" arity="2" since="OTP 23.0"/>
       <fsummary>Sends a request to a generic server.</fsummary>
       <desc>
         <p>
-	  Sends a request to the <c><anno>ServerRef</anno></c>
-          of the <c>gen_server</c> process
-          and returns a handle <c><anno>RequestId</anno></c>.
-	  The returned handle shall later be used with
-          <seemfa marker="#receive_response/2"><c>receive_response/2</c></seemfa>,
-          <seemfa marker="#wait_response/2"><c>wait_response/2</c></seemfa>, or
-	  <seemfa marker="#check_response/2"><c>check_response/2</c></seemfa>
-	  to fetch the actual result of the request.
+	  Sends an asynchronous <c>call</c> request <c><anno>Request</anno></c>
+          to the <c>gen_server</c> process identified by <c><anno>ServerRef</anno></c>
+          and returns a request identifier <c><anno>ReqId</anno></c>. The return
+          value <c><anno>ReqId</anno></c> shall later be used with
+          <seemfa marker="#receive_response/2"> <c>receive_response/2</c></seemfa>,
+          <seemfa marker="#wait_response/2"> <c>wait_response/2</c></seemfa>, or
+	  <seemfa marker="#check_response/2"> <c>check_response/2</c></seemfa>
+	  to fetch the actual result of the request. Besides passing
+          the request identifier directly to these functions, it can also be
+          saved in a request identifier collection using 
+	  <seemfa marker="#reqids_add/3"><c>reqids_add/3</c></seemfa>.
+          Such a collection of request identifiers can later be used in
+          order to get one response corresponding to a request in the
+          collection by passing the collection as argument to
+          <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>,
+	  <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>, or
+	  <seemfa marker="#check_response/3"><c>check_response/3</c></seemfa>.
+          If you are about to save the request identifier in a request identifier
+          collection, you may want to consider using
+          <seemfa marker="#send_request/4"><c>send_request/4</c></seemfa>
+          instead.
 	</p>
 	<p>
-	  The call
-          <c>
-            gen_server:wait_response(gen_server:send_request(<anno>ServerRef</anno>,
-            <anno>Request</anno>), Timeout)
-          </c>
-	  can be seen as equivalent to
-	  <seemfa marker="#call/3"><c>gen_server:call(<anno>ServerRef</anno>, <anno>Request</anno>, Timeout)</c></seemfa>,
-	  ignoring the error handling.
+          The call <c>gen_server:receive_response(gen_server:send_request(<anno>ServerRef</anno>,
+          <anno>Request</anno>), Timeout)</c> can be seen as equivalent to
+	  <seemfa marker="#call/3"><c>gen_server:call(<anno>ServerRef</anno>, <anno>Request</anno>,
+          Timeout)</c></seemfa>, ignoring the error handling.
 	</p>
 	<p>
 	  The <c>gen_server</c> process calls
@@ -886,6 +1165,36 @@ gen_server:abcast     -----> Module:handle_cast/2
       </desc>
     </func>
 
+    <func>
+      <name name="send_request" arity="4" since="OTP 25.0"/>
+      <fsummary>Sends a request to a generic server.</fsummary>
+      <desc>
+	<p>
+          Sends an asynchronous <c>call</c> request <c><anno>Request</anno></c>
+          to the <c>gen_server</c> process identified by <c><anno>ServerRef</anno></c>.
+          The <c><anno>Label</anno></c> will be associated with the request
+          identifier of the operation and added to the returned request
+          identifier collection <c><anno>NewReqIdCollection</anno></c>.
+          The collection can later be used in order to get one response
+          corresponding to a request in the collection by passing the
+          collection as argument to
+	  <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>,
+	  <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>,
+	  or,
+	  <seemfa marker="#check_response/3"><c>check_response/3</c></seemfa>.
+        </p>
+
+        <p>
+          The same as calling
+          <seemfa marker="#reqids_add/3"><c>gen_server:reqids_add</c></seemfa>(<seemfa
+          marker="#send_request/2"><c>gen_server:send_request</c></seemfa><c>(<anno>ServerRef</anno>,
+          <anno>Request</anno>), <anno>Label</anno>,
+          <anno>ReqIdCollection</anno>)</c>, but calling <c>send_request/4</c>
+          is slightly more efficient.
+        </p>
+      </desc>
+    </func>
+
     <func>
       <name name="start" arity="3" since=""/>
       <name name="start" arity="4" since=""/>
@@ -1028,41 +1337,116 @@ gen_server:abcast     -----> Module:handle_cast/2
 
     <func>
       <name name="wait_response" arity="2" since="OTP 23.0"/>
-      <fsummary>Wait for a reply from a server.</fsummary>
+      <fsummary>Wait or poll for a response from a server.</fsummary>
       <desc>
 	<p>
-	  This function is used to wait for a reply of a request made with
-	  <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>
-	  from the <c>gen_server</c> process.
-          This function must be called from the same process that called
-	  <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>.
+	  Wait for a response corresponding to the request identifier
+          <c><anno>ReqId</anno></c>. The request must have been made by
+	  <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>,
+	  and it must have been made by the same process calling
+          this function.
 	</p>
 	<p>
-	  <c><anno>Timeout</anno></c> is an integer
-	  that specifies how many milliseconds to wait for a 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;
-          thus the function can be invoked repeatedly until a
+	  <c><anno>WaitTime</anno></c> specifies how long to wait for
+          a reply. 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 passed from
-          the return value of <c>Module:handle_call/3</c>.
+	  The return value <c><anno>Reply</anno></c> is passed from 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.
+	  died before a reply was sent.
 	</p>
 	<p>
 	  The difference between
-	  <seemfa marker="#receive_response/2"><c>receive_response()</c></seemfa>
-	  and <c>wait_response()</c> is that <c>receive_response()</c>
+	  <seemfa marker="#receive_response/2"><c>receive_response/2</c></seemfa>
+	  and <c>wait_response/2</c> is that <c>receive_response/2</c>
 	  abandons the request at time-out so that a potential future
-	  response is ignored, while <c>wait_response()</c> does not.
+	  response is ignored, while <c>wait_response/2</c> does not.
 	</p>
       </desc>
     </func>
+    
+    <func>
+      <name name="wait_response" arity="3" since="OTP 25.0"/>
+      <fsummary>Wait or poll for a response from a server.</fsummary>
+      <desc>
+	<p>
+	  Wait for a response corresponding to a request identifier saved
+          in <c><anno>ReqIdCollection</anno></c>. All request identifiers
+          of <c><anno>ReqIdCollection</anno></c> must correspond to requests
+          that have been made using
+	  <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa> or
+          <seemfa marker="#send_request/4"><c>send_request/4</c></seemfa>,
+          and all request must have been made by the process calling this
+          function.
+	</p>
+        <p>
+          The <c><anno>Label</anno></c> in the response equals the
+          <c><anno>Label</anno></c> associated with the request identifier
+          that the response corresponds to. The <c><anno>Label</anno></c>
+          of a request identifier is associated when
+          <seemfa marker="#reqids_add/3">saving the request id</seemfa> in
+          a request identifier collection, or when sending the request using
+          <seemfa marker="#send_request/4"><c>send_request/4</c></seemfa>.
+        </p>
+        <p>
+          Compared to
+          <seemfa marker="#wait_response/2"><c>wait_response/2</c></seemfa>,
+          the returned result associated with a specific request identifier
+          or an exception associated with a specific request identifier will
+          be wrapped in a 3-tuple. The first element of this tuple equals the
+          value that would have been produced by <c>wait_response/2</c>,
+          the second element equals the <c><anno>Label</anno></c> associated
+          with the specific request identifier, and the third element
+          <c><anno>NewReqIdCollection</anno></c> is a possibly  modified
+          request identifier collection.
+        </p>
+        <p>
+          If <c><anno>ReqIdCollection</anno></c> is empty, <c>no_request</c>
+          will be returned. If no response is received before the
+          <c><anno>WaitTime</anno></c> timeout has triggered, the atom
+          <c>timeout</c> is returned. It is valid to continue waiting for a
+          response as many times as needed up until a response has been received
+          and completed by <c>check_response()</c>, <c>receive_response()</c>,
+          or <c>wait_response()</c>.
+        </p>
+	<p>
+	  The difference between
+          <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>
+	  and <c>wait_response/3</c> is that <c>receive_response/3</c>
+	  abandons requests at timeout so that a potential future
+	  responses are ignored, while <c>wait_response/3</c> does not.
+	</p>
+        <p>
+          If <c><anno>Delete</anno></c> equals <c>true</c>, the association
+          with <c><anno>Label</anno></c> will have been deleted from
+          <c><anno>ReqIdCollection</anno></c> in the resulting
+          <c><anno>NewReqIdCollection</anno></c>. If
+          <c><anno>Delete</anno></c> equals <c>false</c>,
+          <c><anno>NewReqIdCollection</anno></c> will equal
+          <c><anno>ReqIdCollection</anno></c>. Note that deleting an
+          association is not for free and that a collection containing
+          already handled requests can still be used by subsequent calls to
+          <c>wait_response/3</c>,
+ 	  <seemfa marker="#check_response/3"><c>check_response/3</c></seemfa>,
+          and
+	  <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>.
+          However, without deleting handled associations, the above calls will
+          not be able to detect when there are no more outstanding requests to
+          handle, so you will have to keep track of this some other way than
+          relying on a <c>no_request</c> return. Note that if you pass a
+          collection only containing associations of already handled or
+          abandoned requests to <c>wait_response/3</c>, it will always block
+          until a timeout determined by <c><anno>WaitTime</anno></c> is
+          triggered and then return <c>no_reply</c>.
+        </p>
+      </desc>
+    </func>
   </funcs>
 
  
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
index c1a2b1e529..6711addc1c 100644
--- a/lib/stdlib/doc/src/gen_statem.xml
+++ b/lib/stdlib/doc/src/gen_statem.xml
@@ -1624,11 +1624,67 @@ handle_event(_, _, State, Data) ->
       <name name="request_id"/>
       <desc>
 	<p>
-	  A request handle, see <seemfa marker="#send_request/2"> <c>send_request/2</c> </seemfa>
+	  An opaque request identifier. See
+          <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>
 	  for details.
 	</p>
       </desc>
     </datatype>
+
+    <datatype>
+      <name name="request_id_collection"/>
+      <desc>
+        <p>
+	  An opaque collection of request identifiers
+          (<seetype marker="#request_id"><c>request_id()</c></seetype>)
+	  where each request identifier can be associated with a label
+          chosen by the user. For more information see
+          <seemfa marker="#reqids_new/0"><c>reqids_new/0</c></seemfa>.
+	</p>
+      </desc>
+    </datatype>
+
+    <datatype>
+      <name name="response_timeout"/>
+      <desc>
+        <p>
+          Used to set a time limit on how long to wait for a response using
+          either 
+          <seemfa marker="#receive_response/2"><c>receive_response/2</c></seemfa>,
+          <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>,
+          <seemfa marker="#wait_response/2"><c>wait_response/2</c></seemfa>,
+          or
+          <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>.
+          The time unit used is <c>millisecond</c>. Currently valid values:
+        </p>
+        <taglist>
+          <tag><c>0..4294967295</c></tag>
+          <item><p>
+            Timeout relative to current time in milliseconds.
+          </p></item>
+          <tag><c>infinity</c></tag>
+          <item><p>
+            Infinite timeout. That is, the operation will never time out.
+          </p></item>
+          <tag><c>{abs, Timeout}</c></tag>
+          <item><p>
+            An absolute
+            <seemfa marker="erts:erlang#monotonic_time/1">Erlang monotonic time</seemfa>
+            timeout in milliseconds. That is, the operation will time out when
+            <seemfa marker="erts:erlang#monotonic_time/1"><c>erlang:monotonic_time(millisecond)</c></seemfa>
+            returns a value larger than or equal to <c>Timeout</c>. <c>Timeout</c>
+            is not allowed to identify a time further into the future than <c>4294967295</c>
+            milliseconds. Identifying the timeout using an absolute timeout value
+            is especially handy when you have a deadline for responses corresponding
+            to a complete collection of requests
+            (<seetype marker="#request_id_collection"><c>request_id_collection()</c></seetype>)
+,
+            since you do not have to recalculate the relative time until the deadline
+            over and over again.
+	  </p></item>
+        </taglist>
+      </desc>
+    </datatype>
   </datatypes>
 
   <funcs>
@@ -1744,15 +1800,15 @@ handle_event(_, _, State, Data) ->
     </func>
 
     <func>
-      <name name="check_response" arity="2" since="OTP-23"/>
+      <name name="check_response" arity="2" since="OTP 23.0"/>
       <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
+	  Check if <c><anno>Msg</anno></c> is a response corresponding
+          to the request identifier <c><anno>ReqId</anno></c>. The request
+          must have been made by
 	  <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>.
-	  If <c>Msg</c> is a reply to the handle <c>RequestId</c>
+	  If <c>Msg</c> is a reply to the handle <c>ReqId</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
@@ -1774,6 +1830,73 @@ handle_event(_, _, State, Data) ->
       </desc>
     </func>
 
+    <func>
+      <name name="check_response" arity="3" since="OTP 25.0"/>
+      <fsummary>Check if a message is a reply from a server.</fsummary>
+      <desc>
+	<p>
+	  Check if <c><anno>Msg</anno></c> is a response corresponding
+          to a request identifier saved in <c><anno>ReqIdCollection</anno></c>.
+          All request identifiers of <c><anno>ReqIdCollection</anno></c>
+          must correspond to requests that have been made using
+	  <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa> or
+          <seemfa marker="#send_request/4"><c>send_request/4</c></seemfa>,
+          and all request must have been made by the process calling this
+          function.
+	</p>
+        <p>
+          The <c><anno>Label</anno></c> in the response equals the
+          <c><anno>Label</anno></c> associated with the request identifier
+          that the response corresponds to. The <c><anno>Label</anno></c>
+          of a request identifier is associated when
+          <seemfa marker="#reqids_add/3">saving the request id</seemfa>
+          in a request identifier collection, or when sending the request using
+          <seemfa marker="#send_request/4"><c>send_request/4</c></seemfa>.
+        </p>
+        <p>
+          Compared to
+          <seemfa marker="#check_response/2"><c>check_response/2</c></seemfa>,
+          the returned result associated with a specific request identifier
+          or an exception associated with a specific request identifier will
+          be wrapped in a 3-tuple. The first element of this tuple equals the
+          value that would have been produced by <c>check_response/2</c>,
+          the second element equals the <c><anno>Label</anno></c> associated
+          with the specific request identifier, and the third element
+          <c><anno>NewReqIdCollection</anno></c> is a possibly  modified
+          request identifier collection.
+        </p>
+        <p>
+          If <c><anno>ReqIdCollection</anno></c> is empty, the atom
+          <c>no_request</c> will be returned. If <c><anno>Msg</anno></c>
+          does not correspond to any of the request identifiers in
+          <c><anno>ReqIdCollection</anno></c>, the atom
+          <c>no_reply</c> is returned.
+        </p>
+        <p>
+          If <c><anno>Delete</anno></c> equals <c>true</c>, the association
+          with <c><anno>Label</anno></c> will have been deleted from
+          <c><anno>ReqIdCollection</anno></c> in the resulting
+          <c><anno>NewReqIdCollection</anno></c>. If
+          <c><anno>Delete</anno></c> equals <c>false</c>,
+          <c><anno>NewReqIdCollection</anno></c> will equal
+          <c><anno>ReqIdCollection</anno></c>. Note that deleting an
+          association is not for free and that a collection containing
+          already handled requests can still be used by subsequent calls to
+          <c>check_response/3</c>,
+ 	  <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>,
+          and
+	  <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>.
+          However, without deleting handled associations, the above calls will
+          not be able to detect when there are no more outstanding requests to
+          handle, so you will have to keep track of this some other way than
+          relying on a <c>no_request</c> return. Note that if you pass a
+          collection only containing associations of already handled or
+          abandoned requests to <c>check_response/3</c>, it will always
+          return <c>no_reply</c>.
+        </p>
+      </desc>
+    </func>
+
     <func>
       <name name="enter_loop" arity="4" since="OTP 19.1"/>
       <fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary>
@@ -1871,11 +1994,23 @@ handle_event(_, _, State, Data) ->
 
     <func>
       <name name="receive_response" arity="1" since="OTP 24.0"/>
-      <name name="receive_response" arity="2" since="OTP 24.0"/>
       <fsummary>Receive for a reply from a server.</fsummary>
       <desc>
 	<p>
-	  This function is used to receive for a reply of a request made with
+          The same as calling 
+	  <seemfa marker="#receive_response/2"><c>gen_statem:receive_response(ReqId,
+          infinity)</c></seemfa>.
+        </p>
+      </desc>
+    </func>
+    
+    <func>
+      <name name="receive_response" arity="2" clause_i="1" since="OTP 24.0"/>
+      <fsummary>Receive for a reply from a server.</fsummary>
+      <desc>
+	<p>
+	  Receive a response corresponding to the request identifier
+          <c><anno>ReqId</anno></c>- The request must have been made by
 	  <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>
 	  to the <c>gen_statem</c> process. This function must be called
 	  from the same process from which
@@ -1883,15 +2018,13 @@ handle_event(_, _, State, Data) ->
 	  was made.
 	</p>
 	<p>
-	  <c>Timeout</c> is an integer
-	  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>. Assuming that the
+	  <c><anno>Timeout</anno></c> specifies how long to wait for
+          a response. If no response is received within the specified time,
+          the function returns <c>timeout</c>. Assuming that the
 	  server executes on a node supporting aliases (introduced in
-	  OTP 24) no response will be received after a timeout. Otherwise,
-	  a garbage response might be received at a later time.
+	  OTP 24) the request will also be abandoned. That is, no
+          response will be received after a timeout. Otherwise, a
+          stray response might be received at a later time.
 	</p>
 	<p>
 	  The return value <c><anno>Reply</anno></c> is generated when a
@@ -1908,13 +2041,94 @@ handle_event(_, _, State, Data) ->
 	</p>
 	<p>
 	  The difference between
-	  <seemfa marker="#wait_response/2"><c>wait_response()</c></seemfa>
-	  and <c>receive_response()</c> is that <c>receive_response()</c>
+	  <seemfa marker="#wait_response/2"><c>wait_response/2</c></seemfa>
+	  and <c>receive_response/2</c> is that <c>receive_response/2</c>
 	  abandons the request at timeout so that a potential future
-	  response is ignored, while <c>wait_response()</c> does not.
+	  response is ignored, while <c>wait_response/2</c> does not.
 	</p>
       </desc>
     </func>
+    
+    <func>
+      <name name="receive_response" arity="3" since="OTP 25.0"/>
+      <fsummary>Receive a response from a server.</fsummary>
+      <desc>
+	<p>
+	  Receive a response corresponding to a request identifier saved
+          in <c><anno>ReqIdCollection</anno></c>. All request identifiers
+          of <c><anno>ReqIdCollection</anno></c> must correspond to requests
+          that have been made using
+	  <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa> or
+          <seemfa marker="#send_request/4"><c>send_request/4</c></seemfa>,
+          and all request must have been made by the process calling this
+          function.
+	</p>
+        <p>
+          The <c><anno>Label</anno></c> in the response equals the
+          <c><anno>Label</anno></c> associated with the request identifier
+          that the response corresponds to. The <c><anno>Label</anno></c>
+          of a request identifier is associated when
+          <seemfa marker="#reqids_add/3">adding the request id</seemfa>
+          in a request identifier collection, or when sending the request using
+          <seemfa marker="#send_request/4"><c>send_request/4</c></seemfa>.
+        </p>
+        <p>
+          Compared to
+          <seemfa marker="#receive_response/2"><c>receive_response/2</c></seemfa>,
+          the returned result associated with a specific request identifier
+          will be wrapped in a 3-tuple. The first element of this tuple equals
+          the value that would have been produced by <c>receive_response/2</c>,
+          the second element equals the <c><anno>Label</anno></c> associated
+          with the specific request identifier, and the third element
+          <c><anno>NewReqIdCollection</anno></c> is a possibly modified
+          request identifier collection.
+        </p>
+        <p>
+          If <c><anno>ReqIdCollection</anno></c> is empty, the atom
+          <c>no_request</c> will be returned.
+        </p>
+	<p>
+	  <c><anno>Timeout</anno></c> specifies how long to wait for
+          a response. If no response is received within the specified time,
+          the function returns <c>timeout</c>. Assuming that the
+	  server executes on a node supporting aliases (introduced in
+	  OTP 24) all requests identified by <c><anno>ReqIdCollection</anno></c>
+          will also be abandoned. That is, no responses will be received
+          after a timeout. Otherwise, stray responses might be received
+          at a later time.
+	</p>
+	<p>
+	  The difference between <c>receive_response/3</c> and
+          <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>
+          is that <c>receive_response/3</c> abandons the requests at timeout
+          so that potential future responses are ignored, while
+          <c>wait_response/3</c> does not.
+	</p>
+        <p>
+          If <c><anno>Delete</anno></c> equals <c>true</c>, the association
+          with <c><anno>Label</anno></c> will have been deleted from
+          <c><anno>ReqIdCollection</anno></c> in the resulting
+          <c><anno>NewReqIdCollection</anno></c>. If
+          <c><anno>Delete</anno></c> equals <c>false</c>,
+          <c><anno>NewReqIdCollection</anno></c> will equal
+          <c><anno>ReqIdCollection</anno></c>. Note that deleting an
+          association is not for free and that a collection containing
+          already handled requests can still be used by subsequent calls to
+          <c>receive_response/3</c>,
+ 	  <seemfa marker="#check_response/3"><c>check_response/3</c></seemfa>,
+          and
+	  <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>.
+          However, without deleting handled associations, the above calls will
+          not be able to detect when there are no more outstanding requests to
+          handle, so you will have to keep track of this some other way than
+          relying on a <c>no_request</c> return. Note that if you pass a
+          collection only containing associations of already handled or
+          abandoned requests to <c>receive_response/3</c>, it will always block
+          until a timeout determined by <c><anno>Timeout</anno></c> is
+          triggered.
+        </p>
+      </desc>
+    </func>
 
     <func>
       <name name="reply" arity="1" since="OTP 19.0"/>
@@ -1950,20 +2164,98 @@ handle_event(_, _, State, Data) ->
     </func>
 
     <func>
-      <name name="send_request" arity="2" since="OTP-23"/>
-      <fsummary>Send a request to a <c>gen_statem</c>.</fsummary>
+      <name name="reqids_add" arity="3" since="OTP 25.0"/>
+      <fsummary>Save a request identifier.</fsummary>
       <desc>
+	<p>
+          Saves <c><anno>ReqId</anno></c> and associates a <c><anno>Label</anno></c>
+          with the request identifier by adding this information to
+          <c><anno>ReqIdCollection</anno></c> and returning the
+          resulting request identifier collection.
+        </p>
+      </desc>
+    </func>
+
+    <func>
+      <name name="reqids_new" arity="0" since="OTP 25.0"/>
+      <fsummary>Create a new empty request identifier collection.</fsummary>
+      <desc>
+	<p>
+          Returns a new empty request identifier collection. A 
+          request identifier collection can be utilized in order
+          the handle multiple outstanding requests.
+        </p>
         <p>
-	  Sends a request to the <c>gen_statem</c>
-	  <seetype marker="#server_ref"><c><anno>ServerRef</anno></c></seetype>
-	  and returns a handle <c><anno>RequestId</anno></c>.
-	</p>
+          Request identifiers of requests made by
+	  <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>
+          can be saved in a request identifier collection using 
+	  <seemfa marker="#reqids_add/3"><c>reqids_add/3</c></seemfa>.
+          Such a collection of request identifiers can later be used in
+          order to get one response corresponding to a request in the
+          collection by passing the collection as argument to
+          <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>,
+	  <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>,
+	  or,
+	  <seemfa marker="#check_response/3"><c>check_response/3</c></seemfa>.
+        </p>
+        <p>
+          <seemfa marker="#reqids_size/1"><c>reqids_size/1</c></seemfa>
+          can be used to determine the amount of request identifiers in a
+          request identifier collection.
+        </p>
+      </desc>
+    </func>
+
+    <func>
+      <name name="reqids_size" arity="1" since="OTP 25.0"/>
+      <fsummary>Get size of a request identifier collection.</fsummary>
+      <desc>
 	<p>
-	  The return value <c><anno>RequestId</anno></c> shall later be used with
-          <seemfa marker="#receive_response/2"> <c>receive_response/1,2</c></seemfa>,
-          <seemfa marker="#wait_response/2"> <c>wait_response/1,2</c></seemfa>, or
+          Returns the amount of request identifiers saved in
+          <c><anno>ReqIdCollection</anno></c>.
+        </p>
+      </desc>
+    </func>
+
+    <func>
+      <name name="reqids_to_list" arity="1" since="OTP 25.0"/>
+      <fsummary>List a request identifiers.</fsummary>
+      <desc>
+	<p>
+          Returns a list of <c>{<anno>ReqId</anno>, <anno>Label</anno>}</c>
+          tuples which corresponds to all request identifiers with their
+          associated labels present in the <c><anno>ReqIdCollection</anno></c>
+          collection.
+        </p>
+      </desc>
+    </func>
+
+    <func>
+      <name name="send_request" arity="2" since="OTP 23.0"/>
+      <fsummary>Send a request to a <c>gen_statem</c>.</fsummary>
+      <desc>
+        <p>
+	  Sends an asynchronous <c>call</c> request <c><anno>Request</anno></c>
+          to the <c>gen_statem</c> process identified by <c><anno>ServerRef</anno></c>
+          and returns a request identifier <c><anno>ReqId</anno></c>. The return
+          value <c><anno>ReqId</anno></c> shall later be used with
+          <seemfa marker="#receive_response/2"> <c>receive_response/2</c></seemfa>,
+          <seemfa marker="#wait_response/2"> <c>wait_response/2</c></seemfa>, or
 	  <seemfa marker="#check_response/2"> <c>check_response/2</c></seemfa>
-	  to fetch the actual result of the request.
+	  to fetch the actual result of the request. Besides passing
+          the request identifier directly to these functions, it can also be
+          saved in a request identifier collection using 
+	  <seemfa marker="#reqids_add/3"><c>reqids_add/3</c></seemfa>.
+          Such a collection of request identifiers can later be used in
+          order to get one response corresponding to a request in the
+          collection by passing the collection as argument to
+          <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>,
+	  <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>, or
+	  <seemfa marker="#check_response/3"><c>check_response/3</c></seemfa>.
+          If you are about to save the request identifier in a request identifier
+          collection, you may want to consider using
+          <seemfa marker="#send_request/4"><c>send_request/4</c></seemfa>
+          instead.
 	</p>
 	<p>
 	  The call <c>gen_statem:wait_response(gen_statem:send_request(ServerRef,Request), Timeout)</c>
@@ -1993,6 +2285,36 @@ handle_event(_, _, State, Data) ->
       </desc>
     </func>
 
+    <func>
+      <name name="send_request" arity="4" since="OTP 25.0"/>
+      <fsummary>Sends a request to a generic server.</fsummary>
+      <desc>
+	<p>
+          Sends an asynchronous <c>call</c> request <c><anno>Request</anno></c>
+          to the <c>gen_statem</c> process identified by <c><anno>ServerRef</anno></c>.
+          The <c><anno>Label</anno></c> will be associated with the request
+          identifier of the operation and added to the returned request
+          identifier collection <c><anno>NewReqIdCollection</anno></c>.
+          The collection can later be used in order to get one response
+          corresponding to a request in the collection by passing the
+          collection as argument to
+	  <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>,
+	  <seemfa marker="#wait_response/3"><c>wait_response/3</c></seemfa>,
+	  or,
+	  <seemfa marker="#check_response/3"><c>check_response/3</c></seemfa>.
+        </p>
+
+        <p>
+          The same as calling
+          <seemfa marker="#reqids_add/3"><c>gen_statem:reqids_add</c></seemfa>(<seemfa
+          marker="#send_request/2"><c>statem:send_request</c></seemfa><c>(<anno>ServerRef</anno>,
+          <anno>Request</anno>), <anno>Label</anno>,
+          <anno>ReqIdCollection</anno>)</c>, but calling <c>send_request/4</c>
+          is slightly more efficient.
+        </p>
+      </desc>
+    </func>
+
     <func>
       <name name="start" arity="3" since="OTP 19.0"/>
       <name name="start" arity="4" since="OTP 19.0"/>
@@ -2222,12 +2544,24 @@ handle_event(_, _, State, Data) ->
     </func>
 
     <func>
-      <name name="wait_response" arity="1" since="OTP 23.0"/>
-      <name name="wait_response" arity="2" since="OTP 23.0"/>
+      <name name="wait_response" arity="1" clause_i="1" since="OTP 23.0"/>
       <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
+          The same as calling 
+	  <seemfa marker="#receive_response/2"><c>gen_statem:receive_response(ReqId,
+          infinity)</c></seemfa>.
+        </p>
+      </desc>
+    </func>
+    
+    <func>
+      <name name="wait_response" arity="2" since="OTP 23.0"/>
+      <fsummary>Wait or poll for a reply from a server.</fsummary>
+      <desc>
+	<p>
+	  Wait for a response corresponding to the request identifier
+          <c><anno>ReqId</anno></c>. The request must have been made by
 	  <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>
 	  to the <c>gen_statem</c> process. This function must be called
 	  from the same process from which
@@ -2235,11 +2569,8 @@ handle_event(_, _, State, Data) ->
 	  was made.
 	</p>
 	<p>
-	  <c>Timeout</c> is an integer
-	  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
+	  <c><anno>WaitTime</anno></c> specifies how long to wait for
+          a reply. 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.
@@ -2259,13 +2590,90 @@ handle_event(_, _, State, Data) ->
 	</p>
 	<p>
 	  The difference between
-	  <seemfa marker="#receive_response/2"><c>receive_response()</c></seemfa>
-	  and <c>wait_response()</c> is that <c>receive_response()</c>
+	  <seemfa marker="#receive_response/2"><c>receive_response/2</c></seemfa>
+	  and <c>wait_response/2</c> is that <c>receive_response/2</c>
 	  abandons the request at timeout so that a potential future
-	  response is ignored, while <c>wait_response()</c> does not.
+	  response is ignored, while <c>wait_response/2</c> does not.
 	</p>
       </desc>
     </func>
+
+    <func>
+      <name name="wait_response" arity="3" since="OTP 25.0"/>
+      <fsummary>Wait or poll for a response from a server.</fsummary>
+      <desc>
+	<p>
+	  Wait for a response corresponding to a request identifier saved
+          in <c><anno>ReqIdCollection</anno></c>. All request identifiers
+          of <c><anno>ReqIdCollection</anno></c> must correspond to requests
+          that have been made using
+	  <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa> or
+          <seemfa marker="#send_request/4"><c>send_request/4</c></seemfa>,
+          and all request must have been made by the process calling this
+          function.
+	</p>
+        <p>
+          The <c><anno>Label</anno></c> in the response equals the
+          <c><anno>Label</anno></c> associated with the request identifier
+          that the response corresponds to. The <c><anno>Label</anno></c>
+          of a request identifier is associated when
+          <seemfa marker="#reqids_add/3">saving the request id</seemfa> in
+          a request identifier collection, or when sending the request using
+          <seemfa marker="#send_request/4"><c>send_request/4</c></seemfa>.
+        </p>
+        <p>
+          Compared to
+          <seemfa marker="#wait_response/2"><c>wait_response/2</c></seemfa>,
+          the returned result associated with a specific request identifier
+          or an exception associated with a specific request identifier will
+          be wrapped in a 3-tuple. The first element of this tuple equals the
+          value that would have been produced by <c>wait_response/2</c>,
+          the second element equals the <c><anno>Label</anno></c> associated
+          with the specific request identifier, and the third element
+          <c><anno>NewReqIdCollection</anno></c> is a possibly  modified
+          request identifier collection.
+        </p>
+        <p>
+          If <c><anno>ReqIdCollection</anno></c> is empty, <c>no_request</c>
+          will be returned. If no response is received before the
+          <c><anno>WaitTime</anno></c> timeout has triggered, the atom
+          <c>timeout</c> is returned. It is valid to continue waiting for a
+          response as many times as needed up until a response has been received
+          and completed by <c>check_response()</c>, <c>receive_response()</c>,
+          or <c>wait_response()</c>.
+        </p>
+	<p>
+	  The difference between
+          <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>
+	  and <c>wait_response/3</c> is that <c>receive_response/3</c>
+	  abandons requests at timeout so that a potential future
+	  responses are ignored, while <c>wait_response/3</c> does not.
+	</p>
+        <p>
+          If <c><anno>Delete</anno></c> equals <c>true</c>, the association
+          with <c><anno>Label</anno></c> will have been deleted from
+          <c><anno>ReqIdCollection</anno></c> in the resulting
+          <c><anno>NewReqIdCollection</anno></c>. If
+          <c><anno>Delete</anno></c> equals <c>false</c>,
+          <c><anno>NewReqIdCollection</anno></c> will equal
+          <c><anno>ReqIdCollection</anno></c>. Note that deleting an
+          association is not for free and that a collection containing
+          already handled requests can still be used by subsequent calls to
+          <c>wait_response/3</c>,
+ 	  <seemfa marker="#check_response/3"><c>check_response/3</c></seemfa>,
+          and
+	  <seemfa marker="#receive_response/3"><c>receive_response/3</c></seemfa>.
+          However, without deleting handled associations, the above calls will
+          not be able to detect when there are no more outstanding requests to
+          handle, so you will have to keep track of this some other way than
+          relying on a <c>no_request</c> return. Note that if you pass a
+          collection only containing associations of already handled or
+          abandoned requests to <c>wait_response/3</c>, it will always block
+          until a timeout determined by <c><anno>WaitTime</anno></c> is
+          triggered and then return <c>no_reply</c>.
+        </p>
+      </desc>
+    </func>
   </funcs>
 
  
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index 1ebd4ac868..363094fb15 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.erl
@@ -29,23 +29,27 @@
 -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,
-         send_request/3, wait_response/2,
-         receive_response/2, check_response/2,
+         send_request/3, send_request/5,
+         wait_response/2, receive_response/2, check_response/2,
+         wait_response/3, receive_response/3, check_response/3,
+         reqids_new/0, reqids_size/1,
+         reqids_add/3, reqids_to_list/1,
          stop/1, stop/3]).
 
 -export([init_it/6, init_it/7]).
 
 -export([format_status_header/2, format_status/4]).
 
+-define(MAX_INT_TIMEOUT, 4294967295).
 -define(default_timeout, 5000).
 
 -include("logger.hrl").
 
 %%-----------------------------------------------------------------
 
--export_type(
-   [reply_tag/0,
-    request_id/0]).
+-export_type([reply_tag/0,
+              request_id/0,
+              request_id_collection/0]).
 
 -type linkage()    :: 'monitor' | 'link' | 'nolink'.
 -type emgr_name()  :: {'local', atom()}
@@ -73,6 +77,10 @@
 
 -opaque request_id() :: reference().
 
+-opaque request_id_collection() :: map().
+
+-type response_timeout() ::
+        0..?MAX_INT_TIMEOUT | 'infinity' | {abs, integer()}.
 
 %%-----------------------------------------------------------------
 %% Starts a generic process.
@@ -278,11 +286,12 @@ 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,
+-spec send_request(Name::server_ref(), Tag::term(), Request::term()) ->
+          request_id().
+send_request(Process, Tag, Request) when is_pid(Process) ->
+    do_send_request(Process, Tag, Request);
+send_request(Process, Tag, Request) ->
+    Fun = fun(Pid) -> do_send_request(Pid, Tag, Request) end,
     try do_for_proc(Process, Fun)
     catch exit:Reason ->
             %% Make send_request async and fake a down message
@@ -291,62 +300,232 @@ send_request(Process, Label, Request) ->
             Mref
     end.
 
+-spec send_request(Name::server_ref(), Tag::term(), Request::term(),
+                   Label::term(), ReqIdCol::request_id_collection()) ->
+          request_id_collection().
+send_request(Process, Tag, Request, Label, ReqIdCol) when is_map(ReqIdCol) ->
+    maps:put(send_request(Process, Tag, Request), Label, ReqIdCol).
+
 -dialyzer({no_improper_lists, do_send_request/3}).
 
-do_send_request(Process, Label, Request) ->
-    Mref = erlang:monitor(process, Process, [{alias, demonitor}]),
-    erlang:send(Process, {Label, {self(), [alias|Mref]}, Request}, [noconnect]),
-    Mref.
+do_send_request(Process, Tag, Request) ->
+    ReqId = erlang:monitor(process, Process, [{alias, demonitor}]),
+    _ = erlang:send(Process, {Tag, {self(), [alias|ReqId]}, Request}, [noconnect]),
+    ReqId.
 
 %%
 %% 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) ->
+-spec wait_response(ReqId, Timeout) -> Result when
+      ReqId :: request_id(),
+      Timeout :: response_timeout(),
+      Resp :: {reply, Reply::term()} | {error, {Reason::term(), server_ref()}},
+      Result :: Resp | 'timeout'.
+
+wait_response(ReqId, Timeout) ->
+    TMO = timeout_value(Timeout),
     receive
-        {[alias|Mref], Reply} ->
-            erlang:demonitor(Mref, [flush]),
+        {[alias|ReqId], Reply} ->
+            erlang:demonitor(ReqId, [flush]),
             {reply, Reply};
-        {'DOWN', Mref, _, Object, Reason} ->
+        {'DOWN', ReqId, _, Object, Reason} ->
             {error, {Reason, Object}}
-    after Timeout ->
+    after TMO ->
             timeout
     end.
 
--spec receive_response(RequestId::request_id(), timeout()) ->
-        {reply, Reply::term()} | 'timeout' | {error, {term(), server_ref()}}.
-receive_response(Mref, Timeout) when is_reference(Mref) ->
+-spec wait_response(ReqIdCol, Timeout, Delete) -> Result when
+      ReqIdCol :: request_id_collection(),
+      Timeout :: response_timeout(),
+      Delete :: boolean(),
+      Resp :: {reply, Reply::term()} | {error, {Reason::term(), server_ref()}},
+      Result :: {Resp, Label::term(), NewReqIdCol::request_id_collection()} |
+                'no_request' | 'timeout'.
+
+wait_response(ReqIdCol, Timeout, Delete) when map_size(ReqIdCol) == 0,
+                                              is_boolean(Delete) ->
+    _ = timeout_value(Timeout),
+    no_request;
+wait_response(ReqIdCol, Timeout, Delete) when is_map(ReqIdCol),
+                                              is_boolean(Delete) ->
+    TMO = timeout_value(Timeout),
     receive
-        {[alias|Mref], Reply} ->
-            erlang:demonitor(Mref, [flush]),
+        {[alias|ReqId], _} = Msg when is_map_key(ReqId, ReqIdCol) ->
+            collection_result(Msg, ReqIdCol, Delete);
+        {'DOWN', ReqId, _, _, _} = Msg when is_map_key(ReqId, ReqIdCol) ->
+            collection_result(Msg, ReqIdCol, Delete)
+    after TMO ->
+            timeout
+    end.
+
+-spec receive_response(ReqId, Timeout) -> Result when
+      ReqId :: request_id(),
+      Timeout :: response_timeout(),
+      Resp :: {reply, Reply::term()} | {error, {Reason::term(), server_ref()}},
+      Result :: Resp | 'timeout'.
+
+receive_response(ReqId, Timeout) ->
+    TMO = timeout_value(Timeout),
+    receive
+        {[alias|ReqId], Reply} ->
+            erlang:demonitor(ReqId, [flush]),
             {reply, Reply};
-        {'DOWN', Mref, _, Object, Reason} ->
+        {'DOWN', ReqId, _, Object, Reason} ->
             {error, {Reason, Object}}
-    after Timeout ->
-            erlang:demonitor(Mref, [flush]),
+    after TMO ->
+            erlang:demonitor(ReqId, [flush]),
             receive
-                {[alias|Mref], Reply} ->
+                {[alias|ReqId], Reply} ->
                     {reply, Reply}
             after 0 ->
                     timeout
             end
     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) ->
+-spec receive_response(ReqIdCol, Timeout, Delete) -> Result when
+      ReqIdCol :: request_id_collection(),
+      Timeout :: response_timeout(),
+      Delete :: boolean(),
+      Resp :: {reply, Reply::term()} | {error, {Reason::term(), server_ref()}},
+      Result :: {Resp, Label::term(), NewReqIdCol::request_id_collection()}
+              | 'no_request' | 'timeout'.
+
+receive_response(ReqIdCol, Timeout, Delete) when map_size(ReqIdCol) == 0,
+                                                 is_boolean(Delete) ->
+    _ = timeout_value(Timeout),
+    no_request;
+receive_response(ReqIdCol, Timeout, Delete) when is_map(ReqIdCol),
+                                                 is_boolean(Delete) ->
+    TMO = timeout_value(Timeout),
+    receive
+        {[alias|ReqId], _} = Msg when is_map_key(ReqId, ReqIdCol) ->
+            collection_result(Msg, ReqIdCol, Delete);
+        {'DOWN', Mref, _, _, _} = Msg when is_map_key(Mref, ReqIdCol) ->
+            collection_result(Msg, ReqIdCol, Delete)
+    after TMO ->
+            maps:foreach(fun (ReqId, _Label) when is_reference(ReqId) ->
+                                 erlang:demonitor(ReqId, [flush]);
+                             (_, _) ->
+                                 error(badarg)
+                         end, ReqIdCol),
+            flush_responses(ReqIdCol),
+            timeout
+    end.
+
+-spec check_response(Msg::term(), ReqIdOrReqIdCol) -> Result when
+      ReqIdOrReqIdCol :: request_id() | request_id_collection(),
+      ReqIdResp :: {reply, Reply::term()} |
+                   {error, {Reason::term(), server_ref()}},
+      ReqIdColResp :: {{reply, Reply::term()}, Label::term()} |
+                             {{error, {Reason::term(), server_ref()}}, Label::term()},
+      Result :: ReqIdResp | ReqIdColResp | 'no_reply'.
+
+check_response(Msg, ReqId) when is_reference(ReqId) ->
     case Msg of
-        {[alias|Mref], Reply} ->
-            erlang:demonitor(Mref, [flush]),
+        {[alias|ReqId], Reply} ->
+            erlang:demonitor(ReqId, [flush]),
             {reply, Reply};
-        {'DOWN', Mref, _, Object, Reason} ->
+        {'DOWN', ReqId, _, Object, Reason} ->
             {error, {Reason, Object}};
         _ ->
             no_reply
+    end;
+check_response(_, _) ->
+    error(badarg).
+
+-spec check_response(Msg, ReqIdCol, Delete) -> Result when
+      Msg :: term(),
+      ReqIdCol :: request_id_collection(),
+      Delete :: boolean(),
+      Resp :: {reply, Reply::term()} | {error, {Reason::term(), server_ref()}},
+      Result :: {Resp, Label::term(), NewReqIdCol::request_id_collection()}
+              | 'no_request' | 'no_reply'.
+
+check_response(_Msg, ReqIdCol, Delete) when map_size(ReqIdCol) == 0,
+                                            is_boolean(Delete) ->
+    no_request;
+check_response(Msg, ReqIdCol, Delete) when is_map(ReqIdCol),
+                                           is_boolean(Delete) ->
+    case Msg of
+        {[alias|ReqId], _} = Msg when is_map_key(ReqId, ReqIdCol) ->
+            collection_result(Msg, ReqIdCol, Delete);
+        {'DOWN', Mref, _, _, _} = Msg when is_map_key(Mref, ReqIdCol) ->
+            collection_result(Msg, ReqIdCol, Delete);
+        _ ->
+            no_reply
     end.
 
+collection_result({[alias|ReqId], Reply}, ReqIdCol, Delete) ->
+    _ = erlang:demonitor(ReqId, [flush]),
+    collection_result({reply, Reply}, ReqId, ReqIdCol, Delete);
+collection_result({'DOWN', ReqId, _, Object, Reason}, ReqIdCol, Delete) ->
+    collection_result({error, {Reason, Object}}, ReqId, ReqIdCol, Delete).
+
+collection_result(Resp, ReqId, ReqIdCol, false) ->
+    {Resp, maps:get(ReqId, ReqIdCol), ReqIdCol};
+collection_result(Resp, ReqId, ReqIdCol, true) ->
+    {Label, NewReqIdCol} = maps:take(ReqId, ReqIdCol),
+    {Resp, Label, NewReqIdCol}.
+
+flush_responses(ReqIdCol) ->
+    receive
+        {[alias|Mref], _Reply} when is_map_key(Mref, ReqIdCol) ->
+            flush_responses(ReqIdCol)
+    after 0 ->
+            ok
+    end.
+
+timeout_value(infinity) ->
+    infinity;
+timeout_value(Timeout) when 0 =< Timeout, Timeout =< ?MAX_INT_TIMEOUT ->
+    Timeout;
+timeout_value({abs, Timeout}) when is_integer(Timeout) ->
+    case Timeout - erlang:monotonic_time(millisecond) of
+        TMO when TMO < 0 ->
+            0;
+        TMO when TMO > ?MAX_INT_TIMEOUT ->
+            error(badarg);
+        TMO ->
+            TMO
+    end;
+timeout_value(_) ->
+    error(badarg).
+
+-spec reqids_new() ->
+          NewReqIdCol::request_id_collection().
+
+reqids_new() ->
+    maps:new().
+
+-spec reqids_size(request_id_collection()) ->
+          non_neg_integer().
+reqids_size(ReqIdCol) when is_map(ReqIdCol) ->
+    maps:size(ReqIdCol);
+reqids_size(_) ->
+    error(badarg).
+
+-spec reqids_add(ReqId::request_id(), Label::term(),
+                 ReqIdCol::request_id_collection()) ->
+          NewReqIdCol::request_id_collection().
+
+reqids_add(ReqId, _, ReqIdCol) when is_reference(ReqId),
+                                    is_map_key(ReqId, ReqIdCol) ->
+    error(badarg);
+reqids_add(ReqId, Label, ReqIdCol) when is_reference(ReqId),
+                                        is_map(ReqIdCol) ->
+    maps:put(ReqId, Label, ReqIdCol);
+reqids_add(_, _, _) ->
+    error(badarg).
+
+-spec reqids_to_list(ReqIdCol::request_id_collection()) ->
+          [{ReqId::request_id(), Label::term()}].
+
+reqids_to_list(ReqIdCol) when is_map(ReqIdCol) ->
+    maps:to_list(ReqIdCol);
+reqids_to_list(_) ->
+    error(badarg).
+
 %%
 %% Send a reply to the client.
 %%
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index afb63c5b28..3f5737f304 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -43,7 +43,11 @@
 	 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,
-         send_request/3, wait_response/2, receive_response/2, check_response/2,
+         send_request/3, send_request/5,
+         wait_response/2, receive_response/2, check_response/2,
+         wait_response/3, receive_response/3, check_response/3,
+         reqids_new/0, reqids_size/1,
+         reqids_add/3, reqids_to_list/1,
          wake_hib/5]).
 
 -export([init_it/6,
@@ -58,7 +62,7 @@
 -export([format_log/1, format_log/2]).
 
 -export_type([handler/0, handler_args/0, add_handler_ret/0,
-              del_handler_ret/0, request_id/0]).
+              del_handler_ret/0, request_id/0, request_id_collection/0]).
 
 -record(handler, {module             :: atom(),
 		  id = false,
@@ -150,6 +154,11 @@
 
 -opaque request_id() :: gen:request_id().
 
+-opaque request_id_collection() :: gen:request_id_collection().
+
+-type response_timeout() ::
+        timeout() | {abs, integer()}.
+
 %%---------------------------------------------------------------------------
 
 -define(NO_CALLBACK, 'no callback module').
@@ -243,32 +252,182 @@ 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 send_request(EventMgrRef::emgr_ref(), Handler::handler(), Request::term()) ->
+          ReqId::request_id().
+send_request(M, Handler, Request) ->
+    try
+        gen:send_request(M, self(), {call, Handler, Request})
+    catch
+        error:badarg ->
+            error(badarg, [M, Handler, Request])
+    end.
 
--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
+-spec send_request(EventMgrRef::emgr_ref(),
+                   Handler::handler(),
+                   Request::term(),
+                   Label::term(),
+                   ReqIdCollection::request_id_collection()) ->
+          NewReqIdCollection::request_id_collection().
+send_request(M, Handler, Request, Label, ReqIdCol) ->
+    try
+        gen:send_request(M, self(), {call, Handler, Request}, Label, ReqIdCol)
+    catch
+        error:badarg ->
+            error(badarg, [M, Handler, Request, Label, ReqIdCol])
+    end.
+
+-spec wait_response(ReqId, WaitTime) -> Result when
+      ReqId :: request_id(),
+      WaitTime :: response_timeout(),
+      Response :: {reply, Reply::term()}
+                | {error, {Reason::term(), emgr_ref()}},
+      Result :: Response | 'timeout'.
+
+wait_response(ReqId, WaitTime) ->
+    try gen:wait_response(ReqId, WaitTime) of
         {reply, {error, _} = Err} -> Err;
         Return -> Return
+    catch
+        error:badarg ->
+            error(badarg, [ReqId, WaitTime])
     end.
 
--spec receive_response(RequestId::request_id(), timeout()) ->
-        {reply, Reply::term()} | 'timeout' | {error, {Reason::term(), emgr_ref()}}.
-receive_response(RequestId, Timeout) ->
-    case gen:receive_response(RequestId, Timeout) of
+-spec wait_response(ReqIdCollection, WaitTime, Delete) -> Result when
+      ReqIdCollection :: request_id_collection(),
+      WaitTime :: response_timeout(),
+      Delete :: boolean(),
+      Response :: {reply, Reply::term()} |
+                  {error, {Reason::term(), emgr_ref()}},
+      Result :: {Response,
+                 Label::term(),
+                 NewReqIdCollection::request_id_collection()} |
+                'no_request' |
+                'timeout'.
+
+wait_response(ReqIdCol, WaitTime, Delete) ->
+    try gen:wait_response(ReqIdCol, WaitTime, Delete) of
+        {{reply, {error, _} = Err}, Label, NewReqIdCol} ->
+            {Err, Label, NewReqIdCol};
+        Return ->
+            Return
+    catch
+        error:badarg ->
+            error(badarg, [ReqIdCol, WaitTime, Delete])
+    end.
+
+-spec receive_response(ReqId, Timeout) -> Result when
+      ReqId :: request_id(),
+      Timeout :: response_timeout(),
+      Response :: {reply, Reply::term()} |
+                  {error, {Reason::term(), emgr_ref()}},
+      Result :: Response | 'timeout'.
+
+receive_response(ReqId, Timeout) ->
+    try gen:receive_response(ReqId, Timeout) of
         {reply, {error, _} = Err} -> Err;
         Return -> Return
+    catch
+        error:badarg ->
+            error(badarg, [ReqId, Timeout])
     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
+-spec receive_response(ReqIdCollection, Timeout, Delete) -> Result when
+      ReqIdCollection :: request_id_collection(),
+      Timeout :: response_timeout(),
+      Delete :: boolean(),
+      Response :: {reply, Reply::term()} |
+                  {error, {Reason::term(), emgr_ref()}},
+      Result :: {Response,
+                 Label::term(),
+                 NewReqIdCollection::request_id_collection()} |
+                'no_request' |
+                'timeout'.
+
+receive_response(ReqIdCol, Timeout, Delete) ->
+    try gen:receive_response(ReqIdCol, Timeout, Delete) of
+        {{reply, {error, _} = Err}, Label, NewReqIdCol} ->
+            {Err, Label, NewReqIdCol};
+        Return ->
+            Return
+    catch
+        error:badarg ->
+            error(badarg, [ReqIdCol, Timeout, Delete])
+    end.
+
+-spec check_response(Msg, ReqId) -> Result when
+      Msg :: term(),
+      ReqId :: request_id(),
+      Response :: {reply, Reply::term()} |
+                  {error, {Reason::term(), emgr_ref()}},
+      Result :: Response | 'no_reply'.
+
+check_response(Msg, ReqId) ->
+    try gen:check_response(Msg, ReqId) of
         {reply, {error, _} = Err} -> Err;
         Return -> Return
+    catch
+        error:badarg ->
+            error(badarg, [Msg, ReqId])
+    end.
+
+-spec check_response(Msg, ReqIdCollection, Delete) -> Result when
+      Msg :: term(),
+      ReqIdCollection :: request_id_collection(),
+      Delete :: boolean(),
+      Response :: {reply, Reply::term()} |
+                  {error, {Reason::term(), emgr_ref()}},
+      Result :: {Response,
+                 Label::term(),
+                 NewReqIdCollection::request_id_collection()} |
+                'no_request' |
+                'no_reply'.
+
+check_response(Msg, ReqIdCol, Delete) ->
+    try gen:check_response(Msg, ReqIdCol, Delete) of
+        {{reply, {error, _} = Err}, Label, NewReqIdCol} ->
+            {Err, Label, NewReqIdCol};
+        Return ->
+            Return
+    catch
+        error:badarg ->
+            error(badarg, [Msg, ReqIdCol, Delete])
+    end.
+
+-spec reqids_new() ->
+          NewReqIdCollection::request_id_collection().
+
+reqids_new() ->
+    gen:reqids_new().
+
+-spec reqids_size(ReqIdCollection::request_id_collection()) ->
+          non_neg_integer().
+
+reqids_size(ReqIdCollection) ->
+    try
+        gen:reqids_size(ReqIdCollection)
+    catch
+        error:badarg -> error(badarg, [ReqIdCollection])
+    end.
+
+-spec reqids_add(ReqId::request_id(), Label::term(),
+                 ReqIdCollection::request_id_collection()) ->
+          NewReqIdCollection::request_id_collection().
+
+reqids_add(ReqId, Label, ReqIdCollection) ->
+    try
+        gen:reqids_add(ReqId, Label, ReqIdCollection)
+    catch
+        error:badarg -> error(badarg, [ReqId, Label, ReqIdCollection])
+    end.
+
+-spec reqids_to_list(ReqIdCollection::request_id_collection()) ->
+          [{ReqId::request_id(), Label::term()}].
+
+reqids_to_list(ReqIdCollection) ->
+    try
+        gen:reqids_to_list(ReqIdCollection)
+    catch
+        error:badarg -> error(badarg, [ReqIdCollection])
     end.
 
 -spec delete_handler(emgr_ref(), handler(), term()) -> term().
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index 6e26d5270a..01f6d67663 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -97,8 +97,11 @@
          start_monitor/3, start_monitor/4,
 	 stop/1, stop/3,
 	 call/2, call/3,
-         send_request/2, wait_response/2,
-         receive_response/2, check_response/2,
+         send_request/2, send_request/4,
+         wait_response/2, receive_response/2, check_response/2,
+         wait_response/3, receive_response/3, check_response/3,
+         reqids_new/0, reqids_size/1,
+         reqids_add/3, reqids_to_list/1,
 	 cast/2, reply/2,
 	 abcast/2, abcast/3,
 	 multi_call/2, multi_call/3, multi_call/4,
@@ -123,7 +126,8 @@
 -export_type(
    [from/0,
     reply_tag/0,
-    request_id/0]).
+    request_id/0,
+    request_id_collection/0]).
 
 -export_type(
    [server_name/0,
@@ -195,6 +199,11 @@
 
 -opaque request_id() :: gen:request_id().
 
+-opaque request_id_collection() :: gen:request_id_collection().
+
+-type response_timeout() ::
+        timeout() | {abs, integer()}.
+
 %%%  -----------------------------------------------------------------
 %%% Starts a generic server.
 %%% start(Mod, Args, Options)
@@ -373,45 +382,172 @@ call(ServerRef, Request, Timeout) ->
 %% used with wait_response/2 or check_response/2 to fetch the
 %% result of the request.
 
--spec send_request(
-        ServerRef :: server_ref(),
-        Request   :: term()
-       ) ->
-                          RequestId :: request_id().
+-spec send_request(ServerRef::server_ref(), Request::term()) ->
+          ReqId::request_id().
+
 send_request(ServerRef, Request) ->
-    gen:send_request(ServerRef, '$gen_call', Request).
-
--spec wait_response(
-        RequestId :: request_id(),
-        Timeout   :: timeout()) ->
-                           {reply, Reply :: term()} |
-                           'timeout' |
-                           {error,
-                            {Reason :: term(), ServerRef :: server_ref()}}.
-wait_response(RequestId, Timeout) ->
-    gen:wait_response(RequestId, Timeout).
-
--spec receive_response(
-        RequestId :: request_id(),
-        Timeout   :: timeout()
-       ) ->
-                              {reply, Reply :: term()} |
-                              'timeout' |
-                              {error,
-                               {Reason :: term(), ServerRef :: server_ref()}}.
-receive_response(RequestId, Timeout) ->
-    gen:receive_response(RequestId, Timeout).
-
--spec check_response(
-        Msg       :: term(),
-        RequestId :: request_id()
-       ) ->
-                            {reply, Reply :: term()} |
-                            'no_reply' |
-                            {error,
-                             {Reason :: term(), ServerRef :: server_ref()}}.
-check_response(Msg, RequestId) ->
-    gen:check_response(Msg, RequestId).
+    try
+        gen:send_request(ServerRef, '$gen_call', Request)
+    catch
+        error:badarg ->
+            error(badarg, [ServerRef, Request])
+    end.
+
+-spec send_request(ServerRef::server_ref(),
+                   Request::term(),
+                   Label::term(),
+                   ReqIdCollection::request_id_collection()) ->
+          NewReqIdCollection::request_id_collection().
+
+send_request(ServerRef, Request, Label, ReqIdCol) ->
+    try
+        gen:send_request(ServerRef, '$gen_call', Request, Label, ReqIdCol)
+    catch
+        error:badarg ->
+            error(badarg, [ServerRef, Request, Label, ReqIdCol])
+    end.
+
+-spec wait_response(ReqId, WaitTime) -> Result when
+      ReqId :: request_id(),
+      WaitTime :: response_timeout(),
+      Response :: {reply, Reply::term()}
+                | {error, {Reason::term(), server_ref()}},
+      Result :: Response | 'timeout'.
+
+wait_response(ReqId, WaitTime) ->
+    try
+        gen:wait_response(ReqId, WaitTime)
+    catch
+        error:badarg ->
+            error(badarg, [ReqId, WaitTime])
+    end.
+
+-spec wait_response(ReqIdCollection, WaitTime, Delete) -> Result when
+      ReqIdCollection :: request_id_collection(),
+      WaitTime :: response_timeout(),
+      Delete :: boolean(),
+      Response :: {reply, Reply::term()} |
+                  {error, {Reason::term(), server_ref()}},
+      Result :: {Response,
+                 Label::term(),
+                 NewReqIdCollection::request_id_collection()} |
+                'no_request' |
+                'timeout'.
+
+wait_response(ReqIdCol, WaitTime, Delete) ->
+    try
+        gen:wait_response(ReqIdCol, WaitTime, Delete)
+    catch
+        error:badarg ->
+            error(badarg, [ReqIdCol, WaitTime, Delete])
+    end.
+
+-spec receive_response(ReqId, Timeout) -> Result when
+      ReqId :: request_id(),
+      Timeout :: response_timeout(),
+      Response :: {reply, Reply::term()} |
+                  {error, {Reason::term(), server_ref()}},
+      Result :: Response | 'timeout'.
+
+receive_response(ReqId, Timeout) ->
+    try
+        gen:receive_response(ReqId, Timeout)
+    catch
+        error:badarg ->
+            error(badarg, [ReqId, Timeout])
+    end.
+
+-spec receive_response(ReqIdCollection, Timeout, Delete) -> Result when
+      ReqIdCollection :: request_id_collection(),
+      Timeout :: response_timeout(),
+      Delete :: boolean(),
+      Response :: {reply, Reply::term()} |
+                  {error, {Reason::term(), server_ref()}},
+      Result :: {Response,
+                 Label::term(),
+                 NewReqIdCollection::request_id_collection()} |
+                'no_request' |
+                'timeout'.
+
+receive_response(ReqIdCol, Timeout, Delete) ->
+    try
+        gen:receive_response(ReqIdCol, Timeout, Delete)
+    catch
+        error:badarg ->
+            error(badarg, [ReqIdCol, Timeout, Delete])
+    end.
+
+-spec check_response(Msg, ReqId) -> Result when
+      Msg :: term(),
+      ReqId :: request_id(),
+      Response :: {reply, Reply::term()} |
+                  {error, {Reason::term(), server_ref()}},
+      Result :: Response | 'no_reply'.
+
+check_response(Msg, ReqId) ->
+    try
+        gen:check_response(Msg, ReqId)
+    catch
+        error:badarg ->
+            error(badarg, [Msg, ReqId])
+    end.
+
+-spec check_response(Msg, ReqIdCollection, Delete) -> Result when
+      Msg :: term(),
+      ReqIdCollection :: request_id_collection(),
+      Delete :: boolean(),
+      Response :: {reply, Reply::term()} |
+                  {error, {Reason::term(), server_ref()}},
+      Result :: {Response,
+                 Label::term(),
+                 NewReqIdCollection::request_id_collection()} |
+                'no_request' |
+                'no_reply'.
+
+check_response(Msg, ReqIdCol, Delete) ->
+    try
+        gen:check_response(Msg, ReqIdCol, Delete)
+    catch
+        error:badarg ->
+            error(badarg, [Msg, ReqIdCol, Delete])
+    end.
+
+-spec reqids_new() ->
+          NewReqIdCollection::request_id_collection().
+
+reqids_new() ->
+    gen:reqids_new().
+
+-spec reqids_size(ReqIdCollection::request_id_collection()) ->
+          non_neg_integer().
+
+reqids_size(ReqIdCollection) ->
+    try
+        gen:reqids_size(ReqIdCollection)
+    catch
+        error:badarg -> error(badarg, [ReqIdCollection])
+    end.
+
+-spec reqids_add(ReqId::request_id(), Label::term(),
+                 ReqIdCollection::request_id_collection()) ->
+          NewReqIdCollection::request_id_collection().
+
+reqids_add(ReqId, Label, ReqIdCollection) ->
+    try
+        gen:reqids_add(ReqId, Label, ReqIdCollection)
+    catch
+        error:badarg -> error(badarg, [ReqId, Label, ReqIdCollection])
+    end.
+
+-spec reqids_to_list(ReqIdCollection::request_id_collection()) ->
+          [{ReqId::request_id(), Label::term()}].
+
+reqids_to_list(ReqIdCollection) ->
+    try
+        gen:reqids_to_list(ReqIdCollection)
+    catch
+        error:badarg -> error(badarg, [ReqIdCollection])
+    end.
 
 %% -----------------------------------------------------------------
 %% Make a cast to a generic server.
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index 51ac904ee9..f82965d925 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -32,8 +32,12 @@
     start_monitor/3,start_monitor/4,
     stop/1,stop/3,
     cast/2,call/2,call/3,
-    send_request/2,wait_response/1,wait_response/2,
-    receive_response/1,receive_response/2,check_response/2,
+    send_request/2, send_request/4,
+    wait_response/1, wait_response/2, wait_response/3,
+    receive_response/1, receive_response/2, receive_response/3,
+    check_response/2, check_response/3,
+    reqids_new/0, reqids_size/1,
+    reqids_add/3, reqids_to_list/1,
     enter_loop/4,enter_loop/5,enter_loop/6,
     reply/1,reply/2]).
 
@@ -72,7 +76,8 @@
     reply_action/0,
     enter_action/0,
     action/0,
-    request_id/0
+    request_id/0,
+    request_id_collection/0
    ]).
 %% Old types, not advertised
 -export_type(
@@ -284,6 +289,11 @@
 
 -opaque request_id() :: gen:request_id().
 
+-opaque request_id_collection() :: gen:request_id_collection().
+
+-type response_timeout() ::
+        timeout() | {abs, integer()}.
+
 %% The state machine init function.  It is called only once and
 %% the server is not running until this function has returned
 %% an {ok, ...} tuple.  Thereafter the state callbacks are called
@@ -625,34 +635,189 @@ call(ServerRef, Request, Timeout) ->
     call_clean(ServerRef, Request, Timeout, Timeout).
 
 -spec send_request(ServerRef::server_ref(), Request::term()) ->
-        RequestId::request_id().
+        ReqId::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 receive_response(RequestId::request_id()) ->
-        {reply, Reply::term()} | {error, {term(), server_ref()}}.
-receive_response(RequestId) ->
-    gen:receive_response(RequestId, infinity).
-
--spec receive_response(RequestId::request_id(), timeout()) ->
-        {reply, Reply::term()} | 'timeout' | {error, {term(), server_ref()}}.
-receive_response(RequestId, Timeout) ->
-    gen:receive_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).
+    try
+        gen:send_request(Name, '$gen_call', Request)
+    catch
+        error:badarg ->
+            error(badarg, [Name, Request])
+    end.
+
+-spec send_request(ServerRef::server_ref(),
+                   Request::term(),
+                   Label::term(),
+                   ReqIdCollection::request_id_collection()) ->
+          NewReqIdCollection::request_id_collection().
+
+send_request(ServerRef, Request, Label, ReqIdCol) ->
+    try
+        gen:send_request(ServerRef, '$gen_call', Request, Label, ReqIdCol)
+    catch
+        error:badarg ->
+            error(badarg, [ServerRef, Request, Label, ReqIdCol])
+    end.
+
+
+-spec wait_response(ReqId) -> Result when
+      ReqId :: request_id(),
+      Response :: {reply, Reply::term()}
+                | {error, {Reason::term(), server_ref()}},
+      Result :: Response | 'timeout'.
+
+wait_response(ReqId) ->
+    wait_response(ReqId, infinity).
+
+-spec wait_response(ReqId, WaitTime) -> Result when
+      ReqId :: request_id(),
+      WaitTime :: response_timeout(),
+      Response :: {reply, Reply::term()}
+                | {error, {Reason::term(), server_ref()}},
+      Result :: Response | 'timeout'.
+
+wait_response(ReqId, WaitTime) ->
+    try
+        gen:wait_response(ReqId, WaitTime)
+    catch
+        error:badarg ->
+            error(badarg, [ReqId, WaitTime])
+    end.
+
+-spec wait_response(ReqIdCollection, WaitTime, Delete) -> Result when
+      ReqIdCollection :: request_id_collection(),
+      WaitTime :: response_timeout(),
+      Delete :: boolean(),
+      Response :: {reply, Reply::term()} |
+                  {error, {Reason::term(), server_ref()}},
+      Result :: {Response,
+                 Label::term(),
+                 NewReqIdCollection::request_id_collection()} |
+                'no_request' |
+                'timeout'.
+
+wait_response(ReqIdCol, WaitTime, Delete) ->
+    try
+        gen:wait_response(ReqIdCol, WaitTime, Delete)
+    catch
+        error:badarg ->
+            error(badarg, [ReqIdCol, WaitTime, Delete])
+    end.
+
+-spec receive_response(ReqId) -> Result when
+      ReqId :: request_id(),
+      Response :: {reply, Reply::term()} |
+                  {error, {Reason::term(), server_ref()}},
+      Result :: Response | 'timeout'.
+
+receive_response(ReqId) ->
+    receive_response(ReqId, infinity).
+
+-spec receive_response(ReqId, Timeout) -> Result when
+      ReqId :: request_id(),
+      Timeout :: response_timeout(),
+      Response :: {reply, Reply::term()} |
+                  {error, {Reason::term(), server_ref()}},
+      Result :: Response | 'timeout'.
+
+receive_response(ReqId, Timeout) ->
+    try
+        gen:receive_response(ReqId, Timeout)
+    catch
+        error:badarg ->
+            error(badarg, [ReqId, Timeout])
+    end.
+
+-spec receive_response(ReqIdCollection, Timeout, Delete) -> Result when
+      ReqIdCollection :: request_id_collection(),
+      Timeout :: response_timeout(),
+      Delete :: boolean(),
+      Response :: {reply, Reply::term()} |
+                  {error, {Reason::term(), server_ref()}},
+      Result :: {Response,
+                 Label::term(),
+                 NewReqIdCollection::request_id_collection()} |
+                'no_request' |
+                'timeout'.
+
+receive_response(ReqIdCol, Timeout, Delete) ->
+    try
+        gen:receive_response(ReqIdCol, Timeout, Delete)
+    catch
+        error:badarg ->
+            error(badarg, [ReqIdCol, Timeout, Delete])
+    end.
+
+-spec check_response(Msg, ReqId) -> Result when
+      Msg :: term(),
+      ReqId :: request_id(),
+      Response :: {reply, Reply::term()} |
+                  {error, {Reason::term(), server_ref()}},
+      Result :: Response | 'no_reply'.
+
+check_response(Msg, ReqId) ->
+    try
+        gen:check_response(Msg, ReqId)
+    catch
+        error:badarg ->
+            error(badarg, [Msg, ReqId])
+    end.
+
+-spec check_response(Msg, ReqIdCollection, Delete) -> Result when
+      Msg :: term(),
+      ReqIdCollection :: request_id_collection(),
+      Delete :: boolean(),
+      Response :: {reply, Reply::term()} |
+                  {error, {Reason::term(), server_ref()}},
+      Result :: {Response,
+                 Label::term(),
+                 NewReqIdCollection::request_id_collection()} |
+                'no_request' |
+                'no_reply'.
+
+check_response(Msg, ReqIdCol, Delete) ->
+    try
+        gen:check_response(Msg, ReqIdCol, Delete)
+    catch
+        error:badarg ->
+            error(badarg, [Msg, ReqIdCol, Delete])
+    end.
+
+-spec reqids_new() ->
+          NewReqIdCollection::request_id_collection().
+
+reqids_new() ->
+    gen:reqids_new().
+
+-spec reqids_size(ReqIdCollection::request_id_collection()) ->
+          non_neg_integer().
+
+reqids_size(ReqIdCollection) ->
+    try
+        gen:reqids_size(ReqIdCollection)
+    catch
+        error:badarg -> error(badarg, [ReqIdCollection])
+    end.
+
+-spec reqids_add(ReqId::request_id(), Label::term(),
+                 ReqIdCollection::request_id_collection()) ->
+          NewReqIdCollection::request_id_collection().
+
+reqids_add(ReqId, Label, ReqIdCollection) ->
+    try
+        gen:reqids_add(ReqId, Label, ReqIdCollection)
+    catch
+        error:badarg -> error(badarg, [ReqId, Label, ReqIdCollection])
+    end.
+
+-spec reqids_to_list(ReqIdCollection::request_id_collection()) ->
+          [{ReqId::request_id(), Label::term()}].
+
+reqids_to_list(ReqIdCollection) ->
+    try
+        gen:reqids_to_list(ReqIdCollection)
+    catch
+        error:badarg -> error(badarg, [ReqIdCollection])
+    end.
 
 %% Reply from a state machine callback to whom awaits in call/2
 -spec reply([reply_action()] | reply_action()) -> ok.
diff --git a/lib/stdlib/test/dummy_h.erl b/lib/stdlib/test/dummy_h.erl
index f75c580be1..4fffcd86d8 100644
--- a/lib/stdlib/test/dummy_h.erl
+++ b/lib/stdlib/test/dummy_h.erl
@@ -63,6 +63,9 @@ handle_call(hibernate, _State) ->
 handle_call(hibernate_later, _State) ->
     timer:send_after(1000,sleep),
     {ok,later,[]};
+handle_call({delayed_answer, T}, State) ->
+    receive after T -> ok end,
+    {ok, delayed, State};
 handle_call(_Query, State) ->
     {ok, ok, State}.
 
diff --git a/lib/stdlib/test/gen_event_SUITE.erl b/lib/stdlib/test/gen_event_SUITE.erl
index 6dbadd3602..65c5a83c9e 100644
--- a/lib/stdlib/test/gen_event_SUITE.erl
+++ b/lib/stdlib/test/gen_event_SUITE.erl
@@ -32,7 +32,9 @@
          start_opt/1,
          undef_init/1, undef_handle_call/1, undef_handle_event/1,
          undef_handle_info/1, undef_code_change/1, undef_terminate/1,
-         undef_in_terminate/1, format_log_1/1, format_log_2/1]).
+         undef_in_terminate/1, format_log_1/1, format_log_2/1,
+         send_request_receive_reqid_collection/1, send_request_wait_reqid_collection/1,
+         send_request_check_reqid_collection/1]).
 
 suite() -> [{ct_hooks,[ts_install_cth]}].
 
@@ -41,7 +43,9 @@ all() ->
      call_format_status, call_format_status_anon, error_format_status,
      get_state, replace_state,
      start_opt, {group, undef_callbacks}, undef_in_terminate,
-     format_log_1, format_log_2].
+     format_log_1, format_log_2,
+     send_request_receive_reqid_collection, send_request_wait_reqid_collection,
+     send_request_check_reqid_collection].
 
 groups() ->
     [{test_all, [],
@@ -1522,3 +1526,324 @@ format_log_2(_Config) ->
 
 flatten_format_log(Report, Format) ->
     lists:flatten(gen_event:format_log(Report, Format)).
+
+
+send_request_receive_reqid_collection(Config) when is_list(Config) ->
+    {ok, Pid1} = gen_event:start(),
+    ok = gen_event:add_handler(Pid1, dummy_h, [self()]),
+    [dummy_h] = gen_event:which_handlers(Pid1),
+    {ok, Pid2} = gen_event:start(),
+    ok = gen_event:add_handler(Pid2, dummy_h, [self()]),
+    [dummy_h] = gen_event:which_handlers(Pid2),
+    {ok, Pid3} = gen_event:start(),
+    ok = gen_event:add_handler(Pid3, dummy_h, [self()]),
+    [dummy_h] = gen_event:which_handlers(Pid3),
+    send_request_receive_reqid_collection(Pid1, Pid2, Pid3),
+    send_request_receive_reqid_collection_timeout(Pid1, Pid2, Pid3),
+    send_request_receive_reqid_collection_error(Pid1, Pid2, Pid3),
+    ok = gen_event:stop(Pid1),
+    try gen_event:stop(Pid2) catch exit:noproc -> ok end,
+    ok = gen_event:stop(Pid3).
+
+send_request_receive_reqid_collection(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_event:send_request(Pid1, dummy_h, hejsan),
+
+    ReqIdC0 = gen_event:reqids_new(),
+
+    ReqId1 = gen_event:send_request(Pid1, dummy_h, {delayed_answer,400}),
+    ReqIdC1 = gen_event:reqids_add(ReqId1, req1, ReqIdC0),
+    1 = gen_event:reqids_size(ReqIdC1),
+
+    ReqIdC2 = gen_event:send_request(Pid2, dummy_h, {delayed_answer,1}, req2, ReqIdC1),
+    2 = gen_event:reqids_size(ReqIdC2),
+
+    ReqIdC3 = gen_event:send_request(Pid3, dummy_h, {delayed_answer,200}, req3, ReqIdC2),
+    3 = gen_event:reqids_size(ReqIdC3),
+        
+    {{reply, delayed}, req2, ReqIdC4} = gen_event:receive_response(ReqIdC3, infinity, true),
+    2 = gen_event:reqids_size(ReqIdC4),
+
+    {{reply, delayed}, req3, ReqIdC5} = gen_event:receive_response(ReqIdC4, 5678, true),
+    1 = gen_event:reqids_size(ReqIdC5),
+
+    {{reply, delayed}, req1, ReqIdC6} = gen_event:receive_response(ReqIdC5, 5000, true),
+    0 = gen_event:reqids_size(ReqIdC6),
+
+    no_request = gen_event:receive_response(ReqIdC6, 5000, true),
+
+    {reply, {ok, hejhopp}} = gen_event:receive_response(ReqId0, infinity),
+
+    ok.
+
+send_request_receive_reqid_collection_timeout(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_event:send_request(Pid1, dummy_h, hejsan),
+
+    ReqIdC0 = gen_event:reqids_new(),
+
+    ReqId1 = gen_event:send_request(Pid1, dummy_h, {delayed_answer,1000}),
+    ReqIdC1 = gen_event:reqids_add(ReqId1, req1, ReqIdC0),
+
+    ReqIdC2 = gen_event:send_request(Pid2, dummy_h, {delayed_answer,1}, req2, ReqIdC1),
+
+    ReqId3 = gen_event:send_request(Pid3, dummy_h, {delayed_answer,500}),
+    ReqIdC3 = gen_event:reqids_add(ReqId3, req3, ReqIdC2),
+
+    Deadline = erlang:monotonic_time(millisecond) + 100,
+    
+    {{reply, delayed}, req2, ReqIdC4} = gen_event:receive_response(ReqIdC3, {abs, Deadline}, true),
+    2 = gen_event:reqids_size(ReqIdC4),
+
+    timeout = gen_event:receive_response(ReqIdC4, {abs, Deadline}, true),
+
+    Abandoned = lists:sort([{ReqId1, req1}, {ReqId3, req3}]),
+    Abandoned = lists:sort(gen_event:reqids_to_list(ReqIdC4)),
+
+    %% Make sure requests were abandoned...
+    timeout = gen_event:receive_response(ReqIdC4, {abs, Deadline+1000}, true),
+
+    {reply, {ok, hejhopp}} = gen_event:receive_response(ReqId0, infinity),
+
+    ok.
+    
+send_request_receive_reqid_collection_error(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_event:send_request(Pid1, dummy_h, hejsan),
+
+    ReqIdC0 = gen_event:reqids_new(),
+
+    ReqId1 = gen_event:send_request(Pid1, dummy_h, {delayed_answer,400}),
+    ReqIdC1 = gen_event:reqids_add(ReqId1, req1, ReqIdC0),
+    try
+        nope = gen_event:reqids_add(ReqId1, req2, ReqIdC1)
+    catch
+        error:badarg -> ok
+    end,
+
+    unlink(Pid2),
+    exit(Pid2, kill),
+    ReqIdC2 = gen_event:send_request(Pid2, dummy_h, {delayed_answer,1}, req2, ReqIdC1),
+    ReqIdC3 = gen_event:send_request(Pid3, dummy_h, {delayed_answer,200}, req3, ReqIdC2),
+    ReqIdC4 = gen_event:send_request(Pid1, bad_h, hejsan, req4, ReqIdC3),
+    4 = gen_event:reqids_size(ReqIdC4),
+    
+    {{error, {noproc, _}}, req2, ReqIdC5} = gen_event:receive_response(ReqIdC4, 2000, true),
+    3 = gen_event:reqids_size(ReqIdC5),
+
+    {{reply, delayed}, req3, ReqIdC5} = gen_event:receive_response(ReqIdC5, infinity, false),
+    {{reply, delayed}, req1, ReqIdC5} = gen_event:receive_response(ReqIdC5, infinity, false),
+    {{error, bad_module}, req4, ReqIdC5} = gen_event:wait_response(ReqIdC5, infinity, false),
+
+    {reply, {ok, hejhopp}} = gen_event:receive_response(ReqId0, infinity),
+
+    ok.
+
+send_request_wait_reqid_collection(Config) when is_list(Config) ->
+    {ok, Pid1} = gen_event:start(),
+    ok = gen_event:add_handler(Pid1, dummy_h, [self()]),
+    [dummy_h] = gen_event:which_handlers(Pid1),
+    {ok, Pid2} = gen_event:start(),
+    ok = gen_event:add_handler(Pid2, dummy_h, [self()]),
+    [dummy_h] = gen_event:which_handlers(Pid2),
+    {ok, Pid3} = gen_event:start(),
+    ok = gen_event:add_handler(Pid3, dummy_h, [self()]),
+    [dummy_h] = gen_event:which_handlers(Pid3),
+    send_request_wait_reqid_collection(Pid1, Pid2, Pid3),
+    send_request_wait_reqid_collection_timeout(Pid1, Pid2, Pid3),
+    send_request_wait_reqid_collection_error(Pid1, Pid2, Pid3),
+    ok = gen_event:stop(Pid1),
+    try gen_event:stop(Pid2) catch exit:noproc -> ok end,
+    ok = gen_event:stop(Pid3).
+
+send_request_wait_reqid_collection(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_event:send_request(Pid1, dummy_h, hejsan),
+
+    ReqIdC0 = gen_event:reqids_new(),
+
+    ReqId1 = gen_event:send_request(Pid1, dummy_h, {delayed_answer,400}),
+    ReqIdC1 = gen_event:reqids_add(ReqId1, req1, ReqIdC0),
+    1 = gen_event:reqids_size(ReqIdC1),
+
+    ReqIdC2 = gen_event:send_request(Pid2, dummy_h, {delayed_answer,1}, req2, ReqIdC1),
+    2 = gen_event:reqids_size(ReqIdC2),
+
+    ReqIdC3 = gen_event:send_request(Pid3, dummy_h, {delayed_answer,200}, req3, ReqIdC2),
+    3 = gen_event:reqids_size(ReqIdC3),
+    
+    {{reply, delayed}, req2, ReqIdC4} = gen_event:wait_response(ReqIdC3, infinity, true),
+    2 = gen_event:reqids_size(ReqIdC4),
+
+    {{reply, delayed}, req3, ReqIdC5} = gen_event:wait_response(ReqIdC4, 5678, true),
+    1 = gen_event:reqids_size(ReqIdC5),
+
+    {{reply, delayed}, req1, ReqIdC6} = gen_event:wait_response(ReqIdC5, 5000, true),
+    0 = gen_event:reqids_size(ReqIdC6),
+
+    no_request = gen_event:wait_response(ReqIdC6, 5000, true),
+
+    {reply, {ok, hejhopp}} = gen_event:receive_response(ReqId0, infinity),
+
+    ok.
+
+send_request_wait_reqid_collection_timeout(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_event:send_request(Pid1, dummy_h, hejsan),
+
+    ReqIdC0 = gen_event:reqids_new(),
+
+    ReqId1 = gen_event:send_request(Pid1, dummy_h, {delayed_answer,1000}),
+    ReqIdC1 = gen_event:reqids_add(ReqId1, req1, ReqIdC0),
+
+    ReqIdC2 = gen_event:send_request(Pid2, dummy_h, {delayed_answer,1}, req2, ReqIdC1),
+
+    ReqId3 = gen_event:send_request(Pid3, dummy_h, {delayed_answer,500}),
+    ReqIdC3 = gen_event:reqids_add(ReqId3, req3, ReqIdC2),
+
+    Deadline = erlang:monotonic_time(millisecond) + 100,
+    
+    {{reply, delayed}, req2, ReqIdC4} = gen_event:wait_response(ReqIdC3, {abs, Deadline}, true),
+    2 = gen_event:reqids_size(ReqIdC4),
+
+    timeout = gen_event:wait_response(ReqIdC4, {abs, Deadline}, true),
+
+    Unhandled = lists:sort([{ReqId1, req1}, {ReqId3, req3}]),
+    Unhandled = lists:sort(gen_event:reqids_to_list(ReqIdC4)),
+
+    %% Make sure requests were not abandoned...
+    {{reply, delayed}, req3, ReqIdC4} = gen_event:wait_response(ReqIdC4, {abs, Deadline+1500}, false),
+    {{reply, delayed}, req1, ReqIdC4} = gen_event:wait_response(ReqIdC4, {abs, Deadline+1500}, false),
+
+    {reply, {ok, hejhopp}} = gen_event:receive_response(ReqId0, infinity),
+
+    ok.
+    
+send_request_wait_reqid_collection_error(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_event:send_request(Pid1, dummy_h, hejsan),
+
+    ReqIdC0 = gen_event:reqids_new(),
+
+    ReqId1 = gen_event:send_request(Pid1, dummy_h, {delayed_answer,400}),
+    ReqIdC1 = gen_event:reqids_add(ReqId1, req1, ReqIdC0),
+    try
+        nope = gen_event:reqids_add(ReqId1, req2, ReqIdC1)
+    catch
+        error:badarg -> ok
+    end,
+
+    unlink(Pid2),
+    exit(Pid2, kill),
+    ReqIdC2 = gen_event:send_request(Pid2, dummy_h, {delayed_answer,1}, req2, ReqIdC1),
+    ReqIdC3 = gen_event:send_request(Pid3, dummy_h, {delayed_answer,200}, req3, ReqIdC2),
+    ReqIdC4 = gen_event:send_request(Pid1, bad_h, hejsan, req4, ReqIdC3),
+    4 = gen_event:reqids_size(ReqIdC4),
+    
+    {{error, {noproc, _}}, req2, ReqIdC5} = gen_event:wait_response(ReqIdC4, 2000, true),
+    3 = gen_event:reqids_size(ReqIdC5),
+
+    {{reply, delayed}, req3, ReqIdC5} = gen_event:wait_response(ReqIdC5, infinity, false),
+    {{reply, delayed}, req1, ReqIdC5} = gen_event:wait_response(ReqIdC5, infinity, false),
+    {{error, bad_module}, req4, ReqIdC5} = gen_event:wait_response(ReqIdC5, infinity, false),
+
+    {reply, {ok, hejhopp}} = gen_event:receive_response(ReqId0, infinity),
+
+    ok.
+
+send_request_check_reqid_collection(Config) when is_list(Config) ->
+    {ok, Pid1} = gen_event:start(),
+    ok = gen_event:add_handler(Pid1, dummy_h, [self()]),
+    [dummy_h] = gen_event:which_handlers(Pid1),
+    {ok, Pid2} = gen_event:start(),
+    ok = gen_event:add_handler(Pid2, dummy_h, [self()]),
+    [dummy_h] = gen_event:which_handlers(Pid2),
+    {ok, Pid3} = gen_event:start(),
+    ok = gen_event:add_handler(Pid3, dummy_h, [self()]),
+    [dummy_h] = gen_event:which_handlers(Pid3),
+    send_request_check_reqid_collection(Pid1, Pid2, Pid3),
+    send_request_check_reqid_collection_error(Pid1, Pid2, Pid3),
+    ok = gen_event:stop(Pid1),
+    try gen_event:stop(Pid2) catch exit:noproc -> ok end,
+    ok = gen_event:stop(Pid3).
+
+send_request_check_reqid_collection(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_event:send_request(Pid1, dummy_h, hejsan),
+
+    receive after 100 -> ok end,
+
+    ReqIdC0 = gen_event:reqids_new(),
+
+    ReqIdC1 = gen_event:send_request(Pid1, dummy_h, {delayed_answer,400}, req1, ReqIdC0),
+    1 = gen_event:reqids_size(ReqIdC1),
+
+    ReqId2 = gen_event:send_request(Pid2, dummy_h, {delayed_answer,1}),
+    ReqIdC2 = gen_event:reqids_add(ReqId2, req2, ReqIdC1),
+    2 = gen_event:reqids_size(ReqIdC2),
+
+    ReqIdC3 = gen_event:send_request(Pid3, dummy_h, {delayed_answer,200}, req3, ReqIdC2),
+    3 = gen_event:reqids_size(ReqIdC3),
+
+    Msg0 = next_msg(),
+    no_reply = gen_event:check_response(Msg0, ReqIdC3, true),
+    
+    {{reply, delayed}, req2, ReqIdC4} = gen_event:check_response(next_msg(), ReqIdC3, true),
+    2 = gen_event:reqids_size(ReqIdC4),
+
+    {{reply, delayed}, req3, ReqIdC5} = gen_event:check_response(next_msg(), ReqIdC4, true),
+    1 = gen_event:reqids_size(ReqIdC5),
+
+    {{reply, delayed}, req1, ReqIdC6} = gen_event:check_response(next_msg(), ReqIdC5, true),
+    0 = gen_event:reqids_size(ReqIdC6),
+
+    no_request = gen_event:check_response(Msg0, ReqIdC6, true),
+
+    {reply, {ok, hejhopp}} = gen_event:check_response(Msg0, ReqId0),
+
+    ok.
+    
+send_request_check_reqid_collection_error(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_event:send_request(Pid1, dummy_h, hejsan),
+
+    receive after 100 -> ok end,
+
+    ReqIdC0 = gen_event:reqids_new(),
+
+    ReqId1 = gen_event:send_request(Pid1, dummy_h, {delayed_answer,400}),
+    ReqIdC1 = gen_event:reqids_add(ReqId1, req1, ReqIdC0),
+    try
+        nope = gen_event:reqids_add(ReqId1, req2, ReqIdC1)
+    catch
+        error:badarg -> ok
+    end,
+
+    unlink(Pid2),
+    exit(Pid2, kill),
+    ReqIdC2 = gen_event:send_request(Pid2, dummy_h, {delayed_answer,1}, req2, ReqIdC1),
+
+    ReqIdC3 = gen_event:send_request(Pid3, dummy_h, {delayed_answer,200}, req3, ReqIdC2),
+
+    ReqIdC4 = gen_event:send_request(Pid1, bad_h, hejsan, req4, ReqIdC3),
+    4 = gen_event:reqids_size(ReqIdC4),
+
+    Msg0 = next_msg(),
+
+    no_reply = gen_event:check_response(Msg0, ReqIdC3, true),
+    
+    {{error, {noproc, _}}, req2, ReqIdC5} = gen_event:check_response(next_msg(), ReqIdC4, true),
+    3 = gen_event:reqids_size(ReqIdC5),
+
+    {{reply, delayed}, req3, ReqIdC5} = gen_event:check_response(next_msg(), ReqIdC5, false),
+    {{reply, delayed}, req1, ReqIdC5} = gen_event:check_response(next_msg(), ReqIdC5, false),
+    {{error, bad_module}, req4, ReqIdC5} = gen_event:check_response(next_msg(), ReqIdC5, false),
+
+    no_reply = gen_event:check_response(Msg0, ReqIdC3, false),
+
+    {reply, {ok, hejhopp}} = gen_event:check_response(Msg0, ReqId0),
+
+    ok.
+
+next_msg() ->
+    receive M -> M end.
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index 652bff2a51..5fa604d4fd 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -26,7 +26,11 @@
 
 -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, send_request/1, cast/1, cast_fast/1,
+-export([start/1, crash/1, call/1, send_request/1,
+         send_request_receive_reqid_collection/1,
+         send_request_wait_reqid_collection/1,
+         send_request_check_reqid_collection/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, calling_self/1,
 	 call_remote_n1/1, call_remote_n2/1, call_remote_n3/1, spec_init/1,
@@ -65,7 +69,9 @@ suite() ->
      {timetrap,{minutes,1}}].
 
 all() -> 
-    [start, {group,stop}, crash, call, send_request, cast, cast_fast, info, abcast,
+    [start, {group,stop}, crash, call, send_request,
+     send_request_receive_reqid_collection, send_request_wait_reqid_collection,
+     send_request_check_reqid_collection, cast, cast_fast, info, abcast,
      continue, multicall, multicall_down, call_remote1, call_remote2, calling_self,
      call_remote3, call_remote_n1, call_remote_n2,
      call_remote_n3, spec_init,
@@ -598,6 +604,323 @@ send_request(Config) when is_list(Config) ->
     process_flag(trap_exit, OldFl),
     ok.
 
+send_request_receive_reqid_collection(Config) when is_list(Config) ->
+    {ok, Pid1} = gen_server:start_link({local, my_test_name1},
+                                       gen_server_SUITE, [], []),
+    {ok, Pid2} = gen_server:start_link({local, my_test_name2},
+                                       gen_server_SUITE, [], []),
+    {ok, Pid3} = gen_server:start_link({local, my_test_name3},
+                                       gen_server_SUITE, [], []),
+    send_request_receive_reqid_collection(Pid1, Pid2, Pid3),
+    send_request_receive_reqid_collection_timeout(Pid1, Pid2, Pid3),
+    send_request_receive_reqid_collection_error(Pid1, Pid2, Pid3),
+    unlink(Pid1),
+    exit(Pid1, kill),
+    unlink(Pid2),
+    exit(Pid2, kill),
+    unlink(Pid3),
+    exit(Pid3, kill),
+    false = is_process_alive(Pid1),
+    false = is_process_alive(Pid2),
+    false = is_process_alive(Pid3),
+    ok.
+
+send_request_receive_reqid_collection(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_server:send_request(Pid1, started_p),
+
+    ReqIdC0 = gen_server:reqids_new(),
+
+    ReqId1 = gen_server:send_request(Pid1, {delayed_answer,400}),
+    ReqIdC1 = gen_server:reqids_add(ReqId1, req1, ReqIdC0),
+    1 = gen_server:reqids_size(ReqIdC1),
+
+    ReqIdC2 = gen_server:send_request(Pid2, {delayed_answer,1}, req2, ReqIdC1),
+    2 = gen_server:reqids_size(ReqIdC2),
+
+    ReqIdC3 = gen_server:send_request(Pid3, {delayed_answer,200}, req3, ReqIdC2),
+    3 = gen_server:reqids_size(ReqIdC3),
+    
+    {{reply, delayed}, req2, ReqIdC4} = gen_server:receive_response(ReqIdC3, infinity, true),
+    2 = gen_server:reqids_size(ReqIdC4),
+
+    {{reply, delayed}, req3, ReqIdC5} = gen_server:receive_response(ReqIdC4, 5678, true),
+    1 = gen_server:reqids_size(ReqIdC5),
+
+    {{reply, delayed}, req1, ReqIdC6} = gen_server:receive_response(ReqIdC5, 5000, true),
+    0 = gen_server:reqids_size(ReqIdC6),
+
+    no_request = gen_server:receive_response(ReqIdC6, 5000, true),
+
+    {reply, ok} = gen_server:receive_response(ReqId0, infinity),
+
+    ok.
+
+send_request_receive_reqid_collection_timeout(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_server:send_request(Pid1, started_p),
+
+    ReqIdC0 = gen_server:reqids_new(),
+
+    ReqId1 = gen_server:send_request(Pid1, {delayed_answer,1000}),
+    ReqIdC1 = gen_server:reqids_add(ReqId1, req1, ReqIdC0),
+
+    ReqIdC2 = gen_server:send_request(Pid2, {delayed_answer,1}, req2, ReqIdC1),
+
+    ReqId3 = gen_server:send_request(Pid3, {delayed_answer,500}),
+    ReqIdC3 = gen_server:reqids_add(ReqId3, req3, ReqIdC2),
+
+    Deadline = erlang:monotonic_time(millisecond) + 100,
+    
+    {{reply, delayed}, req2, ReqIdC4} = gen_server:receive_response(ReqIdC3, {abs, Deadline}, true),
+    2 = gen_server:reqids_size(ReqIdC4),
+
+    timeout = gen_server:receive_response(ReqIdC4, {abs, Deadline}, true),
+
+    Abandoned = lists:sort([{ReqId1, req1}, {ReqId3, req3}]),
+    Abandoned = lists:sort(gen_server:reqids_to_list(ReqIdC4)),
+
+    %% Make sure requests were abandoned...
+    timeout = gen_server:receive_response(ReqIdC4, {abs, Deadline+1000}, true),
+
+    {reply, ok} = gen_server:receive_response(ReqId0, infinity),
+
+    ok.
+    
+send_request_receive_reqid_collection_error(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_server:send_request(Pid1, started_p),
+
+    ReqIdC0 = gen_server:reqids_new(),
+
+    ReqId1 = gen_server:send_request(Pid1, {delayed_answer,400}),
+    ReqIdC1 = gen_server:reqids_add(ReqId1, req1, ReqIdC0),
+    try
+        nope = gen_server:reqids_add(ReqId1, req2, ReqIdC1)
+    catch
+        error:badarg -> ok
+    end,
+ 
+    unlink(Pid2),
+    ReqIdC2 = gen_server:send_request(Pid2, stop_shutdown, req2, ReqIdC1),
+    ReqIdC3 = gen_server:send_request(Pid3, {delayed_answer,200}, req3, ReqIdC2),
+    3 = gen_server:reqids_size(ReqIdC3),
+    
+    {{error, {shutdown, _}}, req2, ReqIdC4} = gen_server:receive_response(ReqIdC3, 2000, true),
+    2 = gen_server:reqids_size(ReqIdC4),
+
+    {{reply, delayed}, req3, ReqIdC4} = gen_server:receive_response(ReqIdC4, infinity, false),
+
+    {{reply, delayed}, req1, ReqIdC4} = gen_server:receive_response(ReqIdC4, infinity, false),
+
+    {reply, ok} = gen_server:receive_response(ReqId0, infinity),
+
+    ok.
+
+send_request_wait_reqid_collection(Config) when is_list(Config) ->
+    {ok, Pid1} = gen_server:start_link({local, my_test_name1},
+                                       gen_server_SUITE, [], []),
+    {ok, Pid2} = gen_server:start_link({local, my_test_name2},
+                                       gen_server_SUITE, [], []),
+    {ok, Pid3} = gen_server:start_link({local, my_test_name3},
+                                       gen_server_SUITE, [], []),
+    send_request_wait_reqid_collection(Pid1, Pid2, Pid3),
+    send_request_wait_reqid_collection_timeout(Pid1, Pid2, Pid3),
+    send_request_wait_reqid_collection_error(Pid1, Pid2, Pid3),
+    unlink(Pid1),
+    exit(Pid1, kill),
+    unlink(Pid2),
+    exit(Pid2, kill),
+    unlink(Pid3),
+    exit(Pid3, kill),
+    false = is_process_alive(Pid1),
+    false = is_process_alive(Pid2),
+    false = is_process_alive(Pid3),
+    ok.
+
+send_request_wait_reqid_collection(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_server:send_request(Pid1, started_p),
+
+    ReqIdC0 = gen_server:reqids_new(),
+
+    ReqId1 = gen_server:send_request(Pid1, {delayed_answer,400}),
+    ReqIdC1 = gen_server:reqids_add(ReqId1, req1, ReqIdC0),
+    1 = gen_server:reqids_size(ReqIdC1),
+
+    ReqIdC2 = gen_server:send_request(Pid2, {delayed_answer,1}, req2, ReqIdC1),
+    2 = gen_server:reqids_size(ReqIdC2),
+
+    ReqIdC3 = gen_server:send_request(Pid3, {delayed_answer,200}, req3, ReqIdC2),
+    3 = gen_server:reqids_size(ReqIdC3),
+    
+    {{reply, delayed}, req2, ReqIdC4} = gen_server:wait_response(ReqIdC3, infinity, true),
+    2 = gen_server:reqids_size(ReqIdC4),
+
+    {{reply, delayed}, req3, ReqIdC5} = gen_server:wait_response(ReqIdC4, 5678, true),
+    1 = gen_server:reqids_size(ReqIdC5),
+
+    {{reply, delayed}, req1, ReqIdC6} = gen_server:wait_response(ReqIdC5, 5000, true),
+    0 = gen_server:reqids_size(ReqIdC6),
+
+    no_request = gen_server:wait_response(ReqIdC6, 5000, true),
+
+    {reply, ok} = gen_server:wait_response(ReqId0, infinity),
+
+    ok.
+
+send_request_wait_reqid_collection_timeout(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_server:send_request(Pid1, started_p),
+
+    ReqIdC0 = gen_server:reqids_new(),
+
+    ReqId1 = gen_server:send_request(Pid1, {delayed_answer,1000}),
+    ReqIdC1 = gen_server:reqids_add(ReqId1, req1, ReqIdC0),
+
+    ReqIdC2 = gen_server:send_request(Pid2, {delayed_answer,1}, req2, ReqIdC1),
+
+    ReqId3 = gen_server:send_request(Pid3, {delayed_answer,500}),
+    ReqIdC3 = gen_server:reqids_add(ReqId3, req3, ReqIdC2),
+
+    Deadline = erlang:monotonic_time(millisecond) + 100,
+    
+    {{reply, delayed}, req2, ReqIdC4} = gen_server:wait_response(ReqIdC3, {abs, Deadline}, true),
+    2 = gen_server:reqids_size(ReqIdC4),
+
+    timeout = gen_server:wait_response(ReqIdC4, {abs, Deadline}, true),
+
+    Unhandled = lists:sort([{ReqId1, req1}, {ReqId3, req3}]),
+    Unhandled = lists:sort(gen_server:reqids_to_list(ReqIdC4)),
+
+    %% Make sure requests were not abandoned...
+    {{reply, delayed}, req3, ReqIdC4} = gen_server:wait_response(ReqIdC4, {abs, Deadline+1500}, false),
+    {{reply, delayed}, req1, ReqIdC4} = gen_server:wait_response(ReqIdC4, {abs, Deadline+1500}, false),
+
+    {reply, ok} = gen_server:receive_response(ReqId0, infinity),
+
+    ok.
+    
+send_request_wait_reqid_collection_error(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_server:send_request(Pid1, started_p),
+
+    ReqIdC0 = gen_server:reqids_new(),
+
+    ReqId1 = gen_server:send_request(Pid1, {delayed_answer,400}),
+    ReqIdC1 = gen_server:reqids_add(ReqId1, req1, ReqIdC0),
+    try
+        nope = gen_server:reqids_add(ReqId1, req2, ReqIdC1)
+    catch
+        error:badarg -> ok
+    end,
+ 
+    unlink(Pid2),
+    ReqIdC2 = gen_server:send_request(Pid2, stop_shutdown, req2, ReqIdC1),
+    ReqIdC3 = gen_server:send_request(Pid3, {delayed_answer,200}, req3, ReqIdC2),
+    3 = gen_server:reqids_size(ReqIdC3),
+    
+    {{error, {shutdown, _}}, req2, ReqIdC4} = gen_server:wait_response(ReqIdC3, 2000, true),
+    2 = gen_server:reqids_size(ReqIdC4),
+
+    {{reply, delayed}, req3, ReqIdC4} = gen_server:wait_response(ReqIdC4, infinity, false),
+
+    {{reply, delayed}, req1, ReqIdC4} = gen_server:wait_response(ReqIdC4, infinity, false),
+
+    {reply, ok} = gen_server:wait_response(ReqId0, infinity),
+
+    ok.
+
+send_request_check_reqid_collection(Config) when is_list(Config) ->
+    {ok, Pid1} = gen_server:start_link({local, my_test_name1},
+                                       gen_server_SUITE, [], []),
+    {ok, Pid2} = gen_server:start_link({local, my_test_name2},
+                                       gen_server_SUITE, [], []),
+    {ok, Pid3} = gen_server:start_link({local, my_test_name3},
+                                       gen_server_SUITE, [], []),
+    send_request_check_reqid_collection(Pid1, Pid2, Pid3),
+    send_request_check_reqid_collection_error(Pid1, Pid2, Pid3),
+    unlink(Pid1),
+    exit(Pid1, kill),
+    unlink(Pid2),
+    exit(Pid2, kill),
+    unlink(Pid3),
+    exit(Pid3, kill),
+    false = is_process_alive(Pid1),
+    false = is_process_alive(Pid2),
+    false = is_process_alive(Pid3),
+    ok.
+
+send_request_check_reqid_collection(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_server:send_request(Pid1, started_p),
+
+    receive after 100 -> ok end,
+
+    ReqIdC0 = gen_server:reqids_new(),
+
+    ReqIdC1 = gen_server:send_request(Pid1, {delayed_answer,400}, req1, ReqIdC0),
+    1 = gen_server:reqids_size(ReqIdC1),
+
+    ReqId2 = gen_server:send_request(Pid2, {delayed_answer,1}),
+    ReqIdC2 = gen_server:reqids_add(ReqId2, req2, ReqIdC1),
+    2 = gen_server:reqids_size(ReqIdC2),
+
+    ReqIdC3 = gen_server:send_request(Pid3, {delayed_answer,200}, req3, ReqIdC2),
+    3 = gen_server:reqids_size(ReqIdC3),
+
+    Msg0 = next_msg(),
+    no_reply = gen_server:check_response(Msg0, ReqIdC3, true),
+    
+    {{reply, delayed}, req2, ReqIdC4} = gen_server:check_response(next_msg(), ReqIdC3, true),
+    2 = gen_server:reqids_size(ReqIdC4),
+
+    {{reply, delayed}, req3, ReqIdC5} = gen_server:check_response(next_msg(), ReqIdC4, true),
+    1 = gen_server:reqids_size(ReqIdC5),
+
+    {{reply, delayed}, req1, ReqIdC6} = gen_server:check_response(next_msg(), ReqIdC5, true),
+    0 = gen_server:reqids_size(ReqIdC6),
+
+    no_request = gen_server:check_response(Msg0, ReqIdC6, true),
+
+    {reply, ok} = gen_server:check_response(Msg0, ReqId0),
+
+    ok.
+    
+send_request_check_reqid_collection_error(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_server:send_request(Pid1, started_p),
+
+    receive after 100 -> ok end,
+
+    ReqIdC0 = gen_server:reqids_new(),
+
+    ReqId1 = gen_server:send_request(Pid1, {delayed_answer,400}),
+    ReqIdC1 = gen_server:reqids_add(ReqId1, req1, ReqIdC0),
+
+    unlink(Pid2),
+    ReqIdC2 = gen_server:send_request(Pid2, stop_shutdown, req2, ReqIdC1),
+
+    ReqIdC3 = gen_server:send_request(Pid3, {delayed_answer,200}, req3, ReqIdC2),
+    3 = gen_server:reqids_size(ReqIdC3),
+
+    Msg0 = next_msg(),
+
+    no_reply = gen_server:check_response(Msg0, ReqIdC3, true),
+    
+    {{error, {shutdown, _}}, req2, ReqIdC4} = gen_server:check_response(next_msg(), ReqIdC3, true),
+    2 = gen_server:reqids_size(ReqIdC4),
+
+    {{reply, delayed}, req3, ReqIdC4} = gen_server:check_response(next_msg(), ReqIdC4, false),
+
+    {{reply, delayed}, req1, ReqIdC4} = gen_server:check_response(next_msg(), ReqIdC4, false),
+
+    {reply, ok} = gen_server:check_response(Msg0, ReqId0),
+
+    ok.
+
+next_msg() ->
+    receive M -> M end.
 
 %% --------------------------------------
 %% Test handle_continue.
diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl
index 9ddffcbb72..a34cb55e52 100644
--- a/lib/stdlib/test/gen_statem_SUITE.erl
+++ b/lib/stdlib/test/gen_statem_SUITE.erl
@@ -43,7 +43,9 @@ all() ->
      {group, sys},
      hibernate, auto_hibernate, enter_loop, {group, undef_callbacks},
      undef_in_terminate, {group, format_log},
-     reply_by_alias_with_payload].
+     reply_by_alias_with_payload,
+     send_request_receive_reqid_collection, send_request_wait_reqid_collection,
+     send_request_check_reqid_collection].
 
 groups() ->
     [{start, [], tcs(start)},
@@ -2305,6 +2307,311 @@ reply_by_alias_with_payload(Config) when is_list(Config) ->
             ok
     end.
 
+send_request_receive_reqid_collection(Config) when is_list(Config) ->
+    {ok,Pid1} = gen_statem:start(?MODULE, start_arg(Config, []), []),
+    {ok,Pid2} = gen_statem:start(?MODULE, start_arg(Config, []), []),
+    {ok,Pid3} = gen_statem:start(?MODULE, start_arg(Config, []), []),
+    send_request_receive_reqid_collection(Pid1, Pid2, Pid3),
+    send_request_receive_reqid_collection_timeout(Pid1, Pid2, Pid3),
+    send_request_receive_reqid_collection_error(Pid1, Pid2, Pid3),
+    stopped = gen_statem:call(Pid1, {stop,shutdown}),
+    stopped = gen_statem:call(Pid3, {stop,shutdown}),
+    check_stopped(Pid1),
+    check_stopped(Pid2),
+    check_stopped(Pid3),
+    ok.
+
+send_request_receive_reqid_collection(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_statem:send_request(Pid1, 'alive?'),
+
+    ReqIdC0 = gen_statem:reqids_new(),
+
+    ReqId1 = gen_statem:send_request(Pid1, {delayed_answer,400}),
+    ReqIdC1 = gen_statem:reqids_add(ReqId1, req1, ReqIdC0),
+    1 = gen_statem:reqids_size(ReqIdC1),
+
+    ReqIdC2 = gen_statem:send_request(Pid2, {delayed_answer,1}, req2, ReqIdC1),
+    2 = gen_statem:reqids_size(ReqIdC2),
+
+    ReqIdC3 = gen_statem:send_request(Pid3, {delayed_answer,200}, req3, ReqIdC2),
+    3 = gen_statem:reqids_size(ReqIdC3),
+    
+    {{reply, delayed}, req2, ReqIdC4} = gen_statem:receive_response(ReqIdC3, infinity, true),
+    2 = gen_statem:reqids_size(ReqIdC4),
+
+    {{reply, delayed}, req3, ReqIdC5} = gen_statem:receive_response(ReqIdC4, 5678, true),
+    1 = gen_statem:reqids_size(ReqIdC5),
+
+    {{reply, delayed}, req1, ReqIdC6} = gen_statem:receive_response(ReqIdC5, 5000, true),
+    0 = gen_statem:reqids_size(ReqIdC6),
+
+    no_request = gen_statem:receive_response(ReqIdC6, 5000, true),
+
+    {reply, yes} = gen_statem:receive_response(ReqId0, infinity),
+
+    ok.
+
+send_request_receive_reqid_collection_timeout(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_statem:send_request(Pid1, 'alive?'),
+
+    ReqIdC0 = gen_statem:reqids_new(),
+
+    ReqId1 = gen_statem:send_request(Pid1, {delayed_answer,1000}),
+    ReqIdC1 = gen_statem:reqids_add(ReqId1, req1, ReqIdC0),
+
+    ReqIdC2 = gen_statem:send_request(Pid2, {delayed_answer,1}, req2, ReqIdC1),
+
+    ReqId3 = gen_statem:send_request(Pid3, {delayed_answer,500}),
+    ReqIdC3 = gen_statem:reqids_add(ReqId3, req3, ReqIdC2),
+
+    Deadline = erlang:monotonic_time(millisecond) + 100,
+    
+    {{reply, delayed}, req2, ReqIdC4} = gen_statem:receive_response(ReqIdC3, {abs, Deadline}, true),
+    2 = gen_statem:reqids_size(ReqIdC4),
+
+    timeout = gen_statem:receive_response(ReqIdC4, {abs, Deadline}, true),
+
+    Abandoned = lists:sort([{ReqId1, req1}, {ReqId3, req3}]),
+    Abandoned = lists:sort(gen_statem:reqids_to_list(ReqIdC4)),
+
+    %% Make sure requests were abandoned...
+    timeout = gen_statem:receive_response(ReqIdC4, {abs, Deadline+1000}, true),
+
+    {reply, yes} = gen_statem:receive_response(ReqId0, infinity),
+
+    ok.
+    
+send_request_receive_reqid_collection_error(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_statem:send_request(Pid1, 'alive?'),
+
+    ReqIdC0 = gen_statem:reqids_new(),
+
+    ReqId1 = gen_statem:send_request(Pid1, {delayed_answer,400}),
+    ReqIdC1 = gen_statem:reqids_add(ReqId1, req1, ReqIdC0),
+    try
+        nope = gen_statem:reqids_add(ReqId1, req2, ReqIdC1)
+    catch
+        error:badarg -> ok
+    end,
+ 
+    unlink(Pid2),
+    exit(Pid2, kill),
+    ReqIdC2 = gen_statem:send_request(Pid2, {delayed_answer,1}, req2, ReqIdC1),
+    ReqIdC3 = gen_statem:send_request(Pid3, {delayed_answer,200}, req3, ReqIdC2),
+    3 = gen_statem:reqids_size(ReqIdC3),
+    
+    {{error, {noproc, _}}, req2, ReqIdC4} = gen_statem:receive_response(ReqIdC3, 2000, true),
+    2 = gen_statem:reqids_size(ReqIdC4),
+
+    {{reply, delayed}, req3, ReqIdC4} = gen_statem:receive_response(ReqIdC4, infinity, false),
+
+    {{reply, delayed}, req1, ReqIdC4} = gen_statem:receive_response(ReqIdC4, infinity, false),
+
+    {reply, yes} = gen_statem:receive_response(ReqId0, infinity),
+
+    ok.
+
+send_request_wait_reqid_collection(Config) when is_list(Config) ->
+    {ok,Pid1} = gen_statem:start(?MODULE, start_arg(Config, []), []),
+    {ok,Pid2} = gen_statem:start(?MODULE, start_arg(Config, []), []),
+    {ok,Pid3} = gen_statem:start(?MODULE, start_arg(Config, []), []),
+    send_request_wait_reqid_collection(Pid1, Pid2, Pid3),
+    send_request_wait_reqid_collection_timeout(Pid1, Pid2, Pid3),
+    send_request_wait_reqid_collection_error(Pid1, Pid2, Pid3),
+    stopped = gen_statem:call(Pid1, {stop,shutdown}),
+    stopped = gen_statem:call(Pid3, {stop,shutdown}),
+    check_stopped(Pid1),
+    check_stopped(Pid2),
+    check_stopped(Pid3),
+    ok.
+
+send_request_wait_reqid_collection(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_statem:send_request(Pid1, 'alive?'),
+
+    ReqIdC0 = gen_statem:reqids_new(),
+
+    ReqId1 = gen_statem:send_request(Pid1, {delayed_answer,400}),
+    ReqIdC1 = gen_statem:reqids_add(ReqId1, req1, ReqIdC0),
+    1 = gen_statem:reqids_size(ReqIdC1),
+
+    ReqIdC2 = gen_statem:send_request(Pid2, {delayed_answer,1}, req2, ReqIdC1),
+    2 = gen_statem:reqids_size(ReqIdC2),
+
+    ReqIdC3 = gen_statem:send_request(Pid3, {delayed_answer,200}, req3, ReqIdC2),
+    3 = gen_statem:reqids_size(ReqIdC3),
+    
+    {{reply, delayed}, req2, ReqIdC4} = gen_statem:wait_response(ReqIdC3, infinity, true),
+    2 = gen_statem:reqids_size(ReqIdC4),
+
+    {{reply, delayed}, req3, ReqIdC5} = gen_statem:wait_response(ReqIdC4, 5678, true),
+    1 = gen_statem:reqids_size(ReqIdC5),
+
+    {{reply, delayed}, req1, ReqIdC6} = gen_statem:wait_response(ReqIdC5, 5000, true),
+    0 = gen_statem:reqids_size(ReqIdC6),
+
+    no_request = gen_statem:wait_response(ReqIdC6, 5000, true),
+
+    {reply, yes} = gen_statem:receive_response(ReqId0, infinity),
+
+    ok.
+
+send_request_wait_reqid_collection_timeout(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_statem:send_request(Pid1, 'alive?'),
+
+    ReqIdC0 = gen_statem:reqids_new(),
+
+    ReqId1 = gen_statem:send_request(Pid1, {delayed_answer,1000}),
+    ReqIdC1 = gen_statem:reqids_add(ReqId1, req1, ReqIdC0),
+
+    ReqIdC2 = gen_statem:send_request(Pid2, {delayed_answer,1}, req2, ReqIdC1),
+
+    ReqId3 = gen_statem:send_request(Pid3, {delayed_answer,500}),
+    ReqIdC3 = gen_statem:reqids_add(ReqId3, req3, ReqIdC2),
+
+    Deadline = erlang:monotonic_time(millisecond) + 100,
+    
+    {{reply, delayed}, req2, ReqIdC4} = gen_statem:wait_response(ReqIdC3, {abs, Deadline}, true),
+    2 = gen_statem:reqids_size(ReqIdC4),
+
+    timeout = gen_statem:wait_response(ReqIdC4, {abs, Deadline}, true),
+
+    Unhandled = lists:sort([{ReqId1, req1}, {ReqId3, req3}]),
+    Unhandled = lists:sort(gen_statem:reqids_to_list(ReqIdC4)),
+
+    %% Make sure requests were not abandoned...
+    {{reply, delayed}, req3, ReqIdC4} = gen_statem:wait_response(ReqIdC4, {abs, Deadline+1500}, false),
+    {{reply, delayed}, req1, ReqIdC4} = gen_statem:wait_response(ReqIdC4, {abs, Deadline+1500}, false),
+
+    {reply, yes} = gen_statem:receive_response(ReqId0),
+
+    ok.
+    
+send_request_wait_reqid_collection_error(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_statem:send_request(Pid1, 'alive?'),
+
+    ReqIdC0 = gen_statem:reqids_new(),
+
+    ReqId1 = gen_statem:send_request(Pid1, {delayed_answer,400}),
+    ReqIdC1 = gen_statem:reqids_add(ReqId1, req1, ReqIdC0),
+    try
+        nope = gen_statem:reqids_add(ReqId1, req2, ReqIdC1)
+    catch
+        error:badarg -> ok
+    end,
+ 
+    unlink(Pid2),
+    exit(Pid2, kill),
+    ReqIdC2 = gen_statem:send_request(Pid2, {delayed_answer,1}, req2, ReqIdC1),
+    ReqIdC3 = gen_statem:send_request(Pid3, {delayed_answer,200}, req3, ReqIdC2),
+    3 = gen_statem:reqids_size(ReqIdC3),
+    
+    {{error, {noproc, _}}, req2, ReqIdC4} = gen_statem:wait_response(ReqIdC3, 2000, true),
+    2 = gen_statem:reqids_size(ReqIdC4),
+
+    {{reply, delayed}, req3, ReqIdC4} = gen_statem:wait_response(ReqIdC4, infinity, false),
+
+    {{reply, delayed}, req1, ReqIdC4} = gen_statem:wait_response(ReqIdC4, infinity, false),
+
+    {reply, yes} = gen_statem:receive_response(ReqId0, infinity),
+
+    ok.
+
+send_request_check_reqid_collection(Config) when is_list(Config) ->
+    {ok,Pid1} = gen_statem:start(?MODULE, start_arg(Config, []), []),
+    {ok,Pid2} = gen_statem:start(?MODULE, start_arg(Config, []), []),
+    {ok,Pid3} = gen_statem:start(?MODULE, start_arg(Config, []), []),
+    send_request_check_reqid_collection(Pid1, Pid2, Pid3),
+    send_request_check_reqid_collection_error(Pid1, Pid2, Pid3),
+    stopped = gen_statem:call(Pid1, {stop,shutdown}),
+    stopped = gen_statem:call(Pid3, {stop,shutdown}),
+    check_stopped(Pid1),
+    check_stopped(Pid2),
+    check_stopped(Pid3),
+    ok.
+
+send_request_check_reqid_collection(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_statem:send_request(Pid1, 'alive?'),
+
+    receive after 100 -> ok end,
+
+    ReqIdC0 = gen_statem:reqids_new(),
+
+    ReqIdC1 = gen_statem:send_request(Pid1, {delayed_answer,400}, req1, ReqIdC0),
+    1 = gen_statem:reqids_size(ReqIdC1),
+
+    ReqId2 = gen_statem:send_request(Pid2, {delayed_answer,1}),
+    ReqIdC2 = gen_statem:reqids_add(ReqId2, req2, ReqIdC1),
+    2 = gen_statem:reqids_size(ReqIdC2),
+
+    ReqIdC3 = gen_statem:send_request(Pid3, {delayed_answer,200}, req3, ReqIdC2),
+    3 = gen_statem:reqids_size(ReqIdC3),
+
+    Msg0 = next_msg(),
+    no_reply = gen_statem:check_response(Msg0, ReqIdC3, true),
+    
+    {{reply, delayed}, req2, ReqIdC4} = gen_statem:check_response(next_msg(), ReqIdC3, true),
+    2 = gen_statem:reqids_size(ReqIdC4),
+
+    {{reply, delayed}, req3, ReqIdC5} = gen_statem:check_response(next_msg(), ReqIdC4, true),
+    1 = gen_statem:reqids_size(ReqIdC5),
+
+    {{reply, delayed}, req1, ReqIdC6} = gen_statem:check_response(next_msg(), ReqIdC5, true),
+    0 = gen_statem:reqids_size(ReqIdC6),
+
+    no_request = gen_statem:check_response(Msg0, ReqIdC6, true),
+
+    {reply, yes} = gen_statem:check_response(Msg0, ReqId0),
+
+    ok.
+    
+send_request_check_reqid_collection_error(Pid1, Pid2, Pid3) ->
+
+    ReqId0 = gen_statem:send_request(Pid1, 'alive?'),
+
+    receive after 100 -> ok end,
+
+    ReqIdC0 = gen_statem:reqids_new(),
+
+    ReqId1 = gen_statem:send_request(Pid1, {delayed_answer,400}),
+    ReqIdC1 = gen_statem:reqids_add(ReqId1, req1, ReqIdC0),
+    try
+        nope = gen_statem:reqids_add(ReqId1, req2, ReqIdC1)
+    catch
+        error:badarg -> ok
+    end,
+
+    unlink(Pid2),
+    exit(Pid2, kill),
+    ReqIdC2 = gen_statem:send_request(Pid2, {delayed_answer,1}, req2, ReqIdC1),
+
+    ReqIdC3 = gen_statem:send_request(Pid3, {delayed_answer,200}, req3, ReqIdC2),
+    3 = gen_statem:reqids_size(ReqIdC3),
+
+    Msg0 = next_msg(),
+
+    no_reply = gen_statem:check_response(Msg0, ReqIdC3, true),
+    
+    {{error, {noproc, _}}, req2, ReqIdC4} = gen_statem:check_response(next_msg(), ReqIdC3, true),
+    2 = gen_statem:reqids_size(ReqIdC4),
+
+    {{reply, delayed}, req3, ReqIdC4} = gen_statem:check_response(next_msg(), ReqIdC4, false),
+
+    {{reply, delayed}, req1, ReqIdC4} = gen_statem:check_response(next_msg(), ReqIdC4, false),
+
+    {reply, yes} = gen_statem:check_response(Msg0, ReqId0),
+
+    ok.
+
+next_msg() ->
+    receive M -> M end.
+
 %%
 %% Functionality check
 %%
-- 
2.34.1

openSUSE Build Service is sponsored by