Skip to content

Commit 7a45201

Browse files
authored
Unload MsQuic after checking for QUIC support to free resources. (#74749)
* Unload MsQuic after checking for QUIC support to free resources. * Unload MsQuic unconditionally * Code review feedback
1 parent 82b85b3 commit 7a45201

File tree

1 file changed

+59
-24
lines changed
  • src/libraries/System.Net.Quic/src/System/Net/Quic/Internal

1 file changed

+59
-24
lines changed

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

Lines changed: 59 additions & 24 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+
private static readonly 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,26 +116,67 @@ 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
{
140-
if (!IsQuicSupported)
141-
{
142-
NativeLibrary.Free(msQuicHandle);
143-
}
130+
// Unload the library, we will load it again when we actually use QUIC
131+
NativeLibrary.Free(msQuicHandle);
132+
}
133+
}
134+
135+
private static MsQuicApi AllocateMsQuicApi()
136+
{
137+
Debug.Assert(IsQuicSupported);
138+
139+
if (TryLoadMsQuic(out IntPtr msQuicHandle) &&
140+
TryOpenMsQuic(msQuicHandle, out QUIC_API_TABLE* apiTable))
141+
{
142+
return new MsQuicApi(apiTable);
143+
}
144+
145+
throw new Exception("Failed to create MsQuicApi instance");
146+
}
147+
148+
private static bool TryLoadMsQuic(out IntPtr msQuicHandle) =>
149+
NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MsQuicVersion.Major}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle) ||
150+
NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle);
151+
152+
private static bool TryOpenMsQuic(IntPtr msQuicHandle, out QUIC_API_TABLE* apiTable)
153+
{
154+
apiTable = null;
155+
if (!NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress))
156+
{
157+
return false;
158+
}
159+
160+
QUIC_API_TABLE* table = null;
161+
delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int> msQuicOpenVersion = (delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int>)msQuicOpenVersionAddress;
162+
if (StatusFailed(msQuicOpenVersion((uint)MsQuicVersion.Major, &table)))
163+
{
164+
return false;
144165
}
166+
167+
apiTable = table;
168+
return true;
169+
}
170+
171+
private static bool TryCloseMsQuic(IntPtr msQuicHandle, QUIC_API_TABLE* apiTable)
172+
{
173+
if (NativeLibrary.TryGetExport(msQuicHandle, "MsQuicClose", out IntPtr msQuicClose))
174+
{
175+
((delegate* unmanaged[Cdecl]<QUIC_API_TABLE*, void>)msQuicClose)(apiTable);
176+
return true;
177+
}
178+
179+
return false;
145180
}
146181

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

0 commit comments

Comments
 (0)