Merge pull request #3056 from BrzVlad/fix-multiple-binprot
[mono.git] / mcs / class / System.Transactions / System.Transactions / TransactionScope.cs
1 //
2 // TransactionScope.cs
3 //
4 // Author:
5 //      Atsushi Enomoto  <atsushi@ximian.com>
6 //      Ankit Jain       <JAnkit@novell.com>
7 //
8 // (C)2005 Novell Inc,
9 // (C)2006 Novell Inc,
10 //
11
12
13 using DTCOption = System.Transactions.EnterpriseServicesInteropOption;
14
15 namespace System.Transactions
16 {
17         public sealed class TransactionScope : IDisposable
18         {
19                 static TransactionOptions defaultOptions =
20                         new TransactionOptions (0, TransactionManager.DefaultTimeout);
21
22                 Transaction transaction;
23                 Transaction oldTransaction;
24                 TransactionScope parentScope;
25                 TimeSpan timeout;
26
27                 /* Num of non-disposed nested scopes */
28                 int nested;
29
30                 bool disposed;
31                 bool completed;
32                 bool isRoot;
33
34                 bool asyncFlowEnabled;
35
36                 public TransactionScope ()
37                         : this (TransactionScopeOption.Required,
38                                 TransactionManager.DefaultTimeout)
39                 {
40                 }
41
42                 public TransactionScope(TransactionScopeAsyncFlowOption asyncFlow)
43                         : this(TransactionScopeOption.Required,
44                                 TransactionManager.DefaultTimeout, asyncFlow)
45                 {
46                 }
47
48                 public TransactionScope (Transaction transaction)
49                         : this (transaction, TransactionManager.DefaultTimeout)
50                 {
51                 }
52
53                 public TransactionScope (Transaction transaction,
54                         TimeSpan timeout)
55                         : this (transaction, timeout, DTCOption.None)
56                 {
57                 }
58
59                 [MonoTODO ("EnterpriseServicesInteropOption not supported.")]
60                 public TransactionScope (Transaction transaction,
61                         TimeSpan timeout, DTCOption opt)
62                 {
63                         Initialize (TransactionScopeOption.Required,
64                                 transaction, defaultOptions, opt, timeout, TransactionScopeAsyncFlowOption.Suppress);
65                 }
66
67                 public TransactionScope (TransactionScopeOption option)
68                         : this (option, TransactionManager.DefaultTimeout)
69                 {
70                 }
71
72                 public TransactionScope (TransactionScopeOption option,
73                         TimeSpan timeout)
74                         : this (option, timeout, TransactionScopeAsyncFlowOption.Suppress)
75                 {
76                 }
77
78                 public TransactionScope(TransactionScopeOption option, TransactionScopeAsyncFlowOption asyncFlow)
79                         : this(option, TransactionManager.DefaultTimeout, asyncFlow)
80                 {
81                 }
82
83         public TransactionScope (TransactionScopeOption option,
84                         TimeSpan timeout, TransactionScopeAsyncFlowOption asyncFlow)
85                 {
86                         Initialize (option, null, defaultOptions,
87                                 DTCOption.None, timeout, asyncFlow);
88                 }
89
90                 public TransactionScope (TransactionScopeOption scopeOption,
91                         TransactionOptions options)
92                         : this (scopeOption, options, DTCOption.None)
93                 {
94                 }
95
96                 [MonoTODO ("EnterpriseServicesInteropOption not supported")]
97                 public TransactionScope (TransactionScopeOption scopeOption,
98                         TransactionOptions options,
99                         DTCOption opt)
100                 {
101                         Initialize (scopeOption, null, options, opt,
102                                 TransactionManager.DefaultTimeout, TransactionScopeAsyncFlowOption.Suppress);
103                 }
104
105                 void Initialize (TransactionScopeOption scopeOption,
106                         Transaction tx, TransactionOptions options,
107                         DTCOption interop, TimeSpan timeout, TransactionScopeAsyncFlowOption asyncFlow)
108                 {
109                         completed = false;
110                         isRoot = false;
111                         nested = 0;
112                         asyncFlowEnabled = asyncFlow == TransactionScopeAsyncFlowOption.Enabled;
113
114                         if (timeout < TimeSpan.Zero)
115                                 throw new ArgumentOutOfRangeException ("timeout");
116
117                         this.timeout = timeout;
118
119                         oldTransaction = Transaction.CurrentInternal;
120
121                         Transaction.CurrentInternal = transaction = InitTransaction (tx, scopeOption);
122                         if (transaction != null)
123                                 transaction.InitScope (this);
124                         if (parentScope != null)
125                                 parentScope.nested ++;
126                 }
127
128                 Transaction InitTransaction (Transaction tx, TransactionScopeOption scopeOption)
129                 {
130                         if (tx != null)
131                                 return tx;
132                                 
133                         if (scopeOption == TransactionScopeOption.Suppress) {
134                                 if (Transaction.CurrentInternal != null)
135                                         parentScope = Transaction.CurrentInternal.Scope;
136                                 return null;
137                         }
138
139                         if (scopeOption == TransactionScopeOption.Required) {
140                                 if (Transaction.CurrentInternal == null) {
141                                         isRoot = true;
142                                         return new Transaction ();
143                                 }
144
145                                 parentScope = Transaction.CurrentInternal.Scope;
146                                 return Transaction.CurrentInternal;
147                         }
148
149                         /* RequiresNew */
150                         if (Transaction.CurrentInternal != null)
151                                 parentScope = Transaction.CurrentInternal.Scope;
152                         isRoot = true;
153                         return new Transaction ();
154                 }
155
156                 public void Complete ()
157                 {
158                         if (completed)
159                                 throw new InvalidOperationException ("The current TransactionScope is already complete. You should dispose the TransactionScope.");
160
161                         completed = true;
162                 }
163
164                 internal bool IsComplete {
165                         get { return completed; }
166                 }
167
168                 internal TimeSpan Timeout
169                 {
170                         get { return timeout; }
171                 }
172
173                 public void Dispose ()
174                 {
175                         if (disposed)
176                                 return;
177
178                         disposed = true;
179
180                         if (parentScope != null)
181                                 parentScope.nested --;
182
183                         if (nested > 0) {
184                                 transaction.Rollback ();
185                                 throw new InvalidOperationException ("TransactionScope nested incorrectly");
186                         }
187
188                         if (Transaction.CurrentInternal != transaction && !asyncFlowEnabled) {
189                                 if (transaction != null)
190                                         transaction.Rollback ();
191                                 if (Transaction.CurrentInternal != null)
192                                         Transaction.CurrentInternal.Rollback ();
193
194                                 throw new InvalidOperationException ("Transaction.Current has changed inside of the TransactionScope");
195                         } 
196
197                         if (asyncFlowEnabled) {
198                                 if (oldTransaction != null)
199                                         oldTransaction.Scope = parentScope;
200
201                                 var variedTransaction = Transaction.CurrentInternal;
202
203                                 if (transaction == null && variedTransaction == null)
204                                         /* scope was not in a transaction, (Suppress) */
205                                         return;
206
207                                 variedTransaction.Scope = parentScope;
208                                 Transaction.CurrentInternal = oldTransaction;
209
210                                 transaction.Scope = null;
211
212                                 if (!IsComplete) {
213                                         transaction.Rollback ();
214                                         variedTransaction.Rollback();
215                                         return;
216                                 }
217
218                                 if (!isRoot)
219                                         /* Non-root scope has completed+ended */
220                                         return;
221
222                                 variedTransaction.CommitInternal();
223                                 transaction.CommitInternal();
224                         } else {
225                                 if (Transaction.CurrentInternal == oldTransaction && oldTransaction != null)
226                                         oldTransaction.Scope = parentScope;
227
228                                 Transaction.CurrentInternal = oldTransaction;
229
230                                 if (transaction == null)
231                                         /* scope was not in a transaction, (Suppress) */
232                                         return;
233
234                                 transaction.Scope = null;
235
236                                 if (!IsComplete)
237                                 {
238                                         transaction.Rollback();
239                                         return;
240                                 }
241
242                                 if (!isRoot)
243                                         /* Non-root scope has completed+ended */
244                                         return;
245
246                                 transaction.CommitInternal();
247
248                         }
249                 }
250
251
252         }
253 }
254