mroeppis commented on code in PR #13791:
URL: https://github.com/apache/ignite/pull/13791#discussion_r4504648769


##########
modules/core/src/main/java/org/apache/ignite/spi/communication/tcp/internal/ConnectionClientPool.java:
##########
@@ -355,33 +401,67 @@ public GridCommunicationClient reserveClient(ClusterNode 
node, int connIdx) thro
                     else
                         fut = oldFut;
 
-                    long clientReserveWaitTimeout = registry != null ? 
registry.getSystemWorkerBlockedTimeout() / 3
-                        : cfg.connectionTimeout() / 3;
+                    if (ownClient != null)
+                        client = ownClient;
+
+                    if (client == null) {
+                        long perWaitTimeout = registry != null ?
+                            registry.getSystemWorkerBlockedTimeout() / 3 : 
cfg.connectionTimeout() / 3;
 
-                    long currTimeout = System.currentTimeMillis();
+                        long currTimeout = System.currentTimeMillis();
 
-                    // This cycle will eventually quit when future is 
completed by concurrent thread reserving client.
-                    while (true) {
-                        try {
-                            client = fut.get(clientReserveWaitTimeout, 
TimeUnit.MILLISECONDS);
+                        // This cycle will eventually quit when future is 
completed by concurrent thread reserving client.
+                        while (true) {
+                            // A concurrent thread may have established the 
client while this thread was
+                            // waiting: return it instead of waiting further 
or giving up, even if the
+                            // reserve budget has already elapsed — the budget 
bounds waiting for a
+                            // connection, not the availability of an existing 
client.
+                            GridCommunicationClient[] curClients0 = 
clients.get(nodeId);
 
-                            break;
-                        }
-                        catch (IgniteFutureTimeoutCheckedException ignored) {
-                            currTimeout += clientReserveWaitTimeout;
-
-                            if (log.isDebugEnabled()) {
-                                log.debug(
-                                    "Still waiting for reestablishing 
connection to node " +
-                                        "[nodeId=" + node.id() + ", 
waitingTime=" + currTimeout + "ms]"
-                                );
+                            GridCommunicationClient curClient = curClients0 != 
null && connIdx < curClients0.length ?
+                                curClients0[connIdx] : null;
+
+                            if (curClient != null) {
+                                client = curClient;
+
+                                break;

Review Comment:
   My original implementation establishes a communication client and then 
publishes it to other threads through a shared future and the node-client map.
   
   The problem is that publishing the client and completing the shared future 
are not atomic with respect to `stop()`.
   
   Consider this interleaving:
   1. Thread A establishes a new communication client.
   2. Thread A publishes the client in the node-client map.
   3. Thread B calls `stop()` and completes the shared future exceptionally.
   4. Thread C concurrently retrieves the client from the node-client map.
   5. Thread A attempts to complete the future with the client, but fails 
because the future has already been completed by `stop()`.
   6. Thread A removes and closes the client.
   Thread C may therefore receive a client that is being closed immediately 
afterwards.
   
   This is a genuine publication race: the client becomes visible to other 
threads before the implementation knows whether shutdown has won the completion 
race.
   
   The important detail is that the shared future itself is also a publication 
mechanism. Protecting only the map would not be sufficient: a waiting thread 
could obtain the client directly from the future.
   
   **Why the finding is valid**: Shutdown and client publication need a common 
synchronization mechanism. Otherwise, a client can escape to another thread 
even though the creator subsequently decides that it must be discarded.
   
   My revised implementation addresses this by coordinating the stopping flag, 
future completion, and client publication under `stopLock`.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to