From 9a761f977be6f365fb3850517116881f1ba21f84 Mon Sep 17 00:00:00 2001 From: Bernard Normier Date: Fri, 15 Nov 2024 11:46:25 -0500 Subject: [PATCH 1/2] Fix bugs in idle timeout logic (C#) --- csharp/src/Ice/ConnectionI.cs | 3 +++ .../IdleTimeoutTransceiverDecorator.cs | 20 ++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/csharp/src/Ice/ConnectionI.cs b/csharp/src/Ice/ConnectionI.cs index bdb68e101da..4504da19a81 100644 --- a/csharp/src/Ice/ConnectionI.cs +++ b/csharp/src/Ice/ConnectionI.cs @@ -1941,6 +1941,9 @@ private bool validate(int operation) throw new MarshalException($"Received ValidateConnection message with unexpected size {size}."); } TraceUtil.traceRecv(_readStream, this, _logger, _traceLevels); + + // Client connection starts sending heartbeats once it's received the ValidateConnection message. + _idleTimeoutTransceiver?.scheduleHeartbeat(); } } diff --git a/csharp/src/Ice/Internal/IdleTimeoutTransceiverDecorator.cs b/csharp/src/Ice/Internal/IdleTimeoutTransceiverDecorator.cs index 09fec63834f..c9d424e3573 100644 --- a/csharp/src/Ice/Internal/IdleTimeoutTransceiverDecorator.cs +++ b/csharp/src/Ice/Internal/IdleTimeoutTransceiverDecorator.cs @@ -61,17 +61,8 @@ public void finishRead(Buffer buf) public ConnectionInfo getInfo() => _decoratee.getInfo(); - public int initialize(Buffer readBuffer, Buffer writeBuffer, ref bool hasMoreData) - { - int op = _decoratee.initialize(readBuffer, writeBuffer, ref hasMoreData); - - if (op == SocketOperation.None) // connected - { - rescheduleWriteTimer(); - } - - return op; - } + public int initialize(Buffer readBuffer, Buffer writeBuffer, ref bool hasMoreData) => + _decoratee.initialize(readBuffer, writeBuffer, ref hasMoreData); public string protocol() => _decoratee.protocol(); @@ -126,7 +117,10 @@ internal IdleTimeoutTransceiverDecorator(Transceiver decoratee, ConnectionI conn internal void enableIdleCheck() { - if (!idleCheckEnabled && _readTimer is not null) + // idleCheckEnabled is already true when enableIdleCheck() is called on a TCP server connection that becomes + // Active for the first time. + + if (_readTimer is not null) { rescheduleReadTimer(); idleCheckEnabled = true; @@ -142,6 +136,8 @@ internal void disableIdleCheck() } } + internal void scheduleHeartbeat() => rescheduleWriteTimer(); + private void cancelReadTimer() => _readTimer!.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); private void cancelWriteTimer() => _writeTimer.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); From c06047496eebba13bf84bd72fb92ac4a376d981b Mon Sep 17 00:00:00 2001 From: Bernard Normier Date: Fri, 15 Nov 2024 15:00:25 -0500 Subject: [PATCH 2/2] More fixes --- .../IdleTimeoutTransceiverDecorator.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/csharp/src/Ice/Internal/IdleTimeoutTransceiverDecorator.cs b/csharp/src/Ice/Internal/IdleTimeoutTransceiverDecorator.cs index c9d424e3573..d84c1c1a2bb 100644 --- a/csharp/src/Ice/Internal/IdleTimeoutTransceiverDecorator.cs +++ b/csharp/src/Ice/Internal/IdleTimeoutTransceiverDecorator.cs @@ -2,7 +2,6 @@ #nullable enable -using System.Collections; using System.Diagnostics; using System.Net.Sockets; @@ -99,7 +98,11 @@ public int write(Buffer buf) return op; } - internal IdleTimeoutTransceiverDecorator(Transceiver decoratee, ConnectionI connection, TimeSpan idleTimeout, bool enableIdleCheck) + internal IdleTimeoutTransceiverDecorator( + Transceiver decoratee, + ConnectionI connection, + TimeSpan idleTimeout, + bool enableIdleCheck) { Debug.Assert(idleTimeout > TimeSpan.Zero); @@ -109,18 +112,18 @@ internal IdleTimeoutTransceiverDecorator(Transceiver decoratee, ConnectionI conn if (enableIdleCheck) { _readTimer = new System.Threading.Timer(_ => connection.idleCheck(_idleTimeout)); - } - idleCheckEnabled = enableIdleCheck; + // idleCheckEnabled remains false for now. ConnectionI calls enableIdleCheck() when it becomes Active to + // schedule the first idle check and set idleCheckEnabled to true. + // We don't want the idle check to start when a client connection is waiting for the initial + // ValidateConnection message or when a server connection is in its initial StateHolding state. + } _writeTimer = new System.Threading.Timer(_ => connection.sendHeartbeat()); } internal void enableIdleCheck() { - // idleCheckEnabled is already true when enableIdleCheck() is called on a TCP server connection that becomes - // Active for the first time. - - if (_readTimer is not null) + if (!idleCheckEnabled && _readTimer is not null) { rescheduleReadTimer(); idleCheckEnabled = true;