[#94122] SystemCPeripheral: Fix Dispose race condition

There was a race between the background thread receiving the TCP FIN
message (closing the socket cleanly) and the socket.Close() call in
Dispose. If the background thread did not get scheduled and got a chance
to exit cleanly before .Close() was called, the socket was aborted by
the kernel, causing an exception to be thrown.
diff --git a/src/Plugins/SystemCPlugin/Peripheral/SystemCPeripheral.cs b/src/Plugins/SystemCPlugin/Peripheral/SystemCPeripheral.cs
index cf2825c..2cfd98e 100644
--- a/src/Plugins/SystemCPlugin/Peripheral/SystemCPeripheral.cs
+++ b/src/Plugins/SystemCPlugin/Peripheral/SystemCPeripheral.cs
@@ -271,6 +271,27 @@
                 }
             }
 
+            try
+            {
+                forwardSocket?.Shutdown(SocketShutdown.Both);
+            }
+            catch(SocketException ex)
+            {
+                this.DebugLog("Exception when shutting down forward socket: {0}", ex.Message);
+            }
+            try
+            {
+                backwardSocket?.Shutdown(SocketShutdown.Both);
+            }
+            catch(SocketException ex)
+            {
+                this.DebugLog("Exception when shutting down backward socket: {0}", ex.Message);
+            }
+            if(backwardThreadStarted)
+            {
+                // Give the backward connection thread some time to gracefully shut down the TCP connection.
+                backwardThread.Join(TimeSpan.FromMilliseconds(500));
+            }
             forwardSocket?.Close();
             backwardSocket?.Close();
         }
@@ -477,6 +498,7 @@
             SendRequest(new RenodeMessage(RenodeAction.Init, 0, 0, 0, (ulong)timeSyncPeriodUS), out var response);
 
             backwardThread.Start();
+            backwardThreadStarted = true;
         }
 
         private void SetupTimesync()
@@ -733,6 +755,7 @@
         private ulong outGPIOState;
         private Process systemcProcess;
         private string systemcExecutablePath;
+        private bool backwardThreadStarted = false;
 
         private readonly Dictionary<int, IDirectAccessPeripheral> directAccessPeripherals;