This is an automated email from the ASF dual-hosted git repository.

NSAmelchev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new e99390aaf75 IGNITE-28992 Fixed SSL handshake timeout deadlock in the 
client connector (#13501)
e99390aaf75 is described below

commit e99390aaf7527498c8393e827d17c4ea6ef82fa0
Author: Nikita Amelchev <[email protected]>
AuthorDate: Thu Aug 20 19:21:59 2026 +0300

    IGNITE-28992 Fixed SSL handshake timeout deadlock in the client connector 
(#13501)
---
 .../processors/odbc/ClientListenerNioListener.java | 16 +++----
 .../processors/timeout/GridTimeoutProcessor.java   | 16 ++++++-
 .../timeout/GridTimeoutProcessorSelfTest.java      | 56 ++++++++++++++++++++++
 3 files changed, 77 insertions(+), 11 deletions(-)

diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerNioListener.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerNioListener.java
index ed3eb4247c3..2f9382a44fd 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerNioListener.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerNioListener.java
@@ -17,7 +17,6 @@
 
 package org.apache.ignite.internal.processors.odbc;
 
-import java.io.Closeable;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Predicate;
 import org.apache.ignite.IgniteCheckedException;
@@ -39,6 +38,7 @@ import 
org.apache.ignite.internal.processors.odbc.jdbc.JdbcConnectionContext;
 import org.apache.ignite.internal.processors.odbc.odbc.OdbcConnectionContext;
 import 
org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
 import org.apache.ignite.internal.processors.platform.client.ClientStatus;
+import 
org.apache.ignite.internal.processors.timeout.GridTimeoutProcessor.CancelableTask;
 import org.apache.ignite.internal.thread.context.Scope;
 import org.apache.ignite.internal.util.CommonUtils;
 import org.apache.ignite.internal.util.GridSpinBusyLock;
@@ -318,7 +318,7 @@ public class ClientListenerNioListener extends 
GridNioServerListenerAdapter<Clie
     private void scheduleHandshakeTimeout(GridNioSession ses, long 
handshakeTimeout) {
         assert handshakeTimeout > 0;
 
-        Closeable timeoutTask = ctx.timeout().schedule(new Runnable() {
+        CancelableTask timeoutTask = ctx.timeout().schedule(new Runnable() {
             @Override public void run() {
                 ses.close();
 
@@ -337,15 +337,11 @@ public class ClientListenerNioListener extends 
GridNioServerListenerAdapter<Clie
      * @param ses Connection session.
      */
     private void cancelHandshakeTimeout(GridNioSession ses) {
-        Closeable timeoutTask = 
ses.removeMeta(CONN_CTX_HANDSHAKE_TIMEOUT_TASK);
+        CancelableTask timeoutTask = 
ses.removeMeta(CONN_CTX_HANDSHAKE_TIMEOUT_TASK);
 
-        try {
-            if (timeoutTask != null)
-                timeoutTask.close();
-        }
-        catch (Exception e) {
-            U.warn(log, "Failed to cancel handshake timeout task " +
-                "[remoteAddr=" + ses.remoteAddress() + ", err=" + e + ']');
+        if (timeoutTask != null) {
+            // This method may be called under the SSL handler lock, so it 
must not wait for the timeout callback.
+            timeoutTask.cancel();
         }
     }
 
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessor.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessor.java
index 11f513e758d..bd63318eca8 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessor.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessor.java
@@ -362,16 +362,30 @@ public class GridTimeoutProcessor extends 
GridProcessorAdapter {
                     endTime = U.currentTimeMillis() + period;
 
                     addTimeoutObject(this);
+
+                    // cancel() may have been called after the check above but 
before the task was re-added.
+                    if (cancel)
+                        removeTimeoutObject(this);
                 }
             }
         }
 
+        /**
+         * Cancels this task without waiting for a concurrently running 
execution to finish.
+         * A running execution is not interrupted, and subsequent executions 
are suppressed.
+         */
+        public void cancel() {
+            cancel = true;
+
+            removeTimeoutObject(this);
+        }
+
         /** {@inheritDoc} */
         @Override public void close() {
             cancel = true;
 
             synchronized (this) {
-                // Just waiting for task execution end to make sure that task 
will not be executed anymore.
+                // Wait for a running execution to finish before the final 
queue cleanup.
                 removeTimeoutObject(this);
             }
         }
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessorSelfTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessorSelfTest.java
index d8ff871c04f..d06170f9d6f 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessorSelfTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessorSelfTest.java
@@ -22,7 +22,9 @@ import java.util.Random;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantLock;
 import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteUuid;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.testframework.junits.GridTestKernalContext;
@@ -133,6 +135,60 @@ public class GridTimeoutProcessorSelfTest extends 
GridCommonAbstractTest {
         }
     }
 
+    /**
+     * Tests non-blocking cancellation of a running periodic task.
+     *
+     * @throws Exception If test failed.
+     */
+    @Test
+    public void testNonBlockingCancel() throws Exception {
+        ReentrantLock extLock = new ReentrantLock();
+        CountDownLatch extLockAcquired = new CountDownLatch(1);
+        CountDownLatch taskStarted = new CountDownLatch(1);
+        AtomicInteger taskCallCnt = new AtomicInteger();
+
+        GridTimeoutProcessor.CancelableTask task = ctx.timeout().schedule(() 
-> {
+            U.awaitQuiet(extLockAcquired);
+
+            taskCallCnt.incrementAndGet();
+
+            taskStarted.countDown();
+
+            extLock.lock();
+
+            try {
+                // No-op.
+            }
+            finally {
+                extLock.unlock();
+            }
+        }, 0, 1_000);
+
+        IgniteInternalFuture<?> cancelFut = GridTestUtils.runAsync(() -> {
+            extLock.lock();
+
+            try {
+                extLockAcquired.countDown();
+
+                assertTrue(taskStarted.await(10_000, MILLISECONDS));
+
+                task.cancel();
+            }
+            finally {
+                extLock.unlock();
+            }
+        }, "test-cancel-thread");
+
+        cancelFut.get(10_000);
+
+        // Wait for the timeout callback and its rescheduling logic to finish.
+        synchronized (task) {
+            assertEquals(1, taskCallCnt.get());
+        }
+
+        assertFalse(ctx.timeout().removeTimeoutObject(task));
+    }
+
     /**
      * Multithreaded timeout test.
      *

Reply via email to