Skip to content

Commit 23c9839

Browse files
authored
[release/7.0] Close MsQuic after checking for QUIC support to free resources (#75163, #75441) (#75521)
* Unload MsQuic after checking for QUIC support to free resources (#75163) * Revert "Revert "Unload MsQuic after checking for QUIC support to free resources. (#74749)" (#74984)" This reverts commit 953f524. * update helix images * update helix images * Improve diagnostics when opening MsQuic Co-authored-by: Radek Zikmund <radekzikmund@microsoft.com> * Don't unload MsQuic from the process (#75441) * Revert helix queues change (to be done in another PR) * Code review feedback
1 parent 264b675 commit 23c9839

File tree

1 file changed

+77
-54
lines changed
  • src/libraries/System.Net.Quic/src/System/Net/Quic/Internal

1 file changed

+77
-54
lines changed

src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs

Lines changed: 77 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics;
45
using System.Diagnostics.CodeAnalysis;
56
using System.Runtime.InteropServices;
67
using Microsoft.Quic;
@@ -17,7 +18,10 @@ internal sealed unsafe partial class MsQuicApi
1718
{
1819
private static readonly Version MinWindowsVersion = new Version(10, 0, 20145, 1000);
1920

20-
private static readonly Version MsQuicVersion = new Version(2, 1);
21+
private static readonly Version MinMsQuicVersion = new Version(2, 1);
22+
23+
private static readonly delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int> MsQuicOpenVersion;
24+
private static readonly delegate* unmanaged[Cdecl]<QUIC_API_TABLE*, void> MsQuicClose;
2125

2226
public MsQuicSafeHandle Registration { get; }
2327

@@ -47,7 +51,8 @@ private MsQuicApi(QUIC_API_TABLE* apiTable)
4751
}
4852
}
4953

50-
internal static MsQuicApi Api { get; } = null!;
54+
private static readonly Lazy<MsQuicApi> s_api = new Lazy<MsQuicApi>(AllocateMsQuicApi);
55+
internal static MsQuicApi Api => s_api.Value;
5156

5257
internal static bool IsQuicSupported { get; }
5358

@@ -56,92 +61,110 @@ private MsQuicApi(QUIC_API_TABLE* apiTable)
5661
internal static bool Tls13ServerMayBeDisabled { get; }
5762
internal static bool Tls13ClientMayBeDisabled { get; }
5863

64+
#pragma warning disable CA1810 // Initialize all static fields in 'MsQuicApi' when those fields are declared and remove the explicit static constructor
5965
static MsQuicApi()
6066
{
61-
IntPtr msQuicHandle;
62-
if (!NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MsQuicVersion.Major}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle) &&
67+
if (!NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MinMsQuicVersion.Major}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out IntPtr msQuicHandle) &&
6368
!NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle))
6469
{
70+
// MsQuic library not loaded
71+
return;
72+
}
73+
74+
MsQuicOpenVersion = (delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int>)NativeLibrary.GetExport(msQuicHandle, nameof(MsQuicOpenVersion));
75+
MsQuicClose = (delegate* unmanaged[Cdecl]<QUIC_API_TABLE*, void>)NativeLibrary.GetExport(msQuicHandle, nameof(MsQuicClose));
76+
77+
if (!TryOpenMsQuic(out QUIC_API_TABLE* apiTable, out _))
78+
{
79+
// Too low version of the library (likely pre-2.0)
6580
return;
6681
}
6782

6883
try
6984
{
70-
if (!NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress))
85+
// Check version
86+
const int ArraySize = 4;
87+
uint* libVersion = stackalloc uint[ArraySize];
88+
uint size = (uint)ArraySize * sizeof(uint);
89+
if (StatusFailed(apiTable->GetParam(null, QUIC_PARAM_GLOBAL_LIBRARY_VERSION, &size, libVersion)))
7190
{
7291
return;
7392
}
7493

75-
QUIC_API_TABLE* apiTable = null;
76-
delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int> msQuicOpenVersion = (delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int>)msQuicOpenVersionAddress;
77-
if (StatusFailed(msQuicOpenVersion((uint)MsQuicVersion.Major, &apiTable)))
94+
var version = new Version((int)libVersion[0], (int)libVersion[1], (int)libVersion[2], (int)libVersion[3]);
95+
if (version < MinMsQuicVersion)
7896
{
97+
if (NetEventSource.Log.IsEnabled())
98+
{
99+
NetEventSource.Info(null, $"Incompatible MsQuic library version '{version}', expecting at least '{MinMsQuicVersion}'");
100+
}
79101
return;
80102
}
81103

