From c1d286f4c8f58fbd7abe682ce411155ff8567f66 Mon Sep 17 00:00:00 2001 From: Ismael Hamed <1279846+ismaelhamed@users.noreply.github.com> Date: Sun, 3 Apr 2022 08:39:06 +0200 Subject: [PATCH 1/2] Fix TcpConnection error handling --- src/core/Akka/IO/TcpConnection.cs | 65 +++++++++++++++---------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/src/core/Akka/IO/TcpConnection.cs b/src/core/Akka/IO/TcpConnection.cs index 5d87c6f6114..f24b42a0c68 100644 --- a/src/core/Akka/IO/TcpConnection.cs +++ b/src/core/Akka/IO/TcpConnection.cs @@ -108,14 +108,14 @@ enum ConnectionStatus protected TcpConnection(TcpExt tcp, Socket socket, bool pullMode, Option writeCommandsBufferMaxSize) { if (socket == null) throw new ArgumentNullException(nameof(socket)); - + _pullMode = pullMode; _writeCommandsQueue = new PendingSimpleWritesQueue(Log, writeCommandsBufferMaxSize); _traceLogging = tcp.Settings.TraceLogging; - + Tcp = tcp; Socket = socket; - + if (pullMode) SetStatus(ConnectionStatus.ReadingSuspended); } @@ -164,21 +164,20 @@ private Receive WaitingForRegistration(IActorRef commander) var registerInfo = new ConnectionInfo(register.Handler, register.KeepOpenOnPeerClosed, register.UseResumeWriting); - // if we have resumed reading from pullMode while waiting for Register then read - if (_pullMode && !HasStatus(ConnectionStatus.ReadingSuspended)) ResumeReading(); - else if (!_pullMode) ReceiveAsync(); - Context.SetReceiveTimeout(null); Context.Become(Connected(registerInfo)); + // if we are in push mode or already have resumed reading in pullMode while waiting for Register then read + if (!_pullMode || !HasStatus(ConnectionStatus.ReadingSuspended)) ResumeReading(); + // If there is something buffered before we got Register message - put it all to the socket var bufferedWrite = GetNextWrite(); if (bufferedWrite.HasValue) { SetStatus(ConnectionStatus.Sending); DoWrite(registerInfo, bufferedWrite.Value); - } - + } + return true; case ResumeReading _: ClearStatus(ConnectionStatus.ReadingSuspended); return true; case SuspendReading _: SetStatus(ConnectionStatus.ReadingSuspended); return true; @@ -205,7 +204,7 @@ private Receive WaitingForRegistration(IActorRef commander) Log.Warning("Received Write command before Register command. " + "It will be buffered until Register will be received (buffered write size is {0} bytes)", commandSize); } - + return true; default: return false; } @@ -268,7 +267,7 @@ private Receive ClosingWithPendingWrite(ConnectionInfo info, IActorRef closeComm AcknowledgeSent(); if (IsWritePending) DoWrite(info, GetAllowedPendingWrite()); - else + else HandleClose(info, closeCommander, closedEvent); return true; case UpdatePendingWriteAndThen updatePendingWrite: @@ -277,7 +276,7 @@ private Receive ClosingWithPendingWrite(ConnectionInfo info, IActorRef closeComm if (nextWrite.HasValue) DoWrite(info, nextWrite); - else + else HandleClose(info, closeCommander, closedEvent); return true; case WriteFileFailed fail: HandleError(info.Handler, fail.Cause); return true; @@ -312,7 +311,7 @@ private Receive HandleWriteMessages(ConnectionInfo info) case SocketSent _: // Send ack to sender AcknowledgeSent(); - + // If there is something to send - send it var pendingWrite = GetAllowedPendingWrite(); if (pendingWrite.HasValue) @@ -320,14 +319,14 @@ private Receive HandleWriteMessages(ConnectionInfo info) SetStatus(ConnectionStatus.Sending); DoWrite(info, pendingWrite); } - + // If message is fully sent, notify sender who sent ResumeWriting command if (!IsWritePending && _interestedInResume != null) { _interestedInResume.Tell(WritingResumed.Instance); _interestedInResume = null; } - + return true; case WriteCommand write: if (HasStatus(ConnectionStatus.WritingSuspended)) @@ -356,7 +355,7 @@ private Receive HandleWriteMessages(ConnectionInfo info) DropWrite(info, write); return true; } - + nextWrite = GetNextWrite(headCommands: new []{ (simpleWriteCommand, Sender) }); } else @@ -364,15 +363,15 @@ private Receive HandleWriteMessages(ConnectionInfo info) _writeCommandsQueue.EnqueueSimpleWrites(write, Sender); nextWrite = GetNextWrite(); } - + // If there is something to send and we are allowed to, lets put the next command on the wire if (nextWrite.HasValue) { SetStatus(ConnectionStatus.Sending); DoWrite(info, nextWrite.Value); - } + } } - + return true; case ResumeWriting _: /* @@ -396,7 +395,7 @@ private Receive HandleWriteMessages(ConnectionInfo info) case UpdatePendingWriteAndThen updatePendingWrite: var updatedWrite = updatePendingWrite.RemainingWrite; updatePendingWrite.Work(); - if (updatedWrite.HasValue) + if (updatedWrite.HasValue) DoWrite(info, updatedWrite.Value); return true; case WriteFileFailed fail: @@ -461,7 +460,7 @@ private void AcknowledgeSent() { ackInfo.Commander.Tell(ackInfo.Ack); } - + ClearStatus(ConnectionStatus.Sending); } @@ -538,7 +537,7 @@ private void DoWrite(ConnectionInfo info, Option write) { _pendingAcks.Enqueue(pendingAck); } - + write.Value.DoWrite(info); } @@ -672,7 +671,7 @@ SocketCompleted ResolveMessage(SocketAsyncEventArgs e) throw new NotSupportedException($"Socket operation {e.LastOperation} is not supported"); } } - + var args = new SocketAsyncEventArgs(); args.UserToken = onCompleteNotificationsReceiver; args.Completed += (sender, e) => @@ -684,7 +683,7 @@ SocketCompleted ResolveMessage(SocketAsyncEventArgs e) return args; } - + protected void ReleaseSocketEventArgs(SocketAsyncEventArgs e) { e.UserToken = null; @@ -700,7 +699,7 @@ protected void ReleaseSocketEventArgs(SocketAsyncEventArgs e) catch (InvalidOperationException) { } e.Dispose(); - + } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -810,7 +809,7 @@ private Option GetNextWrite(IEnumerable<(SimpleWriteCommand Comman { return CreatePendingBufferWrite(writeCommands); } - + // No more writes out there return Option.None; } @@ -986,7 +985,7 @@ public bool EnqueueSimpleWrites(WriteCommand command, IActorRef sender) { return EnqueueSimpleWrites(command, sender, out _); } - + /// /// Adds all subcommands stored in provided command. /// Performs buffer size checks @@ -997,7 +996,7 @@ public bool EnqueueSimpleWrites(WriteCommand command, IActorRef sender) public bool EnqueueSimpleWrites(WriteCommand command, IActorRef sender, out int bufferedSize) { bufferedSize = 0; - + foreach (var writeInfo in ExtractFromCommand(command)) { var sizeAfterAppending = _totalSizeInBytes + writeInfo.DataSize; @@ -1013,10 +1012,10 @@ public bool EnqueueSimpleWrites(WriteCommand command, IActorRef sender, out int _queue.Enqueue((writeInfo.Command, sender, writeInfo.DataSize)); bufferedSize += writeInfo.DataSize; } - + return true; } - + /// /// Adds all subcommands stored in provided command. /// Performs buffer size checks for all, except first one, that is not buffered @@ -1037,7 +1036,7 @@ public bool EnqueueSimpleWritesExceptFirst(WriteCommand command, IActorRef sende first = writeInfo.Command; continue; } - + var sizeAfterAppending = _totalSizeInBytes + writeInfo.DataSize; if (_maxQueueSizeInBytes.HasValue && _maxQueueSizeInBytes.Value < sizeAfterAppending) { @@ -1061,7 +1060,7 @@ public bool EnqueueSimpleWritesExceptFirst(WriteCommand command, IActorRef sende { if (_queue.Count == 0) throw new InvalidOperationException("Write commands queue is empty"); - + var (command, sender, size) = _queue.Dequeue(); _totalSizeInBytes -= size; return (command, sender); @@ -1076,7 +1075,7 @@ public bool EnqueueSimpleWritesExceptFirst(WriteCommand command, IActorRef sende while (TryGetNext(out var command)) yield return command; } - + /// /// Gets next command from the queue, if any /// From e49973c70b979f664d438d5f41e9210a260fb422 Mon Sep 17 00:00:00 2001 From: Ismael Hamed <1279846+ismaelhamed@users.noreply.github.com> Date: Sun, 3 Apr 2022 08:40:14 +0200 Subject: [PATCH 2/2] Try not to get stopped by death pact before Unregistration is complete --- src/core/Akka/IO/TcpConnection.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/core/Akka/IO/TcpConnection.cs b/src/core/Akka/IO/TcpConnection.cs index f24b42a0c68..a25a5ce799c 100644 --- a/src/core/Akka/IO/TcpConnection.cs +++ b/src/core/Akka/IO/TcpConnection.cs @@ -13,7 +13,6 @@ using System.Linq; using System.Net.Sockets; using System.Runtime.CompilerServices; -using System.Threading; using Akka.Actor; using Akka.Dispatch; using Akka.Event; @@ -155,10 +154,7 @@ private Receive WaitingForRegistration(IActorRef commander) // up to this point we've been watching the commander, // but since registration is now complete we only need to watch the handler from here on if (!Equals(register.Handler, commander)) - { - Context.Unwatch(commander); - Context.Watch(register.Handler); - } + SignDeathPact(register.Handler); // will unsign death pact with commander automatically if (_traceLogging) Log.Debug("[{0}] registered as connection handler", register.Handler); @@ -727,6 +723,7 @@ private void Abort() protected void StopWith(CloseInformation closeInfo) { _closedMessage = closeInfo; + UnsignDeathPact(); Context.Stop(Self); }