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.
*