Today, in this post, I am going to show you a concept that I have written for a few of my projects: Simple thread transactions.
I know what you are already thinking - yet another up in the sky thread and what the hell is a simple thread transaction?
I'd say that most of us are familiar with the concept of a transaction: A batch of operations that are atomic. They either all complete, or they all fail. Now, Windows already has transactional APIs, but they are not for the faint of heart and so I am going to release some of my code out into the wild that will provide a limited transactional functionality that is lightweight.
I will include the code (a simple C# class) and some usage scenarios. You can have one transaction operating per thread in your program.
The Code Warning: Spoiler! (Click to show)
Code Usage
This simple transaction class can be used to permit code re-use. Consider an API in your program that deletes objects from a database. You might have two methods: DeleteSingleObject and DeleteMultipleObjects. How can we have the multiple-objects version re-use the error handling and other code from the single-object version? Moreover, how can we make sure that we execute a piece of code only after all objects have been successfully deleted? Transactions to the rescue!
Consider the below method that deletes a single object from a database:
How can we convert this into the multiple-objects version of this API? (i.e. the DeleteMultipleObjects method). We could call DeleteSingleObject in a loop but then we wouldn't easily be able to know what happened if an operation failed. We could try...catch as we use exceptions, however this is bad in a loop (especially tight loops). We know the method returns false on failure, however that isn't enough information. The answer? Wrap the whole thing up in our new transactions!
Consider the below method that deletes multiple objects from a database:
Now we just need to tweak our DeleteSingleObject method to deal with the possibility of transactions (and a helper function):
I know someone will find this useful
If you need a lightweight transaction mechanism, this is for you. All code is open source!
Edited by tompsonn - 10/13/12 at 4:37am
I know what you are already thinking - yet another up in the sky thread and what the hell is a simple thread transaction?
I'd say that most of us are familiar with the concept of a transaction: A batch of operations that are atomic. They either all complete, or they all fail. Now, Windows already has transactional APIs, but they are not for the faint of heart and so I am going to release some of my code out into the wild that will provide a limited transactional functionality that is lightweight.
I will include the code (a simple C# class) and some usage scenarios. You can have one transaction operating per thread in your program.
The Code Warning: Spoiler! (Click to show)
Code:
/// <summary>
/// Transaction object used for batching up operations with a simple commit. There can only be one transaction
/// at a time, per thread. Support for atomic operations via Abort() and AbortWithException().
/// </summary>
internal sealed class CoreFastTransaction : IDisposable
{
[ThreadStatic]
private static CoreFastTransaction _currentThreadTransaction;
private bool _isCompleted = false;
private bool _isAborted = false;
private Exception _lastException;
private readonly ThreadLocal<int> _createdThreadId = new ThreadLocal<int>();
/// <summary>
/// Fired when the transaction is successfully completed.
/// </summary>
internal event EventHandler OnCompleted;
/// <summary>
/// Gets the current transaction for the current thread.
/// </summary>
internal static CoreFastTransaction Current
{
get
{
return _currentThreadTransaction;
}
}
/// <summary>
/// Gets whether the current thread has an in-progress transaction.
/// </summary>
internal static bool ThreadHasTransaction
{
get
{
return ( Current != null );
}
}
/// <summary>
/// Gets the last exception that occurred for this transaction.
/// </summary>
internal Exception LastException
{
get
{
return this._lastException;
}
}
/// <summary>
/// Gets whether this transaction is complete.
/// </summary>
internal bool IsCompleted
{
get
{
return this._isCompleted;
}
}
/// <summary>
/// Gets whether this transaction is aborted (IsCompleted is also true).
/// </summary>
internal bool IsAborted
{
get
{
return this._isAborted;
}
}
/// <summary>
///
/// </summary>
private CoreFastTransaction()
{ }
/// <summary>
/// Factory
/// </summary>
/// <returns></returns>
internal static CoreFastTransaction Create()
{
if ( _currentThreadTransaction != null && !_currentThreadTransaction.IsCompleted )
{
throw new InvalidOperationException( "Complete the transaction on the current thread before starting a new one." );
}
_currentThreadTransaction = new CoreFastTransaction();
_currentThreadTransaction._createdThreadId.Value = Thread.CurrentThread.ManagedThreadId;
return _currentThreadTransaction;
}
/// <summary>
/// Aborts the transaction, supplying an exception.
/// </summary>
/// <param name="exception"></param>
internal void AbortWithException( Exception exception )
{
this._VerifyThreadConcurrency();
if ( exception != null )
{
this._lastException = exception;
this.Abort();
}
}
/// <summary>
/// Aborts the transaction.
/// </summary>
internal void Abort()
{
this._VerifyThreadConcurrency();
this._isAborted = true;
this._isCompleted = true;
}
/// <summary>
/// Completes the transaction (success).
/// </summary>
internal void Complete()
{
this._VerifyThreadConcurrency();
if ( !this._isAborted && !this._isCompleted )
{
if ( this.OnCompleted != null )
{
this.OnCompleted( this, EventArgs.Empty );
}
this._isCompleted = true;
}
}
/// <summary>
///
/// </summary>
private void _VerifyThreadConcurrency()
{
if ( !this._createdThreadId.IsValueCreated ||
this._createdThreadId.Value != Thread.CurrentThread.ManagedThreadId )
{
throw new InvalidOperationException( "Cannot perform transaction operation on a different thread than it was created on." );
}
}
/// <summary>
///
/// </summary>
~CoreFastTransaction()
{
this.Dispose( true );
}
#region IDisposable Members
/// <summary>
///
/// </summary>
void IDisposable.Dispose()
{
this.Complete();
this.Dispose( true );
GC.SuppressFinalize( this );
}
/// <summary>
///
/// </summary>
/// <param name="fDisposing"></param>
private void Dispose( bool fDisposing )
{
if ( fDisposing )
{
if ( this._createdThreadId != null )
{
this._createdThreadId.Dispose();
}
}
}
#endregion
}
Code Usage
This simple transaction class can be used to permit code re-use. Consider an API in your program that deletes objects from a database. You might have two methods: DeleteSingleObject and DeleteMultipleObjects. How can we have the multiple-objects version re-use the error handling and other code from the single-object version? Moreover, how can we make sure that we execute a piece of code only after all objects have been successfully deleted? Transactions to the rescue!
Consider the below method that deletes a single object from a database:
Code:
internal bool DeleteSingleObject( ObjectHandleType object )
{
if ( object == null )
{
throw new ArgumentNullException( "No object to delete was specified.", "object" );
}
if ( !ObjectExists( object ) ) /* ObjectExists() is implemented somewhere else, and checks if the object exists */
{
throw new Exception( "The object to delete does not exist." ):
}
if ( !ObjectDelete( object ) ) /* ObjectDelete() is implemented somewhere else, and actually does the work of deleting the object */
{
throw new Exception( "Failed to delete the object." ); /* Of course ObjectDelete() should return WHY it failed, but this is outside the scope of this thread */
}
/* Successfully deleted the object */
return true;
}
How can we convert this into the multiple-objects version of this API? (i.e. the DeleteMultipleObjects method). We could call DeleteSingleObject in a loop but then we wouldn't easily be able to know what happened if an operation failed. We could try...catch as we use exceptions, however this is bad in a loop (especially tight loops). We know the method returns false on failure, however that isn't enough information. The answer? Wrap the whole thing up in our new transactions!
Consider the below method that deletes multiple objects from a database:
Code:
internal bool DeleteMultipleObjects( params ObjectHandleType[] objects )
{
/* The "using" block ensures the transaction is completed (aborted or not) */
using ( CoreFastTransaction transaction = CoreFastTransaction.Create() )
{
/*
* Before we start our loop, we can use the OnCompleted event of your transaction to have some action execute when
* when the transaction completes successfully. A successful completion is defined by all operations inside the transaction
* completing without calling Abort() or AbortWithException() on the current transaction.
*/
transaction.OnCompleted += ( sender, e ) => MessageBox.Show( "COMPLETED HOORAY!" );
for ( int i = 0; i < object.Length; ++i )
{
/* Check for abortion (lolol) */
if ( transaction.IsAborted )
{
/* If an exception was given during the abort */
if ( transaction.LastException != null )
{
MessageBox.Show( "The following error occurred: " +transaction.LastException.Message );
}
return false;
}
DeleteSingleObject( objects[i] );
}
} /* Transaction completes here */
return true;
}
Now we just need to tweak our DeleteSingleObject method to deal with the possibility of transactions (and a helper function):
Code:
internal static void ThrowOrAbortTransaction( Exception result )
{
/* If in a transaction (and possibly a loop), don't throw an exception! */
if ( CoreFastTransaction.ThreadHasTransaction )
{
CoreFastTransaction.Current.AbortWithException( errorResult );
return false;
}
/* Not in transaction - throw exception */
throw errorResult;
}
internal bool DeleteSingleObject( ObjectHandleType object )
{
/* Check for abortion (lolol) */
if ( CoreFastTransaction.ThreadHasTransaction && CoreFastTransaction.Current.IsAborted )
{
return false;
}
Exception errorResult = null;
if ( object == null )
{
errorResult = new ArgumentNullException( "No object to delete was specified.", "object" );
ThrowOrAbortTransaction( errorResult );
return false;
}
if ( !ObjectExists( object ) ) /* ObjectExists() is implemented somewhere else, and checks if the object exists */
{
errorResult = new Exception( "The object to delete does not exist." ):
ThrowOrAbortTransaction( errorResult );
return false;
}
if ( !ObjectDelete( object ) ) /* ObjectDelete() is implemented somewhere else, and actually does the work of deleting the object */
{
/* Of course ObjectDelete() should specify WHY it failed, but this is outside the scope of this thread */
errorResult = new Exception( "Failed to delete the object." );
ThrowOrAbortTransaction( errorResult );
return false;
}
/* Successfully deleted the object */
return true;
}
I know someone will find this useful
If you need a lightweight transaction mechanism, this is for you. All code is open source!Edited by tompsonn - 10/13/12 at 4:37am










