From 752b1db8f0ff44df32b9b40664de8bd405c6e7bd Mon Sep 17 00:00:00 2001 From: mus65 Date: Sat, 23 Nov 2024 17:51:59 +0100 Subject: [PATCH] Add .NET 9 target (#1480) * Add .NET 9 target * Disable SonarSource S3236 This following change in the runtime now causes this analyzer to complain about some Debug.Assert calls which doesn't make sense. https://github.com/dotnet/core/blob/main/release-notes/9.0/preview/preview7/libraries.md#debugassert-now-reports-assert-condition-by-default https://rules.sonarsource.com/csharp/RSPEC-3236/ * make use of .NET 9 Lock type see https://github.com/dotnet/runtime/issues/34812 * Define own Lock type to avoid ifdefs * revert irrelevant style changes * update global.json * Keep net8.0 target in IntegrationTests Co-authored-by: Rob Hague * fix Package Downgrade Warning for some reason this happens starting with .NET 9.0 RC2: /home/mus/git/SSH.NET/test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj : error NU1605: Warning As Error: Detected package downgrade: BouncyCastle.Cryptography from 2.4.0 to 2.3.1. Reference the package directly from the project to select a different version. Renci.SshNet.IntegrationTests -> SSH.NET 1.0.0 -> BouncyCastle.Cryptography (>= 2.4.0) Renci.SshNet.IntegrationTests -> Testcontainers 3.10.0 -> BouncyCastle.Cryptography (>= 2.3.1) * update global.json to RC2 * update global.json to .NET 9 GA * update GitHub Actions for .NET 9 --------- Co-authored-by: Rob Hague --- .editorconfig | 3 +++ .github/workflows/build.yml | 18 +++++++++--------- global.json | 2 +- src/Renci.SshNet/Channels/Channel.cs | 4 ++-- .../Channels/ChannelDirectTcpip.cs | 2 +- .../Channels/ChannelForwardedTcpip.cs | 5 ++++- src/Renci.SshNet/Common/Lock.cs | 19 +++++++++++++++++++ src/Renci.SshNet/Renci.SshNet.csproj | 2 +- src/Renci.SshNet/Session.cs | 8 ++++---- src/Renci.SshNet/Sftp/SftpFileStream.cs | 2 +- src/Renci.SshNet/SshMessageFactory.cs | 5 ++++- .../Renci.SshNet.Benchmarks.csproj | 2 +- .../Renci.SshNet.IntegrationBenchmarks.csproj | 2 +- .../Renci.SshNet.IntegrationTests.csproj | 2 +- .../Common/AsyncSocketListener.cs | 6 ++++-- .../Renci.SshNet.Tests.csproj | 2 +- 16 files changed, 57 insertions(+), 27 deletions(-) create mode 100644 src/Renci.SshNet/Common/Lock.cs diff --git a/.editorconfig b/.editorconfig index 562309d42..c897d8a93 100644 --- a/.editorconfig +++ b/.editorconfig @@ -195,6 +195,9 @@ dotnet_diagnostic.S2971.severity = none # This is rather harmless. dotnet_diagnostic.S3218.severity = none +# S3236: Remove this argument from the method call; it hides the caller information. +dotnet_diagnostic.S3236.severity = none + # S3267: Loops should be simplified with "LINQ" expressions # https://rules.sonarsource.com/csharp/RSPEC-3267 # diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6e4b721d2..458e018c5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,13 +17,13 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: 8.0.x + dotnet-version: 9.0.x - name: Build Unit Tests .NET - run: dotnet build -f net8.0 test/Renci.SshNet.Tests/ + run: dotnet build -f net9.0 test/Renci.SshNet.Tests/ - name: Build IntegrationTests .NET - run: dotnet build -f net8.0 test/Renci.SshNet.IntegrationTests/ + run: dotnet build -f net9.0 test/Renci.SshNet.IntegrationTests/ - name: Build IntegrationTests .NET Framework run: dotnet build -f net48 test/Renci.SshNet.IntegrationTests/ @@ -31,25 +31,25 @@ jobs: - name: Run Unit Tests .NET run: | dotnet test \ - -f net8.0 \ + -f net9.0 \ --no-build \ --logger "console;verbosity=normal" \ --logger GitHubActions \ -p:CollectCoverage=true \ -p:CoverletOutputFormat=cobertura \ - -p:CoverletOutput=../../coverlet/linux_unit_test_net_8_coverage.xml \ + -p:CoverletOutput=../../coverlet/linux_unit_test_net_9_coverage.xml \ test/Renci.SshNet.Tests/ - name: Run Integration Tests .NET run: | dotnet test \ - -f net8.0 \ + -f net9.0 \ --no-build \ --logger "console;verbosity=normal" \ --logger GitHubActions \ -p:CollectCoverage=true \ -p:CoverletOutputFormat=cobertura \ - -p:CoverletOutput=../../coverlet/linux_integration_test_net_8_coverage.xml \ + -p:CoverletOutput=../../coverlet/linux_integration_test_net_9_coverage.xml \ test/Renci.SshNet.IntegrationTests/ # Also run a subset of the integration tests targeting netfx using mono. This is a temporary measure to get @@ -111,13 +111,13 @@ jobs: - name: Run Unit Tests .NET run: | dotnet test ` - -f net8.0 ` + -f net9.0 ` --no-build ` --logger "console;verbosity=normal" ` --logger GitHubActions ` -p:CollectCoverage=true ` -p:CoverletOutputFormat=cobertura ` - -p:CoverletOutput=../../coverlet/windows_unit_test_net_8_coverage.xml ` + -p:CoverletOutput=../../coverlet/windows_unit_test_net_9_coverage.xml ` test/Renci.SshNet.Tests/ - name: Run Unit Tests .NET Framework diff --git a/global.json b/global.json index d07970ac2..f65788943 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.100", + "version": "9.0.100", "rollForward": "latestMajor" } } diff --git a/src/Renci.SshNet/Channels/Channel.cs b/src/Renci.SshNet/Channels/Channel.cs index 4c0909f26..ca8e910fa 100644 --- a/src/Renci.SshNet/Channels/Channel.cs +++ b/src/Renci.SshNet/Channels/Channel.cs @@ -14,8 +14,8 @@ namespace Renci.SshNet.Channels /// internal abstract class Channel : IChannel { - private readonly object _serverWindowSizeLock = new object(); - private readonly object _messagingLock = new object(); + private readonly Lock _serverWindowSizeLock = new Lock(); + private readonly Lock _messagingLock = new Lock(); private readonly uint _initialWindowSize; private readonly ISession _session; private EventWaitHandle _channelClosedWaitHandle = new ManualResetEvent(initialState: false); diff --git a/src/Renci.SshNet/Channels/ChannelDirectTcpip.cs b/src/Renci.SshNet/Channels/ChannelDirectTcpip.cs index 6a07d59b0..7b00c3bae 100644 --- a/src/Renci.SshNet/Channels/ChannelDirectTcpip.cs +++ b/src/Renci.SshNet/Channels/ChannelDirectTcpip.cs @@ -14,7 +14,7 @@ namespace Renci.SshNet.Channels /// internal sealed class ChannelDirectTcpip : ClientChannel, IChannelDirectTcpip { - private readonly object _socketLock = new object(); + private readonly Lock _socketLock = new Lock(); private EventWaitHandle _channelOpen = new AutoResetEvent(initialState: false); private EventWaitHandle _channelData = new AutoResetEvent(initialState: false); diff --git a/src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs b/src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs index 07271c834..2cc0c16f8 100644 --- a/src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs +++ b/src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs @@ -1,6 +1,9 @@ using System; using System.Net; using System.Net.Sockets; +#if NET9_0_OR_GREATER +using System.Threading; +#endif using Renci.SshNet.Abstractions; using Renci.SshNet.Common; @@ -13,7 +16,7 @@ namespace Renci.SshNet.Channels /// internal sealed class ChannelForwardedTcpip : ServerChannel, IChannelForwardedTcpip { - private readonly object _socketShutdownAndCloseLock = new object(); + private readonly Lock _socketShutdownAndCloseLock = new Lock(); private Socket _socket; private IForwardedPort _forwardedPort; diff --git a/src/Renci.SshNet/Common/Lock.cs b/src/Renci.SshNet/Common/Lock.cs new file mode 100644 index 000000000..fc29776d3 --- /dev/null +++ b/src/Renci.SshNet/Common/Lock.cs @@ -0,0 +1,19 @@ +#if !NET9_0_OR_GREATER +using System.Threading; + +namespace Renci.SshNet.Common +{ + internal sealed class Lock + { + public bool TryEnter() + { + return Monitor.TryEnter(this); + } + + public void Exit() + { + Monitor.Exit(this); + } + } +} +#endif diff --git a/src/Renci.SshNet/Renci.SshNet.csproj b/src/Renci.SshNet/Renci.SshNet.csproj index bf822da85..d00f773fa 100644 --- a/src/Renci.SshNet/Renci.SshNet.csproj +++ b/src/Renci.SshNet/Renci.SshNet.csproj @@ -4,7 +4,7 @@ Renci.SshNet SSH.NET SSH.NET - net462;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0 + net462;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0;net9.0 diff --git a/src/Renci.SshNet/Session.cs b/src/Renci.SshNet/Session.cs index d914858bb..63560b685 100644 --- a/src/Renci.SshNet/Session.cs +++ b/src/Renci.SshNet/Session.cs @@ -80,7 +80,7 @@ public class Session : ISession /// Holds an object that is used to ensure only a single thread can read from /// at any given time. /// - private readonly object _socketReadLock = new object(); + private readonly Lock _socketReadLock = new Lock(); /// /// Holds an object that is used to ensure only a single thread can write to @@ -90,7 +90,7 @@ public class Session : ISession /// This is also used to ensure that is /// incremented atomatically. /// - private readonly object _socketWriteLock = new object(); + private readonly Lock _socketWriteLock = new Lock(); /// /// Holds an object that is used to ensure only a single thread can dispose @@ -1894,7 +1894,7 @@ private bool IsSocketConnected() return false; } - if (!Monitor.TryEnter(_socketReadLock)) + if (!_socketReadLock.TryEnter()) { return true; } @@ -1906,7 +1906,7 @@ private bool IsSocketConnected() } finally { - Monitor.Exit(_socketReadLock); + _socketReadLock.Exit(); } } finally diff --git a/src/Renci.SshNet/Sftp/SftpFileStream.cs b/src/Renci.SshNet/Sftp/SftpFileStream.cs index 97b0f393a..8ef5d960d 100644 --- a/src/Renci.SshNet/Sftp/SftpFileStream.cs +++ b/src/Renci.SshNet/Sftp/SftpFileStream.cs @@ -18,7 +18,7 @@ namespace Renci.SshNet.Sftp #pragma warning restore IDE0079 public class SftpFileStream : Stream { - private readonly object _lock = new object(); + private readonly Lock _lock = new Lock(); private readonly int _readBufferSize; private readonly int _writeBufferSize; diff --git a/src/Renci.SshNet/SshMessageFactory.cs b/src/Renci.SshNet/SshMessageFactory.cs index c9b8e8c10..e023f5bb0 100644 --- a/src/Renci.SshNet/SshMessageFactory.cs +++ b/src/Renci.SshNet/SshMessageFactory.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; using System.Globalization; +#if NET9_0_OR_GREATER +using System.Threading; +#endif using Renci.SshNet.Common; using Renci.SshNet.Messages; @@ -14,7 +17,7 @@ internal sealed class SshMessageFactory { private readonly MessageMetadata[] _enabledMessagesByNumber; private readonly bool[] _activatedMessagesById; - private readonly object _lock = new object(); + private readonly Lock _lock = new Lock(); internal static readonly MessageMetadata[] AllMessages = new MessageMetadata[] { diff --git a/test/Renci.SshNet.Benchmarks/Renci.SshNet.Benchmarks.csproj b/test/Renci.SshNet.Benchmarks/Renci.SshNet.Benchmarks.csproj index d07465ef1..f421ad23f 100644 --- a/test/Renci.SshNet.Benchmarks/Renci.SshNet.Benchmarks.csproj +++ b/test/Renci.SshNet.Benchmarks/Renci.SshNet.Benchmarks.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net9.0 enable enable diff --git a/test/Renci.SshNet.IntegrationBenchmarks/Renci.SshNet.IntegrationBenchmarks.csproj b/test/Renci.SshNet.IntegrationBenchmarks/Renci.SshNet.IntegrationBenchmarks.csproj index 0f149c013..3f32fe9bb 100644 --- a/test/Renci.SshNet.IntegrationBenchmarks/Renci.SshNet.IntegrationBenchmarks.csproj +++ b/test/Renci.SshNet.IntegrationBenchmarks/Renci.SshNet.IntegrationBenchmarks.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net9.0 enable enable diff --git a/test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj b/test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj index 5fa390ad4..06ae83ea6 100644 --- a/test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj +++ b/test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj @@ -1,7 +1,7 @@  - net48;net8.0 + net48;net8.0;net9.0 enable true $(NoWarn);SYSLIB0021;SYSLIB1045;SYSLIB0014;IDE0220;IDE0010 diff --git a/test/Renci.SshNet.Tests/Common/AsyncSocketListener.cs b/test/Renci.SshNet.Tests/Common/AsyncSocketListener.cs index a27db194d..23e48799a 100644 --- a/test/Renci.SshNet.Tests/Common/AsyncSocketListener.cs +++ b/test/Renci.SshNet.Tests/Common/AsyncSocketListener.cs @@ -4,6 +4,8 @@ using System.Net; using System.Net.Sockets; using System.Threading; + +using Renci.SshNet.Common; #pragma warning restore IDE0005 namespace Renci.SshNet.Tests.Common @@ -13,7 +15,7 @@ public class AsyncSocketListener : IDisposable private readonly IPEndPoint _endPoint; private readonly ManualResetEvent _acceptCallbackDone; private readonly List _connectedClients; - private readonly object _syncLock; + private readonly Lock _syncLock; private Socket _listener; private Thread _receiveThread; private bool _started; @@ -31,7 +33,7 @@ public AsyncSocketListener(IPEndPoint endPoint) _endPoint = endPoint; _acceptCallbackDone = new ManualResetEvent(false); _connectedClients = new List(); - _syncLock = new object(); + _syncLock = new Lock(); ShutdownRemoteCommunicationSocket = true; } diff --git a/test/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj b/test/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj index df872d754..4b91384d3 100644 --- a/test/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj +++ b/test/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj @@ -1,7 +1,7 @@  - net462;net6.0;net7.0;net8.0 + net462;net6.0;net7.0;net8.0;net9.0