From 8fc905e02b56b556ce7420a7b9fb2dbc6518d6d4 Mon Sep 17 00:00:00 2001 From: Carol Wang Date: Tue, 2 Aug 2022 17:12:32 +0800 Subject: [PATCH] Add setter to TransactionManager.DefaultTimeout and MaxTimeout. (#71703) * Add setter to TransactionManager.DefaultTimeout and MaxTimeout. * Address review feedback. * Interlocked access timespan to avoid data tear on x86 machine. * Remove redundant Interlocked.Read. --- .../ref/System.Transactions.Local.cs | 4 +- .../src/System/Transactions/TransactionManager.cs | 68 +++++++++++++++++++--- .../tests/System.Transactions.Local.Tests.csproj | 1 + .../tests/TransactionManagerTest.cs | 31 ++++++++++ 4 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 src/libraries/System.Transactions.Local/tests/TransactionManagerTest.cs diff --git a/src/libraries/System.Transactions.Local/ref/System.Transactions.Local.cs b/src/libraries/System.Transactions.Local/ref/System.Transactions.Local.cs index f52ae1f..2f08b17 100644 --- a/src/libraries/System.Transactions.Local/ref/System.Transactions.Local.cs +++ b/src/libraries/System.Transactions.Local/ref/System.Transactions.Local.cs @@ -187,10 +187,10 @@ namespace System.Transactions } public static partial class TransactionManager { - public static System.TimeSpan DefaultTimeout { get { throw null; } } + public static System.TimeSpan DefaultTimeout { get { throw null; } set { } } [System.Diagnostics.CodeAnalysis.DisallowNullAttribute] public static System.Transactions.HostCurrentTransactionCallback? HostCurrentCallback { get { throw null; } set { } } - public static System.TimeSpan MaximumTimeout { get { throw null; } } + public static System.TimeSpan MaximumTimeout { get { throw null; } 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; } diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionManager.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionManager.cs index c4d3c4a..ff811ad 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionManager.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionManager.cs @@ -279,7 +279,7 @@ namespace System.Transactions private static MachineSettingsSection MachineSettings => s_machineSettings ??= MachineSettingsSection.GetSection(); private static bool s_defaultTimeoutValidated; - private static TimeSpan s_defaultTimeout; + private static long s_defaultTimeoutTicks; public static TimeSpan DefaultTimeout { get @@ -292,23 +292,45 @@ namespace System.Transactions if (!s_defaultTimeoutValidated) { - s_defaultTimeout = ValidateTimeout(DefaultSettingsSection.Timeout); - // If the timeout value got adjusted, it must have been greater than MaximumTimeout. - if (s_defaultTimeout != DefaultSettingsSection.Timeout) + LazyInitializer.EnsureInitialized(ref s_defaultTimeoutTicks, ref s_defaultTimeoutValidated, ref s_classSyncObject, () => ValidateTimeout(DefaultSettingsSection.Timeout).Ticks); + if (Interlocked.Read(ref s_defaultTimeoutTicks) != DefaultSettingsSection.Timeout.Ticks) { if (etwLog.IsEnabled()) { etwLog.ConfiguredDefaultTimeoutAdjusted(); } } - s_defaultTimeoutValidated = true; } if (etwLog.IsEnabled()) { etwLog.MethodExit(TraceSourceType.TraceSourceBase, "TransactionManager.get_DefaultTimeout"); } - return s_defaultTimeout; + return new TimeSpan(Interlocked.Read(ref s_defaultTimeoutTicks)); + } + set + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceBase, "TransactionManager.set_DefaultTimeout"); + } + + Interlocked.Exchange(ref s_defaultTimeoutTicks, ValidateTimeout(value).Ticks); + if (Interlocked.Read(ref s_defaultTimeoutTicks) != value.Ticks) + { + if (etwLog.IsEnabled()) + { + etwLog.ConfiguredDefaultTimeoutAdjusted(); + } + } + + s_defaultTimeoutValidated = true; + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceBase, "TransactionManager.set_DefaultTimeout"); + } } } @@ -325,7 +347,7 @@ namespace System.Transactions etwLog.MethodEnter(TraceSourceType.TraceSourceBase, "TransactionManager.get_DefaultMaximumTimeout"); } - LazyInitializer.EnsureInitialized(ref s_maximumTimeout, ref s_cachedMaxTimeout, ref s_classSyncObject, () => DefaultSettingsSection.Timeout); + LazyInitializer.EnsureInitialized(ref s_maximumTimeout, ref s_cachedMaxTimeout, ref s_classSyncObject, () => MachineSettingsSection.MaxTimeout); if (etwLog.IsEnabled()) { @@ -334,6 +356,38 @@ namespace System.Transactions return s_maximumTimeout; } + set + { + TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; + if (etwLog.IsEnabled()) + { + etwLog.MethodEnter(TraceSourceType.TraceSourceBase, "TransactionManager.set_DefaultMaximumTimeout"); + } + + if (value < TimeSpan.Zero) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + + s_cachedMaxTimeout = true; + s_maximumTimeout = value; + LazyInitializer.EnsureInitialized(ref s_defaultTimeoutTicks, ref s_defaultTimeoutValidated, ref s_classSyncObject, () => DefaultSettingsSection.Timeout.Ticks); + + long defaultTimeoutTicks = Interlocked.Read(ref s_defaultTimeoutTicks); + Interlocked.Exchange(ref s_defaultTimeoutTicks, ValidateTimeout(new TimeSpan(defaultTimeoutTicks)).Ticks); + if (Interlocked.Read(ref s_defaultTimeoutTicks) != defaultTimeoutTicks) + { + if (etwLog.IsEnabled()) + { + etwLog.ConfiguredDefaultTimeoutAdjusted(); + } + } + + if (etwLog.IsEnabled()) + { + etwLog.MethodExit(TraceSourceType.TraceSourceBase, "TransactionManager.set_DefaultMaximumTimeout"); + } + } } /// diff --git a/src/libraries/System.Transactions.Local/tests/System.Transactions.Local.Tests.csproj b/src/libraries/System.Transactions.Local/tests/System.Transactions.Local.Tests.csproj index 90c3f0f..a5dda3d 100644 --- a/src/libraries/System.Transactions.Local/tests/System.Transactions.Local.Tests.csproj +++ b/src/libraries/System.Transactions.Local/tests/System.Transactions.Local.Tests.csproj @@ -7,6 +7,7 @@ + diff --git a/src/libraries/System.Transactions.Local/tests/TransactionManagerTest.cs b/src/libraries/System.Transactions.Local/tests/TransactionManagerTest.cs new file mode 100644 index 0000000..9566901 --- /dev/null +++ b/src/libraries/System.Transactions.Local/tests/TransactionManagerTest.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Transactions.Tests +{ + public class TransactionManagerTest + { + [Fact] + public void DefaultTimeout_MaxTimeout_Set_Get() + { + TimeSpan tsDefault = TimeSpan.Parse("00:02:00"); + TransactionManager.DefaultTimeout = tsDefault; + Assert.Equal(tsDefault, TransactionManager.DefaultTimeout); + + TimeSpan tsMax = TimeSpan.Parse("00:30:00"); + TransactionManager.MaximumTimeout = tsMax; + Assert.Equal(tsMax, TransactionManager.MaximumTimeout); + + TimeSpan ts = TransactionManager.MaximumTimeout.Add(TimeSpan.FromMinutes(10)); + TransactionManager.DefaultTimeout = ts; + Assert.Equal(tsMax, TransactionManager.MaximumTimeout); + Assert.Equal(TransactionManager.DefaultTimeout, TransactionManager.MaximumTimeout); + + ts = TimeSpan.Parse("-00:01:00"); + Assert.Throws(() => TransactionManager.DefaultTimeout = ts); + Assert.Throws(() => TransactionManager.MaximumTimeout = ts); + } + } +} -- 2.7.4