[tests] Separate MONO_PATH directories by PLATFORM_PATH_SEPARATOR
[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                 public TransactionScope (Transaction transactionToUse,
106                         TransactionScopeAsyncFlowOption asyncFlowOption)
107                 {
108                         throw new NotImplementedException ();
109                 }
110
111                 public TransactionScope (Transaction transactionToUse,
112                         TimeSpan scopeTimeout,
113                         TransactionScopeAsyncFlowOption asyncFlowOption)
114                 {
115                         throw new NotImplementedException ();
116                 }
117
118                 public TransactionScope (TransactionScopeOption scopeOption,
119                         TransactionOptions transactionOptions,
120                         TransactionScopeAsyncFlowOption asyncFlowOption)
121                 {
122                         throw new NotImplementedException ();
123                 }
124
125                 void Initialize (TransactionScopeOption scopeOption,
126                         Transaction tx, TransactionOptions options,
127                         DTCOption interop, TimeSpan timeout, TransactionScopeAsyncFlowOption asyncFlow)
128                 {
129                         completed = false;
130                         isRoot = false;
131                         nested = 0;
132                         asyncFlowEnabled = asyncFlow == TransactionScopeAsyncFlowOption.Enabled;
133
134                         if (timeout < TimeSpan.Zero)
135                                 throw new ArgumentOutOfRangeException ("timeout");
136
137                         this.timeout = timeout;
138
139                         oldTransaction = Transaction.CurrentInternal;
140
141                         Transaction.CurrentInternal = transaction = InitTransaction (tx, scopeOption);
142                         if (transaction != null)
143                                 transaction.InitScope (this);
144                         if (parentScope != null)
145                                 parentScope.nested ++;
146                 }
147
148                 Transaction InitTransaction (Transaction tx, TransactionScopeOption scopeOption)
149                 {
150                         if (tx != null)
151                                 return tx;
152                                 
153                         if (scopeOption == TransactionScopeOption.Suppress) {
154                                 if (Transaction.CurrentInternal != null)
155                                         parentScope = Transaction.CurrentInternal.Scope;
156                                 return null;
157                         }
158
159                         if (scopeOption == TransactionScopeOption.Required) {
160                                 if (Transaction.CurrentInternal == null) {
161                                         isRoot = true;
162                                         return new Transaction ();
163                                 }
164
165                                 parentScope = Transaction.CurrentInternal.Scope;
166                                 return Transaction.CurrentInternal;
167                         }
168
169                         /* RequiresNew */
170                         if (Transaction.CurrentInternal != null)
171                                 parentScope = Transaction.CurrentInternal.Scope;
172                         isRoot = true;
173                         return new Transaction ();
174                 }
175
176                 public void Complete ()
177                 {
178                         if (completed)
179                                 throw new InvalidOperationException ("The current TransactionScope is already complete. You should dispose the TransactionScope.");
180
181                         completed = true;
182                 }
183
184                 internal bool IsComplete {
185                         get { return completed; }
186                 }
187
188                 internal TimeSpan Timeout
189                 {
190                         get { return timeout; }
191                 }
192
193                 public void Dispose ()
194                 {
195                         if (disposed)
196                                 return;
197
198                         disposed = true;
199
200                         if (parentScope != null)
201                                 parentScope.nested --;
202
203                         if (nested > 0) {
204                                 transaction.Rollback ();
205                                 throw new InvalidOperationException ("TransactionScope nested incorrectly");
206                         }
207
208                         if (Transaction.CurrentInternal != transaction && !asyncFlowEnabled) {
209                                 if (transaction != null)
210                                         transaction.Rollback ();
211                                 if (Transaction.CurrentInternal != null)
212                                         Transaction.CurrentInternal.Rollback ();
213
214                                 throw new InvalidOperationException ("Transaction.Current has changed inside of the TransactionScope");
215                         } 
216
217                         if (asyncFlowEnabled) {
218                                 if (oldTransaction != null)
219                                         oldTransaction.Scope = parentScope;
220
221                                 var variedTransaction = Transaction.CurrentInternal;
222
223                                 if (transaction == null && variedTransaction == null)
224                                         /* scope was not in a transaction, (Suppress) */
225                                         return;
226
227                                 variedTransaction.Scope = parentScope;
228                                 Transaction.CurrentInternal = oldTransaction;
229
230                                 transaction.Scope = null;
231
232                                 if (!IsComplete) {
233                                         transaction.Rollback ();
234                                         variedTransaction.Rollback();
235                                         return;
236                                 }
237
238                                 if (!isRoot)
239                                         /* Non-root scope has completed+ended */
240                                         return;
241
242                                 variedTransaction.CommitInternal();
243                                 transaction.CommitInternal();
244                         } else {
245                                 if (Transaction.CurrentInternal == oldTransaction && oldTransaction != null)
246                                         oldTransaction.Scope = parentScope;
247
248                                 Transaction.CurrentInternal = oldTransaction;
249
250                                 if (transaction == null)
251                                         /* scope was not in a transaction, (Suppress) */
252                                         return;
253
254                                 transaction.Scope = null;
255
256                                 if (!IsComplete)
257                                 {
258                                         transaction.Rollback();
259                                         return;
260                                 }
261
262                                 if (!isRoot)
263                                         /* Non-root scope has completed+ended */
264                                         return;
265
266                                 transaction.CommitInternal();
267
268                         }
269                 }
270
271
272         }
273 }
274