Skip to content

Commit

Permalink
Unload MsQuic after checking for QUIC support to free resources. (#74749
Browse files Browse the repository at this point in the history
)

* Unload MsQuic after checking for QUIC support to free resources.

* Unload MsQuic unconditionally

* Code review feedback
  • Loading branch information
rzikm authored Sep 1, 2022
1 parent 82b85b3 commit 7a45201
Showing 1 changed file with 59 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -47,7 +48,8 @@ private MsQuicApi(QUIC_API_TABLE* apiTable)
}
}

internal static MsQuicApi Api { get; } = null!;
private static readonly Lazy<MsQuicApi> _api = new Lazy<MsQuicApi>(AllocateMsQuicApi);
internal static MsQuicApi Api => _api.Value;

internal static bool IsQuicSupported { get; }

Expand All @@ -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]<uint, QUIC_API_TABLE**, int> msQuicOpenVersion = (delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int>)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);
Expand All @@ -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);
Expand All @@ -122,26 +116,67 @@ 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]<QUIC_API_TABLE*, void>)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
{
if (!IsQuicSupported)
{
NativeLibrary.Free(msQuicHandle);
}
// Unload the library, we will load it again when we actually use QUIC
NativeLibrary.Free(msQuicHandle);
}
}

private 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");
}

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);

private 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]<uint, QUIC_API_TABLE**, int> msQuicOpenVersion = (delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int>)msQuicOpenVersionAddress;
if (StatusFailed(msQuicOpenVersion((uint)MsQuicVersion.Major, &table)))
{
return false;
}

apiTable = table;
return true;
}

private static bool TryCloseMsQuic(IntPtr msQuicHandle, QUIC_API_TABLE* apiTable)
{
if (NativeLibrary.TryGetExport(msQuicHandle, "MsQuicClose", out IntPtr msQuicClose))
{
((delegate* unmanaged[Cdecl]<QUIC_API_TABLE*, void>)msQuicClose)(apiTable);
return true;
}

return false;
}

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

0 comments on commit 7a45201

Please sign in to comment.