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 ();
103 public Transaction Clone ()
105 return new Transaction (this);
109 public void Dispose ()
111 throw new NotImplementedException ();
115 public DependentTransaction DependentClone (
116 DependentCloneOption option)
118 DependentTransaction d =
119 new DependentTransaction (this, option);
124 [MonoTODO ("Only SinglePhase commit supported for durable resource managers.")]
125 [PermissionSetAttribute (SecurityAction.LinkDemand)]
126 public Enlistment EnlistDurable (Guid manager,
127 IEnlistmentNotification notification,
128 EnlistmentOptions options)
130 throw new NotImplementedException ("Only SinglePhase commit supported for durable resource managers.");
133 [MonoTODO ("Only Local Transaction Manager supported. Cannot have more than 1 durable resource per transaction. Only EnlistmentOptions.None supported yet.")]
134 [PermissionSetAttribute (SecurityAction.LinkDemand)]
135 public Enlistment EnlistDurable (Guid manager,
136 ISinglePhaseNotification notification,
137 EnlistmentOptions options)
139 if (durables.Count == 1)
140 throw new NotImplementedException ("Only LTM supported. Cannot have more than 1 durable resource per transaction.");
142 EnsureIncompleteCurrentScope ();
144 if (options != EnlistmentOptions.None)
145 throw new NotImplementedException ("Implement me");
147 durables.Add (notification);
149 /* FIXME: Enlistment ?? */
150 return new Enlistment ();
154 public bool EnlistPromotableSinglePhase (
155 IPromotableSinglePhaseNotification notification)
157 throw new NotImplementedException ();
160 [MonoTODO ("EnlistmentOptions being ignored")]
161 public Enlistment EnlistVolatile (
162 IEnlistmentNotification notification,
163 EnlistmentOptions options)
165 return EnlistVolatileInternal (notification, options);
168 [MonoTODO ("EnlistmentOptions being ignored")]
169 public Enlistment EnlistVolatile (
170 ISinglePhaseNotification notification,
171 EnlistmentOptions options)
173 /* FIXME: Anything extra reqd for this? */
174 return EnlistVolatileInternal (notification, options);
177 private Enlistment EnlistVolatileInternal (
178 IEnlistmentNotification notification,
179 EnlistmentOptions options)
181 EnsureIncompleteCurrentScope ();
182 /* FIXME: Handle options.EnlistDuringPrepareRequired */
183 volatiles.Add (notification);
185 /* FIXME: Enlistment.. ? */
186 return new Enlistment ();
189 public override bool Equals (object obj)
191 return Equals (obj as Transaction);
194 // FIXME: Check whether this is correct (currently, GetHashCode() uses 'dependents' but this doesn't)
195 private bool Equals (Transaction t)
197 if (ReferenceEquals (t, this))
199 if (ReferenceEquals (t, null))
201 return this.level == t.level &&
205 public static bool operator == (Transaction x, Transaction y)
207 if (ReferenceEquals (x, null))
208 return ReferenceEquals (y, null);
212 public static bool operator != (Transaction x, Transaction y)
217 public override int GetHashCode ()
219 return (int) level ^ info.GetHashCode () ^ dependents.GetHashCode ();
222 public void Rollback ()
227 public void Rollback (Exception ex)
229 EnsureIncompleteCurrentScope ();
233 internal void Rollback (Exception ex, IEnlistmentNotification enlisted)
238 /* See test ExplicitTransaction7 */
239 if (info.Status == TransactionStatus.Committed)
240 throw new TransactionException ("Transaction has already been committed. Cannot accept any new work.");
243 Enlistment e = new Enlistment ();
244 foreach (IEnlistmentNotification prep in volatiles)
245 if (prep != enlisted)
248 if (durables.Count > 0 && durables [0] != enlisted)
249 durables [0].Rollback (e);
255 get { return aborted; }
259 info.Status = TransactionStatus.Aborted;
263 internal TransactionScope Scope {
264 get { return scope; }
265 set { scope = value; }
268 protected IAsyncResult BeginCommitInternal (AsyncCallback callback)
270 if (committed || committing)
271 throw new InvalidOperationException ("Commit has already been called for this transaction.");
273 this.committing = true;
275 asyncCommit = new AsyncCommit (DoCommit);
276 return asyncCommit.BeginInvoke (callback, null);
279 protected void EndCommitInternal (IAsyncResult ar)
281 asyncCommit.EndInvoke (ar);
284 internal void CommitInternal ()
286 if (committed || committing)
287 throw new InvalidOperationException ("Commit has already been called for this transaction.");
289 this.committing = true;
294 private void DoCommit ()
296 /* Scope becomes null in TransactionScope.Dispose */
298 /* See test ExplicitTransaction8 */
299 Rollback (null, null);
303 if (volatiles.Count == 1 && durables.Count == 0) {
305 ISinglePhaseNotification single = volatiles [0] as ISinglePhaseNotification;
306 if (single != null) {
307 DoSingleCommit (single);
313 if (volatiles.Count > 0)
316 if (durables.Count > 0)
317 DoSingleCommit (durables [0]);
319 if (volatiles.Count > 0)
325 private void Complete ()
331 info.Status = TransactionStatus.Committed;
334 internal void InitScope (TransactionScope scope)
336 /* See test NestedTransactionScope10 */
339 /* See test ExplicitTransaction6a */
341 throw new InvalidOperationException ("Commit has already been called on this transaction.");
346 void DoPreparePhase ()
348 PreparingEnlistment pe;
349 foreach (IEnlistmentNotification enlisted in volatiles) {
350 pe = new PreparingEnlistment (this, enlisted);
352 enlisted.Prepare (pe);
354 /* FIXME: Where should this timeout value come from?
356 Wait after all Prepare()'s are sent
357 pe.WaitHandle.WaitOne (new TimeSpan (0,0,5), true); */
359 if (!pe.IsPrepared) {
360 /* FIXME: if not prepared & !aborted as yet, then
361 this is inDoubt ? . For now, setting aborted = true */
367 /* Either InDoubt(tmp) or Prepare failed and
372 void DoCommitPhase ()
374 foreach (IEnlistmentNotification enlisted in volatiles) {
375 Enlistment e = new Enlistment ();
377 /* Note: e.Done doesn't matter for volatile RMs */
381 void DoSingleCommit (ISinglePhaseNotification single)
386 SinglePhaseEnlistment enlistment = new SinglePhaseEnlistment (this, single);
387 single.SinglePhaseCommit (enlistment);
394 throw new TransactionAbortedException ("Transaction has aborted", innerException);
397 static void EnsureIncompleteCurrentScope ()
399 if (CurrentInternal == null)
401 if (CurrentInternal.Scope != null && CurrentInternal.Scope.IsComplete)
402 throw new InvalidOperationException ("The current TransactionScope is already complete");