From fee1c6db7e52a99e01a4c5696443f93aa1bb3b4c Mon Sep 17 00:00:00 2001 From: Radek Zikmund Date: Mon, 29 Aug 2022 13:33:51 +0200 Subject: [PATCH 1/3] Unload MsQuic after checking for QUIC support to free resources. --- .../src/System/Net/Quic/Internal/MsQuicApi.cs | 77 ++++++++++++++----- 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs index e2866454356dd2..5b2420431a60ea 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/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 Microsoft.Quic; @@ -47,7 +48,8 @@ private MsQuicApi(QUIC_API_TABLE* apiTable) } } - internal static MsQuicApi Api { get; } = null!; + internal static Lazy _api = new Lazy(AllocateMsQuicApi); + internal static MsQuicApi Api => _api.Value; internal static bool IsQuicSupported { get; } @@ -58,29 +60,21 @@ private MsQuicApi(QUIC_API_TABLE* apiTable) static MsQuicApi() { - IntPtr msQuicHandle; - if (!NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MsQuicVersion.Major}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle) && - !NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle)) + if (!TryLoadMsQuic(out IntPtr msQuicHandle)) { return; } try { - if (!NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress)) - { - return; - } - - QUIC_API_TABLE* apiTable = null; - delegate* unmanaged[Cdecl] msQuicOpenVersion = (delegate* unmanaged[Cdecl])msQuicOpenVersionAddress; - if (StatusFailed(msQuicOpenVersion((uint)MsQuicVersion.Major, &apiTable))) + if (!TryOpenMsQuic(msQuicHandle, out QUIC_API_TABLE* apiTable)) { return; } try { + // check version int arraySize = 4; uint* libVersion = stackalloc uint[arraySize]; uint size = (uint)arraySize * sizeof(uint); @@ -99,7 +93,7 @@ static MsQuicApi() return; } - // Assume SChannel is being used on windows and query for the actual provider from the library + // Assume SChannel is being used on windows and query for the actual provider from the library if querying is supported QUIC_TLS_PROVIDER provider = OperatingSystem.IsWindows() ? QUIC_TLS_PROVIDER.SCHANNEL : QUIC_TLS_PROVIDER.OPENSSL; size = sizeof(QUIC_TLS_PROVIDER); apiTable->GetParam(null, QUIC_PARAM_GLOBAL_TLS_PROVIDER, &size, &provider); @@ -122,18 +116,14 @@ static MsQuicApi() Tls13ClientMayBeDisabled = IsTls13Disabled(isServer: false); } - Api = new MsQuicApi(apiTable); IsQuicSupported = true; } finally { - if (!IsQuicSupported && NativeLibrary.TryGetExport(msQuicHandle, "MsQuicClose", out IntPtr msQuicClose)) - { - // Gracefully close the API table - ((delegate* unmanaged[Cdecl])msQuicClose)(apiTable); - } + // Gracefully close the API table to free resources. The API table will be allocated lazily again if needed + bool closed = TryCloseMsQuic(msQuicHandle, apiTable); + Debug.Assert(closed, "Failed to close MsQuic"); } - } finally { @@ -144,6 +134,53 @@ static MsQuicApi() } } + internal static MsQuicApi AllocateMsQuicApi() + { + Debug.Assert(IsQuicSupported); + + if (TryLoadMsQuic(out IntPtr msQuicHandle) && + TryOpenMsQuic(msQuicHandle, out QUIC_API_TABLE* apiTable)) + { + return new MsQuicApi(apiTable); + } + + throw new Exception("Failed to create MsQuicApi instance"); + } + + internal static bool TryLoadMsQuic(out IntPtr msQuicHandle) => + NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MsQuicVersion.Major}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle) || + NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle); + + internal static bool TryOpenMsQuic(IntPtr msQuicHandle, out QUIC_API_TABLE* apiTable) + { + apiTable = null; + if (!NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress)) + { + return false; + } + + QUIC_API_TABLE* table = null; + delegate* unmanaged[Cdecl] msQuicOpenVersion = (delegate* unmanaged[Cdecl])msQuicOpenVersionAddress; + if (StatusFailed(msQuicOpenVersion((uint)MsQuicVersion.Major, &table))) + { + return false; + } + + apiTable = table; + return true; + } + + internal static bool TryCloseMsQuic(IntPtr msQuicHandle, QUIC_API_TABLE* apiTable) + { + if (NativeLibrary.TryGetExport(msQuicHandle, "MsQuicClose", out IntPtr msQuicClose)) + { + ((delegate* unmanaged[Cdecl])msQuicClose)(apiTable); + return true; + } + + return false; + } + private static bool IsWindowsVersionSupported() => OperatingSystem.IsWindowsVersionAtLeast(MinWindowsVersion.Major, MinWindowsVersion.Minor, MinWindowsVersion.Build, MinWindowsVersion.Revision); From bf47ac0810e53d13a57d10ff1c8b948e5bc4b516 Mon Sep 17 00:00:00 2001 From: Radek Zikmund <32671551+rzikm@users.noreply.github.com> Date: Wed, 31 Aug 2022 19:10:51 +0200 Subject: [PATCH 2/3] Unload MsQuic unconditionally --- .../src/System/Net/Quic/Internal/MsQuicApi.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs index 5b2420431a60ea..1d78917f52825b 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs @@ -127,10 +127,8 @@ static MsQuicApi() } finally { - if (!IsQuicSupported) - { - NativeLibrary.Free(msQuicHandle); - } + // Unload the library, we will load it again when we actually use QUIC + NativeLibrary.Free(msQuicHandle); } } From 162c33134953925a18d887672678116b8e38f74e Mon Sep 17 00:00:00 2001 From: Radek Zikmund Date: Thu, 1 Sep 2022 15:39:32 +0200 Subject: [PATCH 3/3] Code review feedback --- .../src/System/Net/Quic/Internal/MsQuicApi.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs index 1d78917f52825b..eb06b9dce8e645 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs @@ -48,7 +48,7 @@ private MsQuicApi(QUIC_API_TABLE* apiTable) } } - internal static Lazy _api = new Lazy(AllocateMsQuicApi); + private static readonly Lazy _api = new Lazy(AllocateMsQuicApi); internal static MsQuicApi Api => _api.Value; internal static bool IsQuicSupported { get; } @@ -74,7 +74,7 @@ static MsQuicApi() try { - // check version + // Check version int arraySize = 4; uint* libVersion = stackalloc uint[arraySize]; uint size = (uint)arraySize * sizeof(uint); @@ -132,7 +132,7 @@ static MsQuicApi() } } - internal static MsQuicApi AllocateMsQuicApi() + private static MsQuicApi AllocateMsQuicApi() { Debug.Assert(IsQuicSupported); @@ -145,11 +145,11 @@ internal static MsQuicApi AllocateMsQuicApi() throw new Exception("Failed to create MsQuicApi instance"); } - internal static bool TryLoadMsQuic(out IntPtr msQuicHandle) => + private static bool TryLoadMsQuic(out IntPtr msQuicHandle) => NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MsQuicVersion.Major}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle) || NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle); - internal static bool TryOpenMsQuic(IntPtr msQuicHandle, out QUIC_API_TABLE* apiTable) + private static bool TryOpenMsQuic(IntPtr msQuicHandle, out QUIC_API_TABLE* apiTable) { apiTable = null; if (!NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress)) @@ -168,7 +168,7 @@ internal static bool TryOpenMsQuic(IntPtr msQuicHandle, out QUIC_API_TABLE* apiT return true; } - internal static bool TryCloseMsQuic(IntPtr msQuicHandle, QUIC_API_TABLE* apiTable) + private static bool TryCloseMsQuic(IntPtr msQuicHandle, QUIC_API_TABLE* apiTable) { if (NativeLibrary.TryGetExport(msQuicHandle, "MsQuicClose", out IntPtr msQuicClose)) {