5 // Atsushi Enomoto <atsushi@ximian.com>
6 // Ankit Jain <JAnkit@novell.com>
13 using System.Collections;
14 using System.Collections.Generic;
15 using System.Security.Permissions;
16 using System.Runtime.Serialization;
17 using System.Threading;
19 namespace System.Transactions
22 public class Transaction : IDisposable, ISerializable
24 static Transaction ambient;
27 TransactionInformation info;
29 ArrayList dependents = new ArrayList ();
31 /* Volatile enlistments */
32 List <IEnlistmentNotification> volatiles = new List <IEnlistmentNotification> ();
34 /* Durable enlistments
35 Durable RMs can also have 2 Phase commit but
36 not in LTM, and that is what we are supporting
39 List <ISinglePhaseNotification> durables = new List <ISinglePhaseNotification> ();
41 delegate void AsyncCommit ();
43 AsyncCommit asyncCommit = null;
45 bool committed = false;
47 TransactionScope scope = null;
49 Exception innerException;
51 internal Transaction ()
53 info = new TransactionInformation ();
54 level = IsolationLevel.Serializable;
57 internal Transaction (Transaction other)
61 dependents = other.dependents;
65 void ISerializable.GetObjectData (SerializationInfo info,
66 StreamingContext context)
68 throw new NotImplementedException ();
71 public event TransactionCompletedEventHandler TransactionCompleted;
73 public static Transaction Current {
75 EnsureIncompleteCurrentScope ();
76 return CurrentInternal;
79 EnsureIncompleteCurrentScope ();
80 CurrentInternal = value;
84 internal static Transaction CurrentInternal {
85 get { return ambient; }
86 set { ambient = value; }
89 public IsolationLevel IsolationLevel {
91 EnsureIncompleteCurrentScope ();
96 public TransactionInformation TransactionInformation {
98 EnsureIncompleteCurrentScope ();
104 public Transaction Clone ()
106 return new Transaction (this);
110 public void Dispose ()
112 throw new NotImplementedException ();
116 public DependentTransaction DependentClone (
117 DependentCloneOption option)
119 DependentTransaction d =
120 new DependentTransaction (this, option);
126 [PermissionSetAttribute (SecurityAction.LinkDemand)]
127 public Enlistment EnlistDurable (Guid manager,
128 IEnlistmentNotification notification,
129 EnlistmentOptions options)
131 throw new NotImplementedException ("Only SinglePhase commit supported for durable resource managers.");
135 [PermissionSetAttribute (SecurityAction.LinkDemand)]
136 public Enlistment EnlistDurable (Guid manager,
137 ISinglePhaseNotification notification,
138 EnlistmentOptions options)
140 if (durables.Count == 1)
141 throw new NotImplementedException ("Only LTM supported. Cannot have more than 1 durable resource per transaction.");
143 EnsureIncompleteCurrentScope ();
145 if (options != EnlistmentOptions.None)
146 throw new NotImplementedException ("Implement me");
148 durables.Add (notification);
150 /* FIXME: Enlistment ?? */
151 return new Enlistment ();
155 public bool EnlistPromotableSinglePhase (
156 IPromotableSinglePhaseNotification notification)
158 throw new NotImplementedException ();
162 public Enlistment EnlistVolatile (
163 IEnlistmentNotification notification,
164 EnlistmentOptions options)
166 return EnlistVolatileInternal (notification, options);
170 public Enlistment EnlistVolatile (
171 ISinglePhaseNotification notification,
172 EnlistmentOptions options)
174 /* FIXME: Anything extra reqd for this? */
175 return EnlistVolatileInternal (notification, options);
178 private Enlistment EnlistVolatileInternal (
179 IEnlistmentNotification notification,
180 EnlistmentOptions options)
182 EnsureIncompleteCurrentScope ();
183 /* FIXME: Handle options.EnlistDuringPrepareRequired */
184 volatiles.Add (notification);
186 /* FIXME: Enlistment.. ? */
187 return new Enlistment ();
191 public override bool Equals (object obj)
193 Transaction t = obj as Transaction;
196 return this.level == t.level &&
201 public static bool op_Inequality (Transaction x, Transaction y)
203 if (x == null && y == null)
205 if (x == null || y == null)
208 return ! x.Equals (y);
211 public override int GetHashCode ()
213 return (int) level ^ info.GetHashCode () ^ dependents.GetHashCode ();
216 public void Rollback ()
221 public void Rollback (Exception ex)
223 EnsureIncompleteCurrentScope ();
227 internal void Rollback (Exception ex, IEnlistmentNotification enlisted)
232 /* See test ExplicitTransaction7 */
233 if (info.Status == TransactionStatus.Committed)
234 throw new TransactionException ("Transaction has already been committed. Cannot accept any new work.");
237 Enlistment e = new Enlistment ();
238 foreach (IEnlistmentNotification prep in volatiles)
239 if (prep != enlisted)
242 if (durables.Count > 0 && durables [0] != enlisted)
243 durables [0].Rollback (e);
249 get { return aborted; }
253 info.Status = TransactionStatus.Aborted;
257 internal TransactionScope Scope {
258 get { return scope; }
259 set { scope = value; }
262 protected IAsyncResult BeginCommitInternal (AsyncCallback callback)
264 if (committed || committing)
265 throw new InvalidOperationException ("Commit has already been called for this transaction.");
267 this.committing = true;
269 asyncCommit = new AsyncCommit (DoCommit);
270 return asyncCommit.BeginInvoke (callback, null);
273 protected void EndCommitInternal (IAsyncResult ar)
275 asyncCommit.EndInvoke (ar);
278 internal void CommitInternal ()
280 if (committed || committing)
281 throw new InvalidOperationException ("Commit has already been called for this transaction.");
283 this.committing = true;
288 private void DoCommit ()
290 /* Scope becomes null in TransactionScope.Dispose */
292 /* See test ExplicitTransaction8 */
293 Rollback (null, null);
297 if (volatiles.Count == 1 && durables.Count == 0) {
299 ISinglePhaseNotification single = volatiles [0] as ISinglePhaseNotification;
300 if (single != null) {
301 DoSingleCommit (single);
307 if (volatiles.Count > 0)
310 if (durables.Count > 0)
311 DoSingleCommit (durables [0]);
313 if (volatiles.Count > 0)
319 private void Complete ()
325 info.Status = TransactionStatus.Committed;
328 internal void InitScope (TransactionScope scope)
330 /* See test NestedTransactionScope10 */
333 /* See test ExplicitTransaction6a */
335 throw new InvalidOperationException ("Commit has already been called on this transaction.");
340 void DoPreparePhase ()
342 PreparingEnlistment pe;
343 foreach (IEnlistmentNotification enlisted in volatiles) {
344 pe = new PreparingEnlistment (this, enlisted);
346 enlisted.Prepare (pe);
348 /* FIXME: Where should this timeout value come from?
350 Wait after all Prepare()'s are sent
351 pe.WaitHandle.WaitOne (new TimeSpan (0,0,5), true); */
353 if (!pe.IsPrepared) {
354 /* FIXME: if not prepared & !aborted as yet, then
355 this is inDoubt ? . For now, setting aborted = true */
361 /* Either InDoubt(tmp) or Prepare failed and
366 void DoCommitPhase ()
368 foreach (IEnlistmentNotification enlisted in volatiles) {
369 Enlistment e = new Enlistment ();
371 /* Note: e.Done doesn't matter for volatile RMs */
375 void DoSingleCommit (ISinglePhaseNotification single)
380 SinglePhaseEnlistment enlistment = new SinglePhaseEnlistment (this, single);
381 single.SinglePhaseCommit (enlistment);
388 throw new TransactionAbortedException ("Transaction has aborted", innerException);
391 static void EnsureIncompleteCurrentScope ()
393 if (CurrentInternal == null)
395 if (CurrentInternal.Scope != null && CurrentInternal.Scope.IsComplete)
396 throw new InvalidOperationException ("The current TransactionScope is already complete");