Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/mcs
diff options
context:
space:
mode:
authorPablo Martínez <crackbashridge@gmail.com>2020-07-08 22:05:42 +0300
committerGitHub <noreply@github.com>2020-07-08 22:05:42 +0300
commit9c0121161c1c421753ed99298a1fb4557ea233fe (patch)
treec6d2bbd0c812c7d82a8e9e8f40469fc1bd388a66 /mcs
parent5bd9f53dc0045b38668ff20bb4916c02dbefcb73 (diff)
Init a timer on TransactionScope initialization to be able to timeout (#20080)
Fixes #19955
Diffstat (limited to 'mcs')
-rw-r--r--mcs/class/System.Transactions/System.Transactions/Transaction.cs37
-rw-r--r--mcs/class/System.Transactions/System.Transactions/TransactionScope.cs58
-rw-r--r--mcs/class/System.Transactions/Test/TransactionScopeTest.cs13
3 files changed, 96 insertions, 12 deletions
diff --git a/mcs/class/System.Transactions/System.Transactions/Transaction.cs b/mcs/class/System.Transactions/System.Transactions/Transaction.cs
index e1de05591ce..462a0f9bf74 100644
--- a/mcs/class/System.Transactions/System.Transactions/Transaction.cs
+++ b/mcs/class/System.Transactions/System.Transactions/Transaction.cs
@@ -23,6 +23,8 @@ namespace System.Transactions
[ThreadStatic]
static Transaction ambient;
+ Transaction internalTransaction;
+
IsolationLevel level;
TransactionInformation info;
@@ -83,6 +85,8 @@ namespace System.Transactions
volatiles = other.Volatiles;
durables = other.Durables;
pspe = other.Pspe;
+ TransactionCompletedInternal = other.TransactionCompletedInternal;
+ internalTransaction = other;
}
[MonoTODO]
@@ -92,7 +96,29 @@ namespace System.Transactions
throw new NotImplementedException ();
}
- public event TransactionCompletedEventHandler TransactionCompleted;
+ internal event TransactionCompletedEventHandler TransactionCompletedInternal;
+
+ // Transaction B was cloned from transaction A. Add event handlers to A
+ // when they are added to B. This will not work when new handlers are added to A
+ // as A has no reference to B.
+ public event TransactionCompletedEventHandler TransactionCompleted
+ {
+ add
+ {
+ if (this.internalTransaction != null)
+ this.internalTransaction.TransactionCompleted += value;
+
+ TransactionCompletedInternal += value;
+ }
+
+ remove
+ {
+ if (this.internalTransaction != null)
+ this.internalTransaction.TransactionCompleted -= value;
+
+ TransactionCompletedInternal -= value;
+ }
+ }
public static Transaction Current {
get {
@@ -378,7 +404,7 @@ namespace System.Transactions
private void DoCommit ()
{
/* Scope becomes null in TransactionScope.Dispose */
- if (Scope != null) {
+ if (Scope != null && (!Scope.IsComplete || !Scope.IsDisposed)) {
/* See test ExplicitTransaction8 */
Rollback (null, null);
CheckAborted ();
@@ -529,14 +555,14 @@ namespace System.Transactions
void CheckAborted ()
{
- if (aborted)
+ if (aborted || (Scope != null && Scope.IsAborted))
throw new TransactionAbortedException ("Transaction has aborted", innerException);
}
void FireCompleted ()
{
- if (TransactionCompleted != null)
- TransactionCompleted (this, new TransactionEventArgs(this));
+ if (TransactionCompletedInternal != null)
+ TransactionCompletedInternal (this, new TransactionEventArgs(this));
}
static void EnsureIncompleteCurrentScope ()
@@ -548,4 +574,3 @@ namespace System.Transactions
}
}
}
-
diff --git a/mcs/class/System.Transactions/System.Transactions/TransactionScope.cs b/mcs/class/System.Transactions/System.Transactions/TransactionScope.cs
index 503a4b6b9d9..9cb38a710be 100644
--- a/mcs/class/System.Transactions/System.Transactions/TransactionScope.cs
+++ b/mcs/class/System.Transactions/System.Transactions/TransactionScope.cs
@@ -9,6 +9,7 @@
// (C)2006 Novell Inc,
//
+using System.Threading;
using DTCOption = System.Transactions.EnterpriseServicesInteropOption;
@@ -19,6 +20,7 @@ namespace System.Transactions
static TransactionOptions defaultOptions =
new TransactionOptions (0, TransactionManager.DefaultTimeout);
+ Timer scopeTimer;
Transaction transaction;
Transaction oldTransaction;
TransactionScope parentScope;
@@ -29,6 +31,7 @@ namespace System.Transactions
bool disposed;
bool completed;
+ bool aborted;
bool isRoot;
bool asyncFlowEnabled;
@@ -99,7 +102,7 @@ namespace System.Transactions
DTCOption interopOption)
{
Initialize (scopeOption, null, transactionOptions, interopOption,
- TransactionManager.DefaultTimeout, TransactionScopeAsyncFlowOption.Suppress);
+ transactionOptions.Timeout, TransactionScopeAsyncFlowOption.Suppress);
}
public TransactionScope (Transaction transactionToUse,
@@ -143,8 +146,35 @@ namespace System.Transactions
transaction.InitScope (this);
if (parentScope != null)
parentScope.nested ++;
+ if (timeout != TimeSpan.Zero)
+ scopeTimer = new Timer (TransactionScope.TimerCallback, this, scopeTimeout, TimeSpan.Zero);
}
+ private static void TimerCallback (object state)
+ {
+ TransactionScope scope = state as TransactionScope;
+ if (null == scope)
+ {
+ throw new TransactionException ("TransactionScopeTimerObjectInvalid", null);
+ }
+
+ scope.TimeoutScope ();
+ }
+
+ private void TimeoutScope()
+ {
+ if (!completed && transaction != null)
+ {
+ try
+ {
+ this.transaction.Rollback ();
+ this.aborted = true;
+ }
+ catch (ObjectDisposedException ex) { }
+ catch (TransactionException txEx) { }
+ }
+ }
+
Transaction InitTransaction (Transaction tx, TransactionScopeOption scopeOption, TransactionOptions options)
{
if (tx != null)
@@ -181,6 +211,14 @@ namespace System.Transactions
completed = true;
}
+ internal bool IsAborted {
+ get { return aborted; }
+ }
+
+ internal bool IsDisposed {
+ get { return disposed; }
+ }
+
internal bool IsComplete {
get { return completed; }
}
@@ -214,6 +252,9 @@ namespace System.Transactions
throw new InvalidOperationException ("Transaction.Current has changed inside of the TransactionScope");
}
+ if (scopeTimer != null)
+ scopeTimer.Dispose();
+
if (asyncFlowEnabled) {
if (oldTransaction != null)
oldTransaction.Scope = parentScope;
@@ -229,7 +270,10 @@ namespace System.Transactions
transaction.Scope = null;
- if (!IsComplete) {
+ if (IsAborted) {
+ throw new TransactionAbortedException("Transaction has aborted");
+ }
+ else if (!IsComplete) {
transaction.Rollback ();
variedTransaction.Rollback();
return;
@@ -251,9 +295,11 @@ namespace System.Transactions
/* scope was not in a transaction, (Suppress) */
return;
- transaction.Scope = null;
-
- if (!IsComplete)
+ if (IsAborted) {
+ transaction.Scope = null;
+ throw new TransactionAbortedException("Transaction has aborted");
+ }
+ else if (!IsComplete)
{
transaction.Rollback();
return;
@@ -265,10 +311,10 @@ namespace System.Transactions
transaction.CommitInternal();
+ transaction.Scope = null;
}
}
}
}
-
diff --git a/mcs/class/System.Transactions/Test/TransactionScopeTest.cs b/mcs/class/System.Transactions/Test/TransactionScopeTest.cs
index a741fcf4775..34e912c94fe 100644
--- a/mcs/class/System.Transactions/Test/TransactionScopeTest.cs
+++ b/mcs/class/System.Transactions/Test/TransactionScopeTest.cs
@@ -11,6 +11,7 @@
using System;
using NUnit.Framework;
using System.Transactions;
+using System.Threading;
namespace MonoTests.System.Transactions
{
@@ -1114,6 +1115,18 @@ namespace MonoTests.System.Transactions
Assert.AreEqual(IsolationLevel.ReadCommitted, Transaction.Current.IsolationLevel);
}
}
+
+ [Test]
+ [ExpectedException(typeof(TransactionAbortedException))]
+ public void TransactionScopeTimeout()
+ {
+ Assert.IsNull(Transaction.Current, "Ambient transaction exists (before)");
+ using (var ts = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromMilliseconds(1)))
+ {
+ Thread.Sleep(100);
+ ts.Complete();
+ }
+ }
}
}