*/
List <ISinglePhaseNotification> durables;
+ IPromotableSinglePhaseNotification pspe = null;
+
delegate void AsyncCommit ();
AsyncCommit asyncCommit = null;
Exception innerException;
Guid tag = Guid.NewGuid ();
- List <IEnlistmentNotification> Volatiles {
+ internal List <IEnlistmentNotification> Volatiles {
get {
if (volatiles == null)
volatiles = new List <IEnlistmentNotification> ();
}
}
- List <ISinglePhaseNotification> Durables {
+ internal List <ISinglePhaseNotification> Durables {
get {
if (durables == null)
durables = new List <ISinglePhaseNotification> ();
return durables;
}
}
-
+
+ internal IPromotableSinglePhaseNotification Pspe { get { return pspe; } }
+
internal Transaction ()
{
info = new TransactionInformation ();
dependents = other.dependents;
volatiles = other.Volatiles;
durables = other.Durables;
+ pspe = other.Pspe;
}
[MonoTODO]
IEnlistmentNotification notification,
EnlistmentOptions options)
{
- throw new NotImplementedException ("Only SinglePhase commit supported for durable resource managers.");
+ throw new NotImplementedException ("DTC unsupported, only SinglePhase commit supported for durable resource managers.");
}
[MonoTODO ("Only Local Transaction Manager supported. Cannot have more than 1 durable resource per transaction. Only EnlistmentOptions.None supported yet.")]
ISinglePhaseNotification notification,
EnlistmentOptions options)
{
- var durables = Durables;
- if (durables.Count == 1)
- throw new NotImplementedException ("Only LTM supported. Cannot have more than 1 durable resource per transaction.");
-
EnsureIncompleteCurrentScope ();
+ if (pspe != null || Durables.Count > 0)
+ throw new NotImplementedException ("DTC unsupported, multiple durable resource managers aren't supported.");
if (options != EnlistmentOptions.None)
- throw new NotImplementedException ("Implement me");
+ throw new NotImplementedException ("EnlistmentOptions other than None aren't supported");
- durables.Add (notification);
+ Durables.Add (notification);
/* FIXME: Enlistment ?? */
return new Enlistment ();
}
- [MonoTODO]
public bool EnlistPromotableSinglePhase (
IPromotableSinglePhaseNotification notification)
{
- throw new NotImplementedException ();
+ EnsureIncompleteCurrentScope ();
+
+ // The specs aren't entirely clear on whether we can have volatile RMs along with a PSPE, but
+ // I'm assuming that yes based on: http://social.msdn.microsoft.com/Forums/br/windowstransactionsprogramming/thread/3df6d4d3-0d82-47c4-951a-cd31140950b3
+ if (pspe != null || Durables.Count > 0)
+ return false;
+
+ pspe = notification;
+ pspe.Initialize();
+
+ return true;
}
[MonoTODO ("EnlistmentOptions being ignored")]
Rollback (ex, null);
}
- internal void Rollback (Exception ex, IEnlistmentNotification enlisted)
+ internal void Rollback (Exception ex, object abortingEnlisted)
{
if (aborted)
{
throw new TransactionException ("Transaction has already been committed. Cannot accept any new work.");
innerException = ex;
- Enlistment e = new Enlistment ();
+ SinglePhaseEnlistment e = new SinglePhaseEnlistment();
foreach (IEnlistmentNotification prep in Volatiles)
- if (prep != enlisted)
+ if (prep != abortingEnlisted)
prep.Rollback (e);
var durables = Durables;
- if (durables.Count > 0 && durables [0] != enlisted)
+ if (durables.Count > 0 && durables [0] != abortingEnlisted)
durables [0].Rollback (e);
+ if (pspe != null && pspe != abortingEnlisted)
+ pspe.Rollback (e);
+
Aborted = true;
FireCompleted ();
this.committing = true;
try {
- DoCommit ();
+ DoCommit ();
}
catch (TransactionException)
{
if (durables.Count > 0)
DoSingleCommit(durables[0]);
+ if (pspe != null)
+ DoSingleCommit(pspe);
+
if (volatiles.Count > 0)
DoCommitPhase();
if (single == null)
return;
- SinglePhaseEnlistment enlistment = new SinglePhaseEnlistment (this, single);
- single.SinglePhaseCommit (enlistment);
+ single.SinglePhaseCommit (new SinglePhaseEnlistment (this, single));
+ CheckAborted ();
+ }
+
+ void DoSingleCommit (IPromotableSinglePhaseNotification single)
+ {
+ if (single == null)
+ return;
+
+ single.SinglePhaseCommit (new SinglePhaseEnlistment (this, single));
CheckAborted ();
}
if (CurrentInternal.Scope != null && CurrentInternal.Scope.IsComplete)
throw new InvalidOperationException ("The current TransactionScope is already complete");
}
- }
+ }
}
#endif
scope.Complete ();*/
}
- irm.Check ( 0, 0, 0, 1, 0, "irm" );
+ irm.Check ( 0, 0, 0, 1, 0, 0, 0, "irm" );
}
[Test]
public void Vol0_Dur1 ()
{
IntResourceManager irm = new IntResourceManager (1);
- irm.Volatile = false;
+ irm.Type = ResourceManagerType.Durable;
irm.UseSingle = true;
using (TransactionScope scope = new TransactionScope ()) {
/* Durable resource enlisted with a IEnlistedNotification
* object
*/
- irm.Volatile = false;
+ irm.Type = ResourceManagerType.Durable;
using (TransactionScope scope = new TransactionScope ()) {
irm.Value = 2;
/* Durable resource enlisted with a IEnlistedNotification
* object
*/
- irm.Volatile = false;
+ irm.Type = ResourceManagerType.Durable;
irm.FailSPC = true;
irm.UseSingle = true;
try {
}
}
catch (TransactionAbortedException) {
- irm.Check ( 1, 0, 0, 0, 0, "irm" );
+ irm.Check ( 1, 0, 0, 0, 0, 0, 0, "irm" );
return;
}
irm [2] = new IntResourceManager ( 5 );
irm [3] = new IntResourceManager ( 7 );
- irm [0].Volatile = false;
+ irm [0].Type = ResourceManagerType.Durable;
for ( int i = 0; i < 4; i++ )
irm [i].UseSingle = true;
irm [2] = new IntResourceManager (5);
irm [3] = new IntResourceManager (7);
- irm [0].Volatile = false;
+ irm [0].Type = ResourceManagerType.Durable;
irm [0].FailSPC = true;
for ( int i = 0; i < 4; i++ )
irm [0].CheckSPC ( "irm [0]" );
/* Volatile RMs get 2PC Prepare, and then get rolled back */
for (int i = 1; i < 4; i++)
- irm [i].Check ( 0, 1, 0, 1, 0, "irm [" + i + "]" );
+ irm [i].Check ( 0, 1, 0, 1, 0, 0, 0, "irm [" + i + "]" );
}
}
irm [2] = new IntResourceManager (5);
irm [3] = new IntResourceManager (7);
- irm [0].Volatile = false;
+ irm [0].Type = ResourceManagerType.Durable;
irm [0].IgnoreSPC = true;
for ( int i = 0; i < 4; i++ )
/* Volatile RMs get 2PC Prepare, and then get rolled back */
for (int i = 1; i < 4; i++)
- irm [i].Check ( 0, 1, 0, 1, 0, "irm [" + i + "]" );
+ irm [i].Check ( 0, 1, 0, 1, 0, 0, 0, "irm [" + i + "]" );
exception = ex;
}
irm[3] = new IntResourceManager(7);
irm[0].IgnoreSPC = true;
- irm[1].Volatile = false;
+ irm[1].Type = ResourceManagerType.Durable;
for (int i = 0; i < 4; i++)
irm[i].UseSingle = true;
/* Volatile RMs get 2PC Prepare, and then get rolled back */
for (int i = 1; i < 4; i++)
- irm[i].Check(0, 1, 0, 1, 0, "irm [" + i + "]");
+ irm[i].Check(0, 1, 0, 1, 0, 0, 0, "irm [" + i + "]");
exception = ex;
}
irm [2] = new IntResourceManager ( 5 );
irm [3] = new IntResourceManager ( 7 );
- irm [0].Volatile = false;
+ irm [0].Type = ResourceManagerType.Durable;
irm [2].FailPrepare = true;
for ( int i = 0; i < 4; i++ )
}
}
catch (TransactionAbortedException) {
- irm [0].Check ( 0, 0, 0, 1, 0, "irm [0]");
+ irm [0].Check ( 0, 0, 0, 1, 0, 0, 0, "irm [0]");
/* irm [1] & [2] get prepare,
* [2] -> ForceRollback,
* [1] & [3] get rollback,
* [0](durable) gets rollback */
- irm [1].Check ( 0, 1, 0, 1, 0, "irm [1]" );
- irm [2].Check ( 0, 1, 0, 0, 0, "irm [2]" );
- irm [3].Check ( 0, 0, 0, 1, 0, "irm [3]" );
+ irm [1].Check ( 0, 1, 0, 1, 0, 0, 0, "irm [1]" );
+ irm [2].Check ( 0, 1, 0, 0, 0, 0, 0, "irm [2]" );
+ irm [3].Check ( 0, 0, 0, 1, 0, 0, 0, "irm [3]" );
return;
}
irm [0] = new IntResourceManager ( 1 );
irm [1] = new IntResourceManager ( 3 );
- irm [0].Volatile = false;
+ irm [0].Type = ResourceManagerType.Durable;
irm [0].FailSPC = true;
irm [0].FailWithException = true;
Assert.IsNotNull ( e.InnerException, "Expected e.InnerException == NotSupportedException, but got None");
Assert.AreEqual ( typeof ( NotSupportedException ), e.InnerException.GetType (), "Expected e.InnerException == NotSupportedException, but got " + e.GetType () );
- irm [0].Check ( 1, 0, 0, 0, 0, "irm [0]" );
- irm [1].Check ( 0, 1, 0, 1, 0, "irm [1]" );
+ irm [0].Check ( 1, 0, 0, 0, 0, 0, 0, "irm [0]" );
+ irm [1].Check ( 0, 1, 0, 1, 0, 0, 0, "irm [1]" );
return;
}
irm [1] = new IntResourceManager ( 3 );
Transaction.Current = ct;
- irm [0].Volatile = false;
+ irm [0].Type = ResourceManagerType.Durable;
irm [0].FailSPC = true;
irm [0].FailWithException = true;
Assert.IsNotNull ( e.InnerException, "Expected e.InnerException == NotSupportedException, but got None" );
Assert.AreEqual ( typeof ( NotSupportedException ), e.InnerException.GetType (), "Expected e.InnerException == NotSupportedException, but got " + e.GetType () );
- irm [0].Check ( 1, 0, 0, 0, 0, "irm [0]" );
- irm [1].Check ( 0, 1, 0, 1, 0, "irm [1]" );
+ irm [0].Check ( 1, 0, 0, 0, 0, 0, 0, "irm [0]" );
+ irm [1].Check ( 0, 1, 0, 1, 0, 0, 0, "irm [1]" );
try {
ct.Commit ();
}
#endregion
+ #region Promotable Single Phase Enlistment
+ [Test]
+ public void Vol0_Dur0_Pspe1 ()
+ {
+ IntResourceManager irm = new IntResourceManager (1);
+ irm.Type = ResourceManagerType.Promotable;
+ using (TransactionScope scope = new TransactionScope ()) {
+ irm.Value = 2;
+
+ scope.Complete ();
+ }
+ irm.Check ( 1, 0, 0, 0, 0, 1, 0, "irm" );
+ }
+
+ [Test]
+ public void Vol1_Dur0_Pspe1 ()
+ {
+ IntResourceManager irm0 = new IntResourceManager (1);
+ IntResourceManager irm1 = new IntResourceManager (1);
+ irm1.Type = ResourceManagerType.Promotable;
+ using (TransactionScope scope = new TransactionScope ()) {
+ irm0.Value = 2;
+ irm1.Value = 8;
+
+ scope.Complete ();
+ }
+ irm1.Check ( 1, 0, 0, 0, 0, 1, 0, "irm1" );
+ }
+
+ [Test]
+ public void Vol0_Dur1_Pspe1 ()
+ {
+ IntResourceManager irm0 = new IntResourceManager (1);
+ IntResourceManager irm1 = new IntResourceManager (1);
+ irm0.Type = ResourceManagerType.Durable;
+ irm0.UseSingle = true;
+ irm1.Type = ResourceManagerType.Promotable;
+ using (TransactionScope scope = new TransactionScope ()) {
+ irm0.Value = 8;
+ irm1.Value = 2;
+ Assert.AreEqual(0, irm1.NumEnlistFailed, "PSPE enlist did not fail although durable RM was already enlisted");
+ }
+ }
+
+ [Test]
+ public void Vol0_Dur0_Pspe2 ()
+ {
+ IntResourceManager irm0 = new IntResourceManager (1);
+ IntResourceManager irm1 = new IntResourceManager (1);
+ irm0.Type = ResourceManagerType.Promotable;
+ irm1.Type = ResourceManagerType.Promotable;
+ using (TransactionScope scope = new TransactionScope ()) {
+ irm0.Value = 8;
+ irm1.Value = 2;
+ Assert.AreEqual(0, irm1.NumEnlistFailed, "PSPE enlist did not fail although PSPE RM was already enlisted");
+ }
+ }
+ #endregion
+
#region Others
/* >1vol
* > 1 durable, On .net this becomes a distributed transaction
irm [0] = new IntResourceManager ( 1 );
irm [1] = new IntResourceManager ( 3 );
- irm [0].Volatile = false;
- irm [1].Volatile = false;
+ irm [0].Type = ResourceManagerType.Durable;
+ irm [1].Type = ResourceManagerType.Durable;
for ( int i = 0; i < 2; i++ )
irm [i].UseSingle = true;
{
CommittableTransaction ct = new CommittableTransaction ();
IntResourceManager irm = new IntResourceManager (1);
- irm.Volatile = false;
+ irm.Type = ResourceManagerType.Durable;
ct.Dispose ();
irm.Check (0, 0, 0, 0, "Dispose transaction");
public int NumCommit = 0;
public int NumInDoubt = 0;
public int NumSingle = 0;
+
+ public int NumInitialize = 0;
+ public int NumPromote = 0;
+ public int NumEnlistFailed = 0;
+ public ResourceManagerType Type = ResourceManagerType.Volatile;
public bool FailPrepare = false;
public bool FailWithException = false;
public bool IgnorePrepare = false;
-
- public bool Volatile = true;
public bool IgnoreSPC = false;
public bool FailSPC = false;
public bool FailCommit = false;
if (transaction != Transaction.Current) {
transaction = Transaction.Current;
-
- if (UseSingle) {
+
+ if ( Type == ResourceManagerType.Promotable ) {
+ transaction.EnlistPromotableSinglePhase(new PromotableSinglePhaseNotification ( this ));
+ } else if (UseSingle) {
SinglePhaseNotification enlistment = new SinglePhaseNotification ( this );
- if ( Volatile )
+ if ( Type == ResourceManagerType.Volatile )
transaction.EnlistVolatile ( enlistment, EnlistmentOptions.None );
else
transaction.EnlistDurable ( guid, enlistment, EnlistmentOptions.None );
} else {
EnlistmentNotification enlistment = new EnlistmentNotification ( this );
- if ( Volatile )
+ if ( Type == ResourceManagerType.Volatile )
transaction.EnlistVolatile ( enlistment, EnlistmentOptions.None );
else
transaction.EnlistDurable ( guid, enlistment, EnlistmentOptions.None );
public void CheckSPC ( string msg )
{
- Check ( 1, 0, 0, 0, 0, msg );
+ Check ( 1, 0, 0, 0, 0, 0, 0, msg );
}
public void Check2PC ( string msg)
{
- Check ( 0, 1, 1, 0, 0, msg );
+ Check ( 0, 1, 1, 0, 0, 0, 0, msg );
}
- public void Check ( int s, int p, int c, int r, int d, string msg )
+ public void Check ( int s, int p, int c, int r, int d, int i, int pr, string msg )
{
Assert.AreEqual ( s, NumSingle, msg + ": NumSingle" );
Assert.AreEqual ( p, NumPrepare, msg + ": NumPrepare" );
Assert.AreEqual ( c, NumCommit, msg + ": NumCommit" );
Assert.AreEqual ( r, NumRollback, msg + ": NumRollback" );
Assert.AreEqual ( d, NumInDoubt, msg + ": NumInDoubt" );
+ Assert.AreEqual ( i, NumInitialize, msg + ": NumRollback" );
+ Assert.AreEqual ( pr, NumPromote, msg + ": NumInDoubt" );
}
/* Used for volatile RMs */
public void Check ( int p, int c, int r, int d, string msg )
{
- Check ( 0, p, c, r, d, msg );
+ Check ( 0, p, c, r, d, 0, 0, msg );
}
}
resource.NumInDoubt++;
throw new Exception ( "IntResourceManager.InDoubt is not implemented." );
}
-
}
public class SinglePhaseNotification : EnlistmentNotification, ISinglePhaseNotification
resource.Commit ();
enlistment.Committed ();
}
+ }
+ }
+
+ public class PromotableSinglePhaseNotification : SinglePhaseNotification, IPromotableSinglePhaseNotification
+ {
+ public PromotableSinglePhaseNotification ( IntResourceManager resource )
+ : base( resource )
+ {
+ }
+ public void Initialize ()
+ {
+ resource.NumInitialize++;
}
+ public void Rollback ( SinglePhaseEnlistment enlistment )
+ {
+ resource.NumRollback++;
+ resource.Rollback ();
+ }
+
+ public byte [] Promote ()
+ {
+ resource.NumPromote++;
+ return new byte[0];
+ }
}
+
+ public enum ResourceManagerType { Volatile, Durable, Promotable };
}