82-
try
83-
{
84-
int arraySize = 4;
85-
uint* libVersion = stackalloc uint[arraySize];
86-
uint size = (uint)arraySize * sizeof(uint);
87-
if (StatusFailed(apiTable->GetParam(null, QUIC_PARAM_GLOBAL_LIBRARY_VERSION, &size, libVersion)))
88-
{
89-
return;
90-
}
104+
// Assume SChannel is being used on windows and query for the actual provider from the library if querying is supported
105+
QUIC_TLS_PROVIDER provider = OperatingSystem.IsWindows() ? QUIC_TLS_PROVIDER.SCHANNEL : QUIC_TLS_PROVIDER.OPENSSL;
106+
size = sizeof(QUIC_TLS_PROVIDER);
107+
apiTable->GetParam(null, QUIC_PARAM_GLOBAL_TLS_PROVIDER, &size, &provider);
108+
UsesSChannelBackend = provider == QUIC_TLS_PROVIDER.SCHANNEL;
91109

92-
var version = new Version((int)libVersion[0], (int)libVersion[1], (int)libVersion[2], (int)libVersion[3]);
93-
if (version < MsQuicVersion)
110+
if (UsesSChannelBackend)
111+
{
112+
// Implies windows platform, check TLS1.3 availability
113+
if (!IsWindowsVersionSupported())
94114
{
95115
if (NetEventSource.Log.IsEnabled())
96116
{
97-
NetEventSource.Info(null, $"Incompatible MsQuic library version '{version}', expecting '{MsQuicVersion}'");
117+
NetEventSource.Info(null, $"Current Windows version ({Environment.OSVersion}) is not supported by QUIC. Minimal supported version is {MinWindowsVersion}");
98118
}
119+
99120
return;
100121
}
101122

102-
// Assume SChannel is being used on windows and query for the actual provider from the library
103-
QUIC_TLS_PROVIDER provider = OperatingSystem.IsWindows() ? QUIC_TLS_PROVIDER.SCHANNEL : QUIC_TLS_PROVIDER.OPENSSL;
104-
size = sizeof(QUIC_TLS_PROVIDER);
105-
apiTable->GetParam(null, QUIC_PARAM_GLOBAL_TLS_PROVIDER, &size, &provider);
106-
UsesSChannelBackend = provider == QUIC_TLS_PROVIDER.SCHANNEL;
123+
Tls13ServerMayBeDisabled = IsTls13Disabled(isServer: true);
124+
Tls13ClientMayBeDisabled = IsTls13Disabled(isServer: false);
125+
}
107126

108-
if (UsesSChannelBackend)
109-
{
110-
// Implies windows platform, check TLS1.3 availability
111-
if (!IsWindowsVersionSupported())
112-
{
113-
if (NetEventSource.Log.IsEnabled())
114-
{
115-
NetEventSource.Info(null, $"Current Windows version ({Environment.OSVersion}) is not supported by QUIC. Minimal supported version is {MinWindowsVersion}");
116-
}
127+
IsQuicSupported = true;
128+
}
129+
finally
130+
{
131+
// Gracefully close the API table to free resources. The API table will be allocated lazily again if needed
132+
MsQuicClose(apiTable);
133+
}
134+
}
135+
#pragma warning restore CA1810
117136

118-
return;
119-
}
137+
private static MsQuicApi AllocateMsQuicApi()
138+
{
139+
Debug.Assert(IsQuicSupported);
120140

121-
Tls13ServerMayBeDisabled = IsTls13Disabled(isServer: true);
122-
Tls13ClientMayBeDisabled = IsTls13Disabled(isServer: false);
123-
}
141+
if (!TryOpenMsQuic(out QUIC_API_TABLE* apiTable, out int openStatus))
142+
{
143+
throw ThrowHelper.GetExceptionForMsQuicStatus(openStatus);
144+
}
124145

125-
Api = new MsQuicApi(apiTable);
126-
IsQuicSupported = true;
127-
}
128-
finally
129-
{
130-
if (!IsQuicSupported && NativeLibrary.TryGetExport(msQuicHandle, "MsQuicClose", out IntPtr msQuicClose))
131-
{
132-
// Gracefully close the API table
133-
((delegate* unmanaged[Cdecl]<QUIC_API_TABLE*, void>)msQuicClose)(apiTable);
134-
}
135-
}
146+
return new MsQuicApi(apiTable);
147+
}
136148

137-
}
138-
finally
149+
private static bool TryOpenMsQuic(out QUIC_API_TABLE* apiTable, out int openStatus)
150+
{
151+
Debug.Assert(MsQuicOpenVersion != null);
152+
153+
QUIC_API_TABLE* table = null;
154+
openStatus = MsQuicOpenVersion((uint)MinMsQuicVersion.Major, &table);
155+
if (StatusFailed(openStatus))
139156
{
140-
if (!IsQuicSupported)
157+
if (NetEventSource.Log.IsEnabled())
141158
{
142-
NativeLibrary.Free(msQuicHandle);
159+
NetEventSource.Info(null, $"MsQuicOpenVersion returned {openStatus} status code.");
143160
}
161+
162+
apiTable = null;
163+
return false;
144164
}
165+
166+
apiTable = table;
167+
return true;
145168
}
146169

147170
private static bool IsWindowsVersionSupported() => OperatingSystem.IsWindowsVersionAtLeast(MinWindowsVersion.Major,

0 commit comments

Comments
 (0)