From a58e25ac7ef0da0e4e66113331b8a80a34517116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= <11718369+ManickaP@users.noreply.github.com> Date: Wed, 8 Feb 2023 01:38:49 +0100 Subject: [PATCH] Close MsQuic after checking for QUIC support to free resources (#75163, #75441) (#80785) Co-authored-by: Radek Zikmund <32671551+rzikm@users.noreply.github.com> --- .../MsQuic/Internal/MsQuicApi.cs | 77 ++++++++++++++----- 1 file changed, 56 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs index e2b46f2bb89ed..7392b4de5daa6 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods; @@ -117,12 +118,17 @@ private MsQuicApi(NativeApi* vtable) Registration = handle; } - internal static MsQuicApi Api { get; } = null!; + private static readonly delegate* unmanaged[Cdecl] MsQuicOpenVersion; + private static readonly delegate* unmanaged[Cdecl] MsQuicClose; + + private static readonly Lazy s_api = new Lazy(AllocateMsQuicApi); + internal static MsQuicApi Api => s_api.Value; internal static bool IsQuicSupported { get; } private const int MsQuicVersion = 1; +#pragma warning disable CA1810 // Initialize all static fields in 'MsQuicApi' when those fields are declared and remove the explicit static constructor static MsQuicApi() { if (OperatingSystem.IsWindows() && !IsWindowsVersionSupported()) @@ -135,30 +141,59 @@ static MsQuicApi() return; } - if (NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out IntPtr msQuicHandle)) + if (!NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out IntPtr msQuicHandle)) { - try - { - if (NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress)) - { - delegate* unmanaged[Cdecl] msQuicOpenVersion = - (delegate* unmanaged[Cdecl])msQuicOpenVersionAddress; - uint status = msQuicOpenVersion(MsQuicVersion, out NativeApi* vtable); - if (MsQuicStatusHelper.SuccessfulStatusCode(status)) - { - IsQuicSupported = true; - Api = new MsQuicApi(vtable); - } - } - } - finally + // MsQuic library not loaded + return; + } + + MsQuicOpenVersion = (delegate* unmanaged[Cdecl])NativeLibrary.GetExport(msQuicHandle, nameof(MsQuicOpenVersion)); + MsQuicClose = (delegate* unmanaged[Cdecl])NativeLibrary.GetExport(msQuicHandle, nameof(MsQuicClose)); + + if (!TryOpenMsQuic(out NativeApi* apiTable, out _)) + { + // Different version of the library. + return; + } + + IsQuicSupported = true; + + // Gracefully close the API table to free resources. The API table will be allocated lazily again if needed + MsQuicClose(apiTable); + } +#pragma warning restore CA1810 + + private static MsQuicApi AllocateMsQuicApi() + { + Debug.Assert(IsQuicSupported); + + if (!TryOpenMsQuic(out NativeApi* apiTable, out uint openStatus)) + { + QuicExceptionHelpers.ThrowIfFailed(openStatus); + } + + return new MsQuicApi(apiTable); + } + + private static bool TryOpenMsQuic(out NativeApi* apiTable, out uint openStatus) + { + Debug.Assert(MsQuicOpenVersion != null); + + NativeApi* table = null; + openStatus = MsQuicOpenVersion((uint)MsQuicVersion, &table); + if (!MsQuicStatusHelper.SuccessfulStatusCode(openStatus)) + { + if (NetEventSource.Log.IsEnabled()) { - if (!IsQuicSupported) - { - NativeLibrary.Free(msQuicHandle); - } + NetEventSource.Info(null, $"MsQuicOpenVersion(version: {MsQuicVersion}) returned {MsQuicStatusCodes.GetError(openStatus)} status code."); } + + apiTable = null; + return false; } + + apiTable = table; + return true; } private static bool IsWindowsVersionSupported() => OperatingSystem.IsWindowsVersionAtLeast(MinWindowsVersion.Major,