Skip to content

Commit fee1c6d

Browse files
committed
Unload MsQuic after checking for QUIC support to free resources.
1 parent 48a3dbe commit fee1c6d

File tree

1 file changed

+57
-20
lines changed
  • src/libraries/System.Net.Quic/src/System/Net/Quic/Internal

1 file changed

+57
-20
lines changed

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

Lines changed: 57 additions & 20 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;
@@ -47,7 +48,8 @@ private MsQuicApi(QUIC_API_TABLE* apiTable)
4748
}
4849
}
4950

50-
internal static MsQuicApi Api { get; } = null!;
51+
internal static Lazy<MsQuicApi> _api = new Lazy<MsQuicApi>(AllocateMsQuicApi);
52+
internal static MsQuicApi Api => _api.Value;
5153

5254
internal static bool IsQuicSupported { get; }
5355

@@ -58,29 +60,21 @@ private MsQuicApi(QUIC_API_TABLE* apiTable)
5860

5961
static MsQuicApi()
6062
{
61-
IntPtr msQuicHandle;
62-
if (!NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MsQuicVersion.Major}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle) &&
63-
!NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle))
63+
if (!TryLoadMsQuic(out IntPtr msQuicHandle))
6464
{
6565
return;
6666
}
6767

6868
try
6969
{
70-
if (!NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress))
71-
{
72-
return;
73-
}
74-
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)))
70+
if (!TryOpenMsQuic(msQuicHandle, out QUIC_API_TABLE* apiTable))
7871
{
7972
return;
8073
}
8174

8275
try
8376
{
77+
// check version
8478
int arraySize = 4;
8579
uint* libVersion = stackalloc uint[arraySize];
8680
uint size = (uint)arraySize * sizeof(uint);
@@ -99,7 +93,7 @@ static MsQuicApi()
9993
return;
10094
}
10195

102-
// Assume SChannel is being used on windows and query for the actual provider from the library
96+
// Assume SChannel is being used on windows and query for the actual provider from the library if querying is supported
10397
QUIC_TLS_PROVIDER provider = OperatingSystem.IsWindows() ? QUIC_TLS_PROVIDER.SCHANNEL : QUIC_TLS_PROVIDER.OPENSSL;
10498
size = sizeof(QUIC_TLS_PROVIDER);
10599
apiTable->GetParam(null, QUIC_PARAM_GLOBAL_TLS_PROVIDER, &size, &provider);
@@ -122,18 +116,14 @@ static MsQuicApi()
122116
Tls13ClientMayBeDisabled = IsTls13Disabled(isServer: false);
123117
}
124118

125-
Api = new MsQuicApi(apiTable);
126119
IsQuicSupported = true;
127120
}
128121
finally
129122
{
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-
}
123+
// Gracefully close the API table to free resources. The API table will be allocated lazily again if needed
124+
bool closed = TryCloseMsQuic(msQuicHandle, apiTable);
125+
Debug.Assert(closed, "Failed to close MsQuic");
135126
}
136-
137127
}
138128
finally
139129
{
@@ -144,6 +134,53 @@ static MsQuicApi()
144134
}
145135
}
146136

137+
internal static MsQuicApi AllocateMsQuicApi()
138+
{
139+
Debug.Assert(IsQuicSupported);
140+
141+
if (TryLoadMsQuic(out IntPtr msQuicHandle) &&
142+
TryOpenMsQuic(msQuicHandle, out QUIC_API_TABLE* apiTable))
143+
{
144+
return new MsQuicApi(apiTable);
145+
}
146+
147+
throw new Exception("Failed to create MsQuicApi instance");
148+
}
149+
150+
internal static bool TryLoadMsQuic(out IntPtr msQuicHandle) =>
151+
NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MsQuicVersion.Major}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle) ||
152+
NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle);
153+
154+
internal static bool TryOpenMsQuic(IntPtr msQuicHandle, out QUIC_API_TABLE* apiTable)
155+
{
156+
apiTable = null;
157+
if (!NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress))
158+
{
159+
return false;
160+
}
161+
162+
QUIC_API_TABLE* table = null;
163+
delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int> msQuicOpenVersion = (delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int>)msQuicOpenVersionAddress;
164+
if (StatusFailed(msQuicOpenVersion((uint)MsQuicVersion.Major, &table)))
165+
{
166+
return false;
167+
}
168+
169+
apiTable = table;
170+
return true;
171+
}
172+
173+
internal static bool TryCloseMsQuic(IntPtr msQuicHandle, QUIC_API_TABLE* apiTable)
174+
{
175+
if (NativeLibrary.TryGetExport(msQuicHandle, "MsQuicClose", out IntPtr msQuicClose))
176+
{
177+
((delegate* unmanaged[Cdecl]<QUIC_API_TABLE*, void>)msQuicClose)(apiTable);
178+
return true;
179+
}
180+
181+
return false;
182+
}
183+
147184
private static bool IsWindowsVersionSupported() => OperatingSystem.IsWindowsVersionAtLeast(MinWindowsVersion.Major,
148185
MinWindowsVersion.Minor, MinWindowsVersion.Build, MinWindowsVersion.Revision);
149186

0 commit comments

Comments
 (0)