Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support trimming distributed transactions if unused, without a feature switch. #3

Merged
merged 1 commit into from
Sep 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ public static partial class TransactionManager
[System.Diagnostics.CodeAnalysis.DisallowNullAttribute]
public static System.Transactions.HostCurrentTransactionCallback? HostCurrentCallback { get { throw null; } set { } }
public static System.TimeSpan MaximumTimeout { get { throw null; } set { } }
public static bool ImplicitDistributedTransactions { get; set; }
public static bool ImplicitDistributedTransactions { get; [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Distributed transactions support may not be compatible with trimming. If your program creates a distributed transaction via System.Transactions, the correctness of the application cannot be guaranteed after trimming.")] set; }
public static event System.Transactions.TransactionStartedEventHandler? DistributedTransactionStarted { add { } remove { } }
public static void RecoveryComplete(System.Guid resourceManagerIdentifier) { }
public static System.Transactions.Enlistment Reenlist(System.Guid resourceManagerIdentifier, byte[] recoveryInformation, System.Transactions.IEnlistmentNotification enlistmentNotification) { throw null; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ internal sealed class DtcProxyShimFactory
// at the same time.
private static readonly object _proxyInitLock = new();

// This object will perform the actual distributed transaction connection.
// It will be set only if TransactionManager.ImplicitDefaultTransactions
// is set to true, allowing the relevant code to be trimmed otherwise.
internal static ITransactionConnector? s_transactionConnector;

// Lock to protect access to listOfNotifications.
private readonly object _notificationLock = new();

Expand All @@ -39,6 +44,7 @@ internal DtcProxyShimFactory(EventWaitHandle notificationEventHandle)

// https://docs.microsoft.com/previous-versions/windows/desktop/ms678898(v=vs.85)
[DllImport(Interop.Libraries.Xolehlp, CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = false)]
[RequiresUnreferencedCode(TransactionManager.DistributedTransactionTrimmingWarning)]
private static extern void DtcGetTransactionManagerExW(
[MarshalAs(UnmanagedType.LPWStr)] string? pszHost,
[MarshalAs(UnmanagedType.LPWStr)] string? pszTmName,
Expand All @@ -47,7 +53,7 @@ private static extern void DtcGetTransactionManagerExW(
object? pvConfigPararms,
[MarshalAs(UnmanagedType.Interface)] out ITransactionDispenser ppvObject);

[RequiresUnreferencedCode("Distributed transactions support may not be compatible with trimming. If your program creates a distributed transaction via System.Transactions, the correctness of the application cannot be guaranteed after trimming.")]
[RequiresUnreferencedCode(TransactionManager.DistributedTransactionTrimmingWarning)]
private static void DtcGetTransactionManager(string? nodeName, out ITransactionDispenser localDispenser) =>
DtcGetTransactionManagerExW(nodeName, null, Guids.IID_ITransactionDispenser_Guid, 0, null, out localDispenser);

Expand All @@ -64,14 +70,15 @@ public void ConnectToProxy(
throw new PlatformNotSupportedException(SR.DistributedNotSupportedOn32Bits);
}

if (!TransactionManager.ImplicitDistributedTransactions)
if (s_transactionConnector is null)
{
throw new NotSupportedException(SR.ImplicitDistributedTransactionsDisabled);
}

ConnectToProxyCore(nodeName, resourceManagerIdentifier, managedIdentifier, out nodeNameMatches, out whereabouts, out resourceManagerShim);
s_transactionConnector.ConnectToProxyCore(this, nodeName, resourceManagerIdentifier, managedIdentifier, out nodeNameMatches, out whereabouts, out resourceManagerShim);
}

[RequiresUnreferencedCode(TransactionManager.DistributedTransactionTrimmingWarning)]
private void ConnectToProxyCore(
string? nodeName,
Guid resourceManagerIdentifier,
Expand All @@ -82,9 +89,7 @@ private void ConnectToProxyCore(
{
lock (_proxyInitLock)
{
#pragma warning disable IL2026 // This warning is left in the product so developers get an ILLink warning when trimming an app using this transaction support
DtcGetTransactionManager(nodeName, out ITransactionDispenser? localDispenser);
#pragma warning restore IL2026

// Check to make sure the node name matches.
if (nodeName is not null)
Expand Down Expand Up @@ -371,4 +376,38 @@ internal ITransactionReceiver GetCachedReceiver()

internal void ReturnCachedReceiver(ITransactionReceiver receiver)
=> _cachedReceivers.Enqueue(receiver);

internal interface ITransactionConnector
{
void ConnectToProxyCore(
DtcProxyShimFactory proxyShimFactory,
string? nodeName,
Guid resourceManagerIdentifier,
object managedIdentifier,
out bool nodeNameMatches,
out byte[] whereabouts,
out ResourceManagerShim resourceManagerShim);
}

[RequiresUnreferencedCode(TransactionManager.DistributedTransactionTrimmingWarning)]
internal sealed class DtcTransactionConnector : ITransactionConnector
{
public void ConnectToProxyCore(
DtcProxyShimFactory proxyShimFactory,
string? nodeName,
Guid resourceManagerIdentifier,
object managedIdentifier,
out bool nodeNameMatches,
out byte[] whereabouts,
out ResourceManagerShim resourceManagerShim)
{
proxyShimFactory.ConnectToProxyCore(
nodeName,
resourceManagerIdentifier,
managedIdentifier,
out nodeNameMatches,
out whereabouts,
out resourceManagerShim);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
using System.IO;
using System.Threading;
using System.Transactions.Configuration;
#if WINDOWS
using System.Transactions.DtcProxyShim;
#endif
using System.Transactions.Oletx;

namespace System.Transactions
Expand All @@ -29,6 +32,10 @@ public static class TransactionManager
private static TransactionTable? s_transactionTable;

private static TransactionStartedEventHandler? s_distributedTransactionStartedDelegate;

internal const string DistributedTransactionTrimmingWarning =
"Distributed transactions support may not be compatible with trimming. If your program creates a distributed transaction via System.Transactions, the correctness of the application cannot be guaranteed after trimming.";

public static event TransactionStartedEventHandler? DistributedTransactionStarted
{
add
Expand Down Expand Up @@ -391,7 +398,31 @@ public static TimeSpan MaximumTimeout
}
}

public static bool ImplicitDistributedTransactions { get; set; }
#if WINDOWS
public static bool ImplicitDistributedTransactions
{
get => DtcProxyShimFactory.s_transactionConnector is not null;
[RequiresUnreferencedCode(DistributedTransactionTrimmingWarning)]
set
{
if (value)
{
DtcProxyShimFactory.s_transactionConnector ??= new DtcProxyShimFactory.DtcTransactionConnector();
}
else
{
DtcProxyShimFactory.s_transactionConnector = null;
}
}
}
#else
public static bool ImplicitDistributedTransactions
{
get;
[RequiresUnreferencedCode(DistributedTransactionTrimmingWarning)]
set;
}
#endif

// This routine writes the "header" for the recovery information, based on the
// type of the calling object and its provided parameter collection. This information
Expand Down