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 f52ae1ffd8b918..2f08b176bc27bf 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 @@ public static partial class TransactionInterop } 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 c4d3c4ad726a7f..242325130d6546 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 @@ internal static IsolationLevel DefaultIsolationLevel 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 @@ public static TimeSpan DefaultTimeout 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 @@ public static TimeSpan MaximumTimeout 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,39 @@ public static TimeSpan MaximumTimeout 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 = 0; + defaultTimeoutTicks = Interlocked.Read(ref s_defaultTimeoutTicks); + Interlocked.Exchange(ref s_defaultTimeoutTicks, ValidateTimeout(new TimeSpan(Interlocked.Read(ref s_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 90c3f0f9742e01..a5dda3d5689350 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 00000000000000..9566901d90db8b --- /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); + } + } +}