Merge pull request #61 from lukeabsent/AMQNET-637

AMQNET-637 More async methods implementations, refactors and increase AMQPNetLite version
diff --git a/src/NMS.AMQP/Apache-NMS-AMQP.csproj b/src/NMS.AMQP/Apache-NMS-AMQP.csproj
index 9fb8ebd..b710bc1 100644
--- a/src/NMS.AMQP/Apache-NMS-AMQP.csproj
+++ b/src/NMS.AMQP/Apache-NMS-AMQP.csproj
@@ -94,7 +94,7 @@
 
     <ItemGroup>
         <!-- AMQPNetLite.Core is .NET Standard 1.3 package -->
-        <PackageReference Include="AMQPNetLite.Core" Version="2.4.0" />
+        <PackageReference Include="AMQPNetLite.Core" Version="2.4.1" />
         <PackageReference Include="Apache.NMS" Version="2.0.0" />
         <PackageReference Include="System.Threading.Tasks.Dataflow" Version="4.9.0" />
     </ItemGroup>
diff --git a/src/NMS.AMQP/INmsTransactionContext.cs b/src/NMS.AMQP/INmsTransactionContext.cs
index 6b8f6b1..7bcfaf9 100644
--- a/src/NMS.AMQP/INmsTransactionContext.cs
+++ b/src/NMS.AMQP/INmsTransactionContext.cs
@@ -19,7 +19,6 @@
 using Apache.NMS.AMQP.Message;
 using Apache.NMS.AMQP.Meta;
 using Apache.NMS.AMQP.Provider;
-using Apache.NMS.AMQP.Util;
 
 namespace Apache.NMS.AMQP
 {
diff --git a/src/NMS.AMQP/Message/NmsMessage.cs b/src/NMS.AMQP/Message/NmsMessage.cs
index 1ca0f14..c56854c 100644
--- a/src/NMS.AMQP/Message/NmsMessage.cs
+++ b/src/NMS.AMQP/Message/NmsMessage.cs
@@ -16,7 +16,9 @@
  */
 
 using System;
+using System.Threading.Tasks;
 using Apache.NMS.AMQP.Message.Facade;
+using Apache.NMS.AMQP.Util.Synchronization;
 using Apache.NMS.Util;
 
 namespace Apache.NMS.AMQP.Message
@@ -150,11 +152,16 @@
 
         public void Acknowledge()
         {
+            AcknowledgeAsync().GetAsyncResult();
+        }
+
+        public async Task AcknowledgeAsync()
+        {
             if (NmsAcknowledgeCallback != null)
             {
                 try
                 {
-                    NmsAcknowledgeCallback.Acknowledge();
+                    await NmsAcknowledgeCallback.Acknowledge().Await();
                     NmsAcknowledgeCallback = null;
                 }
                 catch (Exception e)
diff --git a/src/NMS.AMQP/NmsAcknowledgeCallback.cs b/src/NMS.AMQP/NmsAcknowledgeCallback.cs
index 40364c6..fefb32d 100644
--- a/src/NMS.AMQP/NmsAcknowledgeCallback.cs
+++ b/src/NMS.AMQP/NmsAcknowledgeCallback.cs
@@ -15,8 +15,10 @@
  * limitations under the License.
  */
 
+using System.Threading.Tasks;
 using Apache.NMS.AMQP.Message;
 using Apache.NMS.AMQP.Util;
+using Apache.NMS.AMQP.Util.Synchronization;
 
 namespace Apache.NMS.AMQP
 {
@@ -32,7 +34,7 @@
             this.envelope = envelope;
         }
 
-        public void Acknowledge()
+        public async Task Acknowledge()
         {
             if (session.IsClosed)
             {
@@ -41,11 +43,11 @@
 
             if (envelope == null)
             {
-                session.Acknowledge(AcknowledgementType);
+                await session.AcknowledgeAsync(AcknowledgementType).Await();
             }
             else
             {
-                session.AcknowledgeIndividual(AcknowledgementType, envelope);
+                await session.AcknowledgeIndividualAsync(AcknowledgementType, envelope).Await();
             }
         }
 
diff --git a/src/NMS.AMQP/NmsConnection.cs b/src/NMS.AMQP/NmsConnection.cs
index 9ecffa9..e0a98d8 100644
--- a/src/NMS.AMQP/NmsConnection.cs
+++ b/src/NMS.AMQP/NmsConnection.cs
@@ -25,6 +25,7 @@
 using Apache.NMS.AMQP.Meta;
 using Apache.NMS.AMQP.Provider;
 using Apache.NMS.AMQP.Util;
+using Apache.NMS.AMQP.Util.Synchronization;
 using Apache.NMS.Util;
 
 namespace Apache.NMS.AMQP
@@ -43,7 +44,7 @@
         private readonly AtomicLong temporaryQueueIdGenerator = new AtomicLong();
         private readonly AtomicLong transactionIdGenerator = new AtomicLong();
         private Exception failureCause;
-        private readonly object syncRoot = new object();
+        private readonly NmsSynchronizationMonitor syncRoot = new NmsSynchronizationMonitor();
 
         public NmsConnection(NmsConnectionInfo connectionInfo, IProvider provider)
         {
@@ -89,12 +90,18 @@
             DoStop(true);
         }
 
+        public Task StopAsync()
+        {
+            Stop();
+            return Task.CompletedTask;
+        }
+
         private void DoStop(bool checkClosed)
         {
             if (checkClosed)
                 CheckClosedOrFailed();
 
-            CheckIsOnDeliveryThread();
+            CheckIsOnDeliveryExecutionFlow();
 
             if (started.CompareAndSet(true, false))
             {
@@ -112,13 +119,23 @@
 
         public ISession CreateSession(AcknowledgementMode acknowledgementMode)
         {
+            return CreateSessionAsync(acknowledgementMode).GetAsyncResult();
+        }
+
+        public Task<ISession> CreateSessionAsync()
+        {
+            return CreateSessionAsync(AcknowledgementMode);
+        }
+
+        public async Task<ISession> CreateSessionAsync(AcknowledgementMode acknowledgementMode)
+        {
             CheckClosedOrFailed();
-            CreateNmsConnection();
+            await CreateNmsConnectionAsync().Await();
 
             NmsSession session = new NmsSession(this, GetNextSessionId(), acknowledgementMode);
             try
             {
-                session.Begin().ConfigureAwait(false).GetAwaiter().GetResult();
+                await session.Begin().Await();
                 sessions.TryAdd(session.SessionInfo.Id, session);
                 if (started)
                 {
@@ -139,18 +156,23 @@
 
         public void Close()
         {
-            CheckIsOnDeliveryThread();
+            CloseAsync().GetAsyncResult();
+        }
+
+        public async Task CloseAsync()
+        {
+            CheckIsOnDeliveryExecutionFlow();
 
             if (closed.CompareAndSet(false, true))
             {
                 DoStop(false);
 
                 foreach (NmsSession session in sessions.Values)
-                    session.Shutdown(null);
+                    await session.ShutdownAsync(null).Await();;
 
                 try
                 {
-                    provider.Close();
+                    await provider.CloseAsync().Await();;
                 }
                 catch (Exception)
                 {
@@ -170,7 +192,12 @@
 
         public void Start()
         {
-            CreateNmsConnection();
+            StartAsync().GetAsyncResult();
+        }
+        
+        public async Task StartAsync()
+        {
+            await CreateNmsConnectionAsync().Await();;
 
             if (started.CompareAndSet(false, true))
             {
@@ -288,12 +315,12 @@
         {
             foreach (NmsTemporaryDestination tempDestination in tempDestinations.Values)
             {
-                await provider.CreateResource(tempDestination);
+                await provider.CreateResource(tempDestination).Await();;
             }
 
             foreach (NmsSession session in sessions.Values)
             {
-                await session.OnConnectionRecovery(provider).ConfigureAwait(false);
+                await session.OnConnectionRecovery(provider).Await();
             }
         }
 
@@ -314,7 +341,7 @@
 
             foreach (NmsSession session in sessions.Values)
             {
-                await session.OnConnectionRecovered(provider).ConfigureAwait(false);
+                await session.OnConnectionRecovered(provider).Await();
             }
         }
 
@@ -403,38 +430,56 @@
             return provider.Send(envelope);
         }
 
-        private void CheckIsOnDeliveryThread()
+        private void CheckIsOnDeliveryExecutionFlow()
         {
             foreach (NmsSession session in sessions.Values)
             {
-                session.CheckIsOnDeliveryThread();
+                session.CheckIsOnDeliveryExecutionFlow();
             }
         }
 
         private void CreateNmsConnection()
         {
+            CreateNmsConnectionInternal(true).GetAsyncResult();
+        }
+
+        private Task CreateNmsConnectionAsync()
+        {
+            return CreateNmsConnectionInternal(false);
+        }
+        
+        private async Task CreateNmsConnectionInternal(bool sync = true)
+        {
             if (connected || closed)
             {
                 return;
             }
 
-            lock (syncRoot)
+            var syncLock = sync ? syncRoot.Lock() : await syncRoot.LockAsync();
+            using(syncLock)
             {
                 if (closed || connected)
                 {
                     return;
                 }
-                
+
                 try
                 {
-                    provider.Connect(ConnectionInfo).ConfigureAwait(false).GetAwaiter().GetResult();
+                    var configureTask = provider.Connect(ConnectionInfo).Await();
+                    if (sync)
+                        configureTask.GetAwaiter().GetResult();
+                    else
+                        await configureTask;
                     connected.Set(true);
                 }
                 catch (Exception e)
                 {
                     try
                     {
-                        provider.Close();
+                        if (sync)
+                            provider.Close();
+                        else
+                            await provider.CloseAsync().Await();
                     }
                     catch
                     {
@@ -444,6 +489,7 @@
                     throw NMSExceptionSupport.Create(e);
                 }
             }
+            
         }
 
         internal void OnAsyncException(Exception error)
@@ -488,23 +534,33 @@
 
         public ITemporaryQueue CreateTemporaryQueue()
         {
+            return CreateTemporaryQueueAsync().GetAsyncResult();
+        } 
+        
+        public async Task<ITemporaryQueue> CreateTemporaryQueueAsync()
+        {
             var destinationName = $"{Id}:{temporaryQueueIdGenerator.IncrementAndGet().ToString()}";
             var queue = new NmsTemporaryQueue(destinationName);
-            InitializeTemporaryDestination(queue);
+            await InitializeTemporaryDestinationAsync(queue).Await();;
             return queue;
         }
 
         public ITemporaryTopic CreateTemporaryTopic()
         {
+            return CreateTemporaryTopicAsync().GetAsyncResult();
+        } 
+        
+        public async Task<ITemporaryTopic> CreateTemporaryTopicAsync()
+        {
             var destinationName = $"{Id}:{temporaryTopicIdGenerator.IncrementAndGet().ToString()}";
             NmsTemporaryTopic topic = new NmsTemporaryTopic(destinationName);
-            InitializeTemporaryDestination(topic);
+            await InitializeTemporaryDestinationAsync(topic).Await();;
             return topic;
         }
 
-        private void InitializeTemporaryDestination(NmsTemporaryDestination temporaryDestination)
+        private async Task InitializeTemporaryDestinationAsync(NmsTemporaryDestination temporaryDestination)
         {
-            CreateResource(temporaryDestination).ConfigureAwait(false).GetAwaiter().GetResult();
+            await CreateResource(temporaryDestination).Await();
             tempDestinations.TryAdd(temporaryDestination, temporaryDestination);
             temporaryDestination.Connection = this;
         }
@@ -515,7 +571,7 @@
                 throw new InvalidDestinationException("Can't consume from a temporary destination created using another connection");
         }
 
-        public void DeleteTemporaryDestination(NmsTemporaryDestination destination)
+        public async Task DeleteTemporaryDestinationAsync(NmsTemporaryDestination destination)
         {
             CheckClosedOrFailed();
 
@@ -531,7 +587,7 @@
 
                 tempDestinations.TryRemove(destination, out _);
 
-                DestroyResource(destination).ConfigureAwait(false).GetAwaiter().GetResult();
+                await DestroyResource(destination).Await();
             }
             catch (Exception e)
             {
@@ -541,11 +597,16 @@
 
         public void Unsubscribe(string subscriptionName)
         {
-            CheckClosedOrFailed();
-
-            provider.Unsubscribe(subscriptionName).ConfigureAwait(false).GetAwaiter().GetResult();
+            UnsubscribeAsync(subscriptionName).GetAsyncResult();
         }
 
+        public async Task UnsubscribeAsync(string subscriptionName)
+        {
+            CheckClosedOrFailed();
+            
+            await provider.Unsubscribe(subscriptionName).Await();
+        }
+        
         public Task Rollback(NmsTransactionInfo transactionInfo, NmsTransactionInfo nextTransactionInfo)
         {
             return provider.Rollback(transactionInfo, nextTransactionInfo);
diff --git a/src/NMS.AMQP/NmsConnectionFactory.cs b/src/NMS.AMQP/NmsConnectionFactory.cs
index 88101bb..66543df 100644
--- a/src/NMS.AMQP/NmsConnectionFactory.cs
+++ b/src/NMS.AMQP/NmsConnectionFactory.cs
@@ -17,9 +17,11 @@
 

 using System;

 using System.Collections.Specialized;

+using System.Threading.Tasks;

 using Apache.NMS.AMQP.Meta;

 using Apache.NMS.AMQP.Provider;

 using Apache.NMS.AMQP.Util;

+using Apache.NMS.AMQP.Util.Synchronization;

 using Apache.NMS.Util;

 using URISupport = Apache.NMS.AMQP.Util.URISupport;

 

@@ -166,7 +168,17 @@
         {

             return CreateConnection(UserName, Password);

         }

+        

+        public Task<IConnection> CreateConnectionAsync()

+        {

+            return CreateConnectionAsync(UserName, Password);

+        }

 

+        public Task<IConnection> CreateConnectionAsync(string userName, string password)

+        {

+            return Task.FromResult(CreateConnection(userName, password));

+        }

+       

         public IConnection CreateConnection(string userName, string password)

         {

             try

@@ -201,6 +213,26 @@
             return new NmsContext((NmsConnection)CreateConnection(userName, password), acknowledgementMode);

         }

 

+        public async Task<INMSContext> CreateContextAsync()

+        {

+            return new NmsContext((NmsConnection)await CreateConnectionAsync().Await(), AcknowledgementMode.AutoAcknowledge);

+        }

+

+        public async Task<INMSContext> CreateContextAsync(AcknowledgementMode acknowledgementMode)

+        {

+            return new NmsContext((NmsConnection)await CreateConnectionAsync().Await(), acknowledgementMode);

+        }

+

+        public async Task<INMSContext> CreateContextAsync(string userName, string password)

+        {

+            return new NmsContext((NmsConnection)await CreateConnectionAsync(userName, password).Await(), AcknowledgementMode.AutoAcknowledge);

+        }

+

+        public async Task<INMSContext> CreateContextAsync(string userName, string password, AcknowledgementMode acknowledgementMode)

+        {

+            return new NmsContext((NmsConnection)await CreateConnectionAsync(userName, password).Await(), acknowledgementMode);

+        }

+

         public Uri BrokerUri

         {

             get => brokerUri;

diff --git a/src/NMS.AMQP/NmsConsumer.cs b/src/NMS.AMQP/NmsConsumer.cs
index 9a2d40d..d6d905b 100644
--- a/src/NMS.AMQP/NmsConsumer.cs
+++ b/src/NMS.AMQP/NmsConsumer.cs
@@ -16,6 +16,7 @@
  */
 
 using System;
+using System.Threading.Tasks;
 
 namespace Apache.NMS.AMQP
 {
@@ -40,11 +41,21 @@
             return consumer.Receive();
         }
 
+        public Task<IMessage> ReceiveAsync()
+        {
+            return consumer.ReceiveAsync();
+        }
+
         public IMessage Receive(TimeSpan timeout)
         {
             return consumer.Receive(timeout);
         }
 
+        public Task<IMessage> ReceiveAsync(TimeSpan timeout)
+        {
+            return consumer.ReceiveAsync(timeout);
+        }
+
         public IMessage ReceiveNoWait()
         {
             return consumer.ReceiveNoWait();
@@ -55,11 +66,21 @@
             return consumer.ReceiveBody<T>();
         }
 
+        public Task<T> ReceiveBodyAsync<T>()
+        {
+            return consumer.ReceiveBodyAsync<T>();
+        }
+
         public T ReceiveBody<T>(TimeSpan timeout)
         {
             return consumer.ReceiveBody<T>(timeout);
         }
 
+        public Task<T> ReceiveBodyAsync<T>(TimeSpan timeout)
+        {
+            return consumer.ReceiveBodyAsync<T>(timeout);
+        }
+
         public T ReceiveBodyNoWait<T>()
         {
             return consumer.ReceiveBodyNoWait<T>();
@@ -70,6 +91,11 @@
             consumer.Close();
         }
 
+        public Task CloseAsync()
+        {
+            return consumer.CloseAsync();
+        }
+
         public string MessageSelector => consumer.MessageSelector;
 
         public ConsumerTransformerDelegate ConsumerTransformer
diff --git a/src/NMS.AMQP/NmsContext.cs b/src/NMS.AMQP/NmsContext.cs
index 260647e..d8b905e 100644
--- a/src/NMS.AMQP/NmsContext.cs
+++ b/src/NMS.AMQP/NmsContext.cs
@@ -16,13 +16,15 @@
  */
 
 using System;
+using System.Threading.Tasks;
 using Apache.NMS.AMQP.Util;
+using Apache.NMS.AMQP.Util.Synchronization;
 
 namespace Apache.NMS.AMQP
 {
     public class NmsContext : INMSContext
     {
-        private readonly object syncRoot = new object();
+        private readonly NmsSynchronizationMonitor syncRoot = new NmsSynchronizationMonitor();
 
         private readonly NmsConnection connection;
         private readonly AtomicLong connectionRefCount;
@@ -57,6 +59,11 @@
             connection.Start();
         }
 
+        public Task StartAsync()
+        {
+            return connection.StartAsync();
+        }
+
         public bool IsStarted { get => connection.IsStarted; }
         
         public void Stop()
@@ -64,6 +71,11 @@
             connection.Stop();
         }
 
+        public Task StopAsync()
+        {
+            return connection.StopAsync();
+        }
+
         public INMSContext CreateContext(AcknowledgementMode acknowledgementMode)
         {
             if (connectionRefCount.Get() == 0) {
@@ -76,9 +88,12 @@
 
         public INMSProducer CreateProducer()
         {
-            if (sharedProducer == null) {
-                lock (syncRoot) {
-                    if (sharedProducer == null) {
+            if (sharedProducer == null)
+            {
+                using(syncRoot.Lock())
+                {
+                    if (sharedProducer == null)
+                    {
                         sharedProducer = (NmsMessageProducer) GetSession().CreateProducer();
                     }
                 }
@@ -86,6 +101,21 @@
             return new NmsProducer(GetSession(), sharedProducer);
         }
 
+        public async Task<INMSProducer> CreateProducerAsync()
+        {
+            if (sharedProducer == null)
+            {
+                using (await syncRoot.LockAsync().Await())
+                {
+                    if (sharedProducer == null)
+                    {
+                        sharedProducer = (NmsMessageProducer) await (await GetSessionAsync().Await()).CreateProducerAsync().Await();
+                    }
+                }
+            }
+            return new NmsProducer(await GetSessionAsync(), sharedProducer);
+        }
+
 
         public INMSConsumer CreateConsumer(IDestination destination)
         {
@@ -137,96 +167,238 @@
             return StartIfNeeded(new NmsConsumer(GetSession(), (NmsMessageConsumer) GetSession().CreateSharedDurableConsumer(destination, subscriptionName, selector)));
         }
 
+        public async Task<INMSConsumer> CreateConsumerAsync(IDestination destination)
+        {
+            return await StartIfNeededAsync(new NmsConsumer(await GetSessionAsync().Await(), (NmsMessageConsumer) await (await GetSessionAsync()).CreateConsumerAsync(destination)));
+        }
+
+        public async Task<INMSConsumer> CreateConsumerAsync(IDestination destination, string selector)
+        {
+            return await StartIfNeededAsync(new NmsConsumer(await GetSessionAsync().Await(), (NmsMessageConsumer) await (await GetSessionAsync()).CreateConsumerAsync(destination, selector)));
+        }
+
+        public async Task<INMSConsumer> CreateConsumerAsync(IDestination destination, string selector, bool noLocal)
+        {
+            return await StartIfNeededAsync(new NmsConsumer(await GetSessionAsync(), (NmsMessageConsumer) await (await GetSessionAsync()).CreateConsumerAsync(destination, selector, noLocal)));
+        }
+
+        public async Task<INMSConsumer> CreateDurableConsumerAsync(ITopic destination, string subscriptionName)
+        {
+            return await StartIfNeededAsync(new NmsConsumer(await GetSessionAsync(), (NmsMessageConsumer) await (await GetSessionAsync()).CreateDurableConsumerAsync(destination, subscriptionName)));
+        }
+
+        public async Task<INMSConsumer> CreateDurableConsumerAsync(ITopic destination, string subscriptionName, string selector)
+        {
+            return await StartIfNeededAsync(new NmsConsumer(await GetSessionAsync(), (NmsMessageConsumer) await (await GetSessionAsync()).CreateDurableConsumerAsync(destination, subscriptionName, selector)));
+        }
+
+        public async Task<INMSConsumer> CreateDurableConsumerAsync(ITopic destination, string subscriptionName, string selector, bool noLocal)
+        {
+            return await StartIfNeededAsync(new NmsConsumer(await GetSessionAsync(), (NmsMessageConsumer) await (await GetSessionAsync()).CreateDurableConsumerAsync(destination, subscriptionName, selector, noLocal)));
+        }
+
+        public async Task<INMSConsumer> CreateSharedConsumerAsync(ITopic destination, string subscriptionName)
+        {
+            return await StartIfNeededAsync(new NmsConsumer(await GetSessionAsync(), (NmsMessageConsumer) await (await GetSessionAsync()).CreateSharedConsumerAsync(destination, subscriptionName)));
+        }
+        
+        public async Task<INMSConsumer> CreateSharedConsumerAsync(ITopic destination, string subscriptionName, string selector)
+        {
+            return await StartIfNeededAsync(new NmsConsumer(await GetSessionAsync(), (NmsMessageConsumer) await (await GetSessionAsync()).CreateSharedConsumerAsync(destination, subscriptionName, selector)));
+        }
+
+        public async Task<INMSConsumer> CreateSharedDurableConsumerAsync(ITopic destination, string subscriptionName)
+        {
+            return await StartIfNeededAsync(new NmsConsumer(await GetSessionAsync(), (NmsMessageConsumer) await (await GetSessionAsync()).CreateSharedDurableConsumerAsync(destination, subscriptionName)));
+        }
+
+        public async Task<INMSConsumer> CreateSharedDurableConsumerAsync(ITopic destination, string subscriptionName, string selector)
+        {
+            return await StartIfNeededAsync(new NmsConsumer(await GetSessionAsync(), (NmsMessageConsumer) await (await GetSessionAsync()).CreateSharedDurableConsumerAsync(destination, subscriptionName, selector)));
+        }
+
         public void Unsubscribe(string name)
         {
             GetSession().Unsubscribe(name);
         }
 
+        public async Task UnsubscribeAsync(string name)
+        {
+            await (await GetSessionAsync().Await()).UnsubscribeAsync(name).Await();
+        }
+
         public IQueueBrowser CreateBrowser(IQueue queue)
         {
             return GetSession().CreateBrowser(queue);
         }
 
+        public async Task<IQueueBrowser> CreateBrowserAsync(IQueue queue)
+        {
+            return await (await GetSessionAsync().Await()).CreateBrowserAsync(queue).Await();
+        }
+
         public IQueueBrowser CreateBrowser(IQueue queue, string selector)
         {
             return GetSession().CreateBrowser(queue, selector);
         }
 
+        public async Task<IQueueBrowser> CreateBrowserAsync(IQueue queue, string selector)
+        {
+            return await (await GetSessionAsync().Await()).CreateBrowserAsync(queue, selector).Await();
+        }
+
         public IQueue GetQueue(string name)
         {
             return GetSession().GetQueue(name);
         }
 
+        public async Task<IQueue> GetQueueAsync(string name)
+        {
+            return await (await GetSessionAsync().Await()).GetQueueAsync(name).Await();
+        }
+
         public ITopic GetTopic(string name)
         {
             return GetSession().GetTopic(name);
         }
 
+        public async Task<ITopic> GetTopicAsync(string name)
+        {
+            return await (await GetSessionAsync().Await()).GetTopicAsync(name).Await();
+        }
+
         public ITemporaryQueue CreateTemporaryQueue()
         {
             return GetSession().CreateTemporaryQueue();
         }
 
+        public async Task<ITemporaryQueue> CreateTemporaryQueueAsync()
+        {
+            return await (await GetSessionAsync().Await()).CreateTemporaryQueueAsync().Await();
+        }
+
         public ITemporaryTopic CreateTemporaryTopic()
         {
             return GetSession().CreateTemporaryTopic();
         }
 
+        public async Task<ITemporaryTopic> CreateTemporaryTopicAsync()
+        {
+            return await (await GetSessionAsync().Await()).CreateTemporaryTopicAsync().Await();
+        }
+
         public IMessage CreateMessage()
         {
             return GetSession().CreateMessage();
         }
 
+        public async Task<IMessage> CreateMessageAsync()
+        {
+            return await (await GetSessionAsync().Await()).CreateMessageAsync().Await();
+        }
+
         public ITextMessage CreateTextMessage()
         {
             return GetSession().CreateTextMessage();
         }
 
+        public async Task<ITextMessage> CreateTextMessageAsync()
+        {
+            return await (await GetSessionAsync().Await()).CreateTextMessageAsync().Await();
+        }
+
         public ITextMessage CreateTextMessage(string text)
         {
             return GetSession().CreateTextMessage(text);
         }
 
+        public async Task<ITextMessage> CreateTextMessageAsync(string text)
+        {
+            return await (await GetSessionAsync().Await()).CreateTextMessageAsync(text).Await();
+        }
+
         public IMapMessage CreateMapMessage()
         {
             return GetSession().CreateMapMessage();
         }
 
+        public async Task<IMapMessage> CreateMapMessageAsync()
+        {
+            return await (await GetSessionAsync().Await()).CreateMapMessageAsync().Await();
+        }
+
         public IObjectMessage CreateObjectMessage(object body)
         {
             return GetSession().CreateObjectMessage(body);
         }
 
+        public async Task<IObjectMessage> CreateObjectMessageAsync(object body)
+        {
+            return await (await GetSessionAsync().Await()).CreateObjectMessageAsync(body).Await();
+        }
+
         public IBytesMessage CreateBytesMessage()
         {
             return GetSession().CreateBytesMessage();
         }
 
+        public async Task<IBytesMessage> CreateBytesMessageAsync()
+        {
+            return await (await GetSessionAsync().Await()).CreateBytesMessageAsync().Await();
+        }
+
         public IBytesMessage CreateBytesMessage(byte[] body)
         {
             return GetSession().CreateBytesMessage(body);
         }
 
+        public async Task<IBytesMessage> CreateBytesMessageAsync(byte[] body)
+        {
+            return await (await GetSessionAsync().Await()).CreateBytesMessageAsync(body).Await();
+        }
+
         public IStreamMessage CreateStreamMessage()
         {
             return GetSession().CreateStreamMessage();
         }
 
+        public async Task<IStreamMessage> CreateStreamMessageAsync()
+        {
+            return await (await GetSessionAsync().Await()).CreateStreamMessageAsync().Await();
+        }
+
         public void Close()
         {
+            CloseInternal(true).GetAsyncResult();
+        }
+
+        public Task CloseAsync()
+        {
+            return CloseInternal(false);
+        }
+
+        public async Task CloseInternal(bool sync)
+        {
             NMSException failure = null;
 
             try
             {
-                session?.Close();
+                if (sync)
+                    session?.Close();
+                else
+                    await (session?.CloseAsync() ?? Task.CompletedTask).Await();
             } catch (NMSException jmse)
             {
                 failure = jmse;
             }
 
             if (connectionRefCount.DecrementAndGet() == 0) {
-                try {
-                    connection.Close();
+                try
+                {
+                    if (sync)
+                        connection.Close();
+                    else
+                        await connection.CloseAsync().Await();
                 } catch (NMSException jmse) {
                     if (failure == null)
                     {
@@ -239,27 +411,48 @@
                 throw failure;
             }
         }
-
+        
+        
         public void Recover()
         {
             GetSession().Recover();
         }
 
+        public async Task RecoverAsync()
+        {
+            await (await GetSessionAsync().Await()).RecoverAsync().Await();
+        }
+
         public void Acknowledge()
         {
             GetSession().Acknowledge();
         }
 
+        public async Task AcknowledgeAsync()
+        {
+            await (await GetSessionAsync().Await()).AcknowledgeAsync().Await();
+        }
+
         public void Commit()
         {
             GetSession().Commit();
         }
 
+        public async Task CommitAsync()
+        {
+            await (await GetSessionAsync().Await()).CommitAsync().Await();
+        }
+
         public void Rollback()
         {
             GetSession().Rollback();
         }
 
+        public async Task RollbackAsync()
+        {
+            await (await GetSessionAsync().Await()).RollbackAsync().Await();
+        }
+
         public void PurgeTempDestinations()
         {
             connection.PurgeTempDestinations();
@@ -267,8 +460,10 @@
         
         
         private NmsSession GetSession() {
-            if (session == null) {
-                lock (syncRoot) {
+            if (session == null)
+            {
+                using( syncRoot.Lock())
+                {
                     if (session == null)
                     {
                         session = (NmsSession) connection.CreateSession(AcknowledgementMode);
@@ -277,6 +472,22 @@
             }
             return session;
         }
+
+        private async Task<NmsSession> GetSessionAsync()
+        {
+            if (session == null)
+            {
+                using(await syncRoot.LockAsync().Await())
+                {
+                    if (session == null)
+                    {
+                        session = (NmsSession) await connection.CreateSessionAsync(AcknowledgementMode).Await();
+                    }
+                }
+            }
+
+            return session;
+        }
         
         private NmsConsumer StartIfNeeded(NmsConsumer consumer) {
             if (autoStart) {
@@ -285,6 +496,13 @@
             return consumer;
         }
         
+        private async Task<NmsConsumer> StartIfNeededAsync(NmsConsumer consumer) {
+            if (autoStart) {
+                await connection.StartAsync().Await();
+            }
+            return consumer;
+        }
+        
 
         public ConsumerTransformerDelegate ConsumerTransformer { get => session.ConsumerTransformer; set => session.ConsumerTransformer = value; }
         
diff --git a/src/NMS.AMQP/NmsLocalTransactionContext.cs b/src/NMS.AMQP/NmsLocalTransactionContext.cs
index caae980..58995f8 100644
--- a/src/NMS.AMQP/NmsLocalTransactionContext.cs
+++ b/src/NMS.AMQP/NmsLocalTransactionContext.cs
@@ -23,6 +23,7 @@
 using Apache.NMS.AMQP.Meta;
 using Apache.NMS.AMQP.Provider;
 using Apache.NMS.AMQP.Util;
+using Apache.NMS.AMQP.Util.Synchronization;
 using Apache.NMS.Util;
 
 namespace Apache.NMS.AMQP
@@ -47,7 +48,7 @@
         {
             if (!IsInDoubt())
             {
-                await this.connection.Send(envelope);
+                await this.connection.Send(envelope).Await();
                 this.participants.Add(envelope.ProducerId);
             }
         }
@@ -59,7 +60,7 @@
             {
                 try
                 {
-                    await this.connection.Acknowledge(envelope, ackType).ConfigureAwait(false);
+                    await this.connection.Acknowledge(envelope, ackType).Await();
                     this.participants.Add(envelope.ConsumerId);
                     Tracer.Debug($"TX:{this.transactionInfo.Id} has performed an acknowledge.");
                 }
@@ -72,7 +73,7 @@
             }
             else
             {
-                await this.connection.Acknowledge(envelope, ackType).ConfigureAwait(false);
+                await this.connection.Acknowledge(envelope, ackType).Await();
             }
         }
 
@@ -83,7 +84,7 @@
             try
             {
                 Reset();
-                await this.session.Connection.CreateResource(this.transactionInfo);
+                await this.session.Connection.CreateResource(this.transactionInfo).Await();
                 OnTransactionStarted();
                 Tracer.Debug($"Begin: {this.transactionInfo.Id}");
             }
@@ -120,7 +121,7 @@
             {
                 this.transactionInfo = GetNextTransactionInfo();
                 Tracer.Debug($"Transaction recovery creating new TX:{this.transactionInfo.Id} after failover.");
-                await provider.CreateResource(this.transactionInfo).ConfigureAwait(false);
+                await provider.CreateResource(this.transactionInfo).Await();
             }
         }
 
@@ -141,7 +142,7 @@
             {
                 try
                 {
-                    await Rollback();
+                    await Rollback().Await();
                 }
                 catch (Exception e)
                 {
@@ -157,7 +158,7 @@
 
             try
             {
-                await this.connection.Commit(this.transactionInfo, nextTx).ConfigureAwait(false);
+                await this.connection.Commit(this.transactionInfo, nextTx).Await();
                 OnTransactionCommitted();
                 Reset();
                 this.transactionInfo = nextTx;
@@ -180,7 +181,7 @@
                     // one to recover our state.
                     if (nextTx.ProviderTxId == null)
                     {
-                        await Begin().ConfigureAwait(false);
+                        await Begin().Await();
                     }
                 }
                 catch (Exception e)
@@ -202,7 +203,7 @@
 
             try
             {
-                await this.connection.Rollback(this.transactionInfo, nextTx);
+                await this.connection.Rollback(this.transactionInfo, nextTx).Await();
                 OnTransactionRolledBack();
                 Reset();
                 this.transactionInfo = nextTx;
@@ -223,7 +224,7 @@
                     // one to recover our state.
                     if (startNewTransaction && nextTx.ProviderTxId == null)
                     {
-                        await Begin();
+                        await Begin().Await();
                     }
                 }
                 catch (Exception e)
diff --git a/src/NMS.AMQP/NmsMessageConsumer.cs b/src/NMS.AMQP/NmsMessageConsumer.cs
index 36abe8f..d64632d 100644
--- a/src/NMS.AMQP/NmsMessageConsumer.cs
+++ b/src/NMS.AMQP/NmsMessageConsumer.cs
@@ -21,13 +21,14 @@
 using Apache.NMS.AMQP.Meta;
 using Apache.NMS.AMQP.Provider;
 using Apache.NMS.AMQP.Util;
+using Apache.NMS.AMQP.Util.Synchronization;
 using Apache.NMS.Util;
 
 namespace Apache.NMS.AMQP
 {
     public class NmsMessageConsumer : IMessageConsumer
     {
-        private readonly object syncRoot = new object();
+        private readonly NmsSynchronizationMonitor syncRoot = new NmsSynchronizationMonitor();
         private readonly AcknowledgementMode acknowledgementMode;
         private readonly AtomicBool closed = new AtomicBool();
         private readonly MessageDeliveryTask deliveryTask;
@@ -64,13 +65,6 @@
 
             };
             deliveryTask = new MessageDeliveryTask(this);
-            
-            Session.Connection.CreateResource(Info).ConfigureAwait(false).GetAwaiter().GetResult();
-            
-            Session.Add(this);
-
-            if (Session.IsStarted)
-                Start();
         }
 
         public NmsSession Session { get; }
@@ -97,13 +91,18 @@
 
         public void Close()
         {
+            CloseAsync().GetAsyncResult();
+        }
+
+        public async Task CloseAsync()
+        {
             if (closed)
                 return;
 
-            lock (syncRoot)
+            using(await syncRoot.LockAsync().Await())
             {
                 Shutdown(null);
-                Session.Connection.DestroyResource(Info).ConfigureAwait(false).GetAwaiter().GetResult();
+                await Session.Connection.DestroyResource(Info).Await();
             }
         }
 
@@ -116,7 +115,7 @@
             add
             {
                 CheckClosed();
-                lock (syncRoot)
+                using(syncRoot.Lock())
                 {
                     Listener += value;
                     DrainMessageQueueToListener();
@@ -124,7 +123,7 @@
             }
             remove
             {
-                lock (syncRoot)
+                using(syncRoot.Lock())
                 {
                     Listener -= value;
                 }
@@ -140,12 +139,12 @@
             {
                 if (started)
                 {
-                    return ReceiveInternal(-1);
+                    return ReceiveInternalAsync(-1).GetAsyncResult();
                 }
             }
         }
-        
-        public T ReceiveBody<T>()
+
+        public async Task<IMessage> ReceiveAsync()
         {
             CheckClosed();
             CheckMessageListener();
@@ -154,7 +153,26 @@
             {
                 if (started)
                 {
-                    return ReceiveBodyInternal<T>(-1);
+                    return await ReceiveInternalAsync(-1).Await();
+                }
+            }
+        }
+
+        public T ReceiveBody<T>()
+        {
+            return ReceiveBodyAsync<T>().GetAsyncResult();
+        }
+        
+        public Task<T> ReceiveBodyAsync<T>()
+        {
+            CheckClosed();
+            CheckMessageListener();
+
+            while (true)
+            {
+                if (started)
+                {
+                    return ReceiveBodyInternalAsync<T>(-1);
                 }
             }
         }
@@ -165,7 +183,7 @@
             CheckClosed();
             CheckMessageListener();
 
-            return started ? ReceiveInternal(0) : null;
+            return started ? ReceiveInternalAsync(0).GetAsyncResult() : null;
         }
         
         public T ReceiveBodyNoWait<T>()
@@ -173,19 +191,24 @@
             CheckClosed();
             CheckMessageListener();
 
-            return started ? ReceiveBodyInternal<T>(0) : default;
+            return started ? ReceiveBodyInternalAsync<T>(0).GetAsyncResult() : default;
         }
 
         public IMessage Receive(TimeSpan timeout)
         {
+            return ReceiveAsync(timeout).GetAsyncResult();
+        }
+        
+        public async Task<IMessage> ReceiveAsync(TimeSpan timeout)
+        {
             CheckClosed();
             CheckMessageListener();
-
+            
             int timeoutInMilliseconds = (int) timeout.TotalMilliseconds;
 
             if (started)
             {
-                return ReceiveInternal(timeoutInMilliseconds);
+                return await ReceiveInternalAsync(timeoutInMilliseconds).Await();
             }
 
             long deadline = GetDeadline(timeoutInMilliseconds);
@@ -200,13 +223,18 @@
 
                 if (started)
                 {
-                    return ReceiveInternal(timeoutInMilliseconds);
+                    return await ReceiveInternalAsync(timeoutInMilliseconds).Await();
                 }
             }
         }
         
         public T ReceiveBody<T>(TimeSpan timeout)
         {
+            return ReceiveBodyAsync<T>(timeout).GetAsyncResult();
+        }
+        
+        public async Task<T> ReceiveBodyAsync<T>(TimeSpan timeout)
+        {
             CheckClosed();
             CheckMessageListener();
 
@@ -214,7 +242,7 @@
 
             if (started)
             {
-                return ReceiveBodyInternal<T>(timeoutInMilliseconds);
+                return await ReceiveBodyInternalAsync<T>(timeoutInMilliseconds).Await();
             }
 
             long deadline = GetDeadline(timeoutInMilliseconds);
@@ -229,7 +257,7 @@
 
                 if (started)
                 {
-                    return ReceiveBodyInternal<T>(timeoutInMilliseconds);
+                    return await ReceiveBodyInternalAsync<T>(timeoutInMilliseconds).Await();
                 }
             }
         }
@@ -255,9 +283,16 @@
 
         private event MessageListener Listener;
 
-        public Task Init()
+        public async Task Init()
         {
-            return Session.Connection.StartResource(Info);
+            await Session.Connection.CreateResource(Info).Await();
+            
+            Session.Add(this);
+
+            if (Session.IsStarted)
+                Start();
+            
+            await Session.Connection.StartResource(Info).Await();
         }
 
         public void OnInboundMessage(InboundMessageDispatch envelope)
@@ -276,11 +311,14 @@
 
             if (Session.IsStarted && Listener != null)
             {
-                Session.EnqueueForDispatch(deliveryTask);
+                using (syncRoot.Exclude()) // Exclude lock for a time of dispatching, so it does not pass along to actionblock
+                {
+                    Session.EnqueueForDispatch(deliveryTask);
+                }
             }
         }
 
-        private void DeliverNextPending()
+        private async Task DeliverNextPendingAsync()
         {
             if (Tracer.IsDebugEnabled)
             {
@@ -289,7 +327,7 @@
             
             if (Session.IsStarted && started && Listener != null)
             {
-                lock (syncRoot)
+                using(await syncRoot.LockAsync())
                 {
                     try
                     {
@@ -313,7 +351,7 @@
                                     Tracer.Debug($"{Info.Id} filtered expired message: {envelope.Message.NMSMessageId}");
                                 }
 
-                                DoAckExpired(envelope);
+                                await DoAckExpiredAsync(envelope).Await();
                             }
                             else if (IsRedeliveryExceeded(envelope))
                             {
@@ -323,7 +361,7 @@
                                 }
 
                                 // TODO: Apply redelivery policy
-                                DoAckExpired(envelope);
+                                await DoAckExpiredAsync(envelope).Await();
                             }
                             else
                             {
@@ -331,9 +369,9 @@
                                 bool autoAckOrDupsOk = acknowledgementMode == AcknowledgementMode.AutoAcknowledge || acknowledgementMode == AcknowledgementMode.DupsOkAcknowledge;
 
                                 if (autoAckOrDupsOk)
-                                    DoAckDelivered(envelope);
+                                    await DoAckDeliveredAsync(envelope).Await();
                                 else
-                                    AckFromReceive(envelope);
+                                    await AckFromReceiveAsync(envelope).Await();
 
                                 try
                                 {
@@ -347,9 +385,9 @@
                                 if (autoAckOrDupsOk)
                                 {
                                     if (!deliveryFailed)
-                                        DoAckConsumed(envelope);
+                                        await DoAckConsumedAsync(envelope).Await();
                                     else
-                                        DoAckReleased(envelope);
+                                        await DoAckReleasedAsync(envelope).Await();
                                 }
                             }
                         }
@@ -386,29 +424,31 @@
             return false;
         }
 
-        private void DoAckReleased(InboundMessageDispatch envelope)
+        private Task DoAckReleasedAsync(InboundMessageDispatch envelope)
         {
-            Session.AcknowledgeIndividual(AckType.RELEASED, envelope);
+            return Session.AcknowledgeIndividualAsync(AckType.RELEASED, envelope);
         }
 
-        private IMessage ReceiveInternal(int timeout)
+        
+        private Task<IMessage> ReceiveInternalAsync(int timeout)
         {
-            return ReceiveInternal(timeout, envelope =>
+            return ReceiveInternalBaseAsync(timeout, async envelope =>
             {
                 IMessage message = envelope.Message.Copy();
-                AckFromReceive(envelope);
+                await AckFromReceiveAsync(envelope);
                 return message;
             });
         }
         
-        private T ReceiveBodyInternal<T>(int timeout)
+        
+        private Task<T> ReceiveBodyInternalAsync<T>(int timeout)
         {
-            return ReceiveInternal<T>(timeout, envelope =>
+            return ReceiveInternalBaseAsync<T>(timeout, async envelope =>
             {
                 try
                 {
                     T body = envelope.Message.Body<T>();
-                    AckFromReceive(envelope);
+                    await AckFromReceiveAsync(envelope);
                     return body;
                 }
                 catch (MessageFormatException mfe)
@@ -427,7 +467,7 @@
         }
 
 
-        private T ReceiveInternal<T>(int timeout, Func<InboundMessageDispatch, T> func)
+        private async Task<T> ReceiveInternalBaseAsync<T>(int timeout, Func<InboundMessageDispatch, Task<T>> func)
         {
             try
             {
@@ -444,7 +484,7 @@
                         Tracer.Debug("Trying to dequeue next message.");
                     }
 
-                    InboundMessageDispatch envelope = messageQueue.Dequeue(timeout);
+                    InboundMessageDispatch envelope = await messageQueue.DequeueAsync(timeout).Await();
 
                     if (failureCause != null)
                         throw NMSExceptionSupport.Create(failureCause);
@@ -459,7 +499,7 @@
                             Tracer.Debug($"{Info.Id} filtered expired message: {envelope.Message.NMSMessageId}");
                         }
 
-                        DoAckExpired(envelope);
+                        await DoAckExpiredAsync(envelope).Await();
 
                         if (timeout > 0)
                             timeout = (int) Math.Max(deadline - DateTime.UtcNow.Ticks / 10_000L, 0);
@@ -472,7 +512,7 @@
                         }
 
                         // TODO: Apply redelivery policy
-                        DoAckExpired(envelope);
+                        await DoAckExpiredAsync(envelope).Await();
                     }
                     else
                     {
@@ -481,7 +521,7 @@
                             Tracer.Debug($"{Info.Id} received message {envelope.Message.NMSMessageId}.");
                         }
 
-                        return func.Invoke(envelope);
+                        return await func.Invoke(envelope);
                     }
                 }
             }
@@ -501,35 +541,35 @@
             return DateTime.UtcNow.Ticks / 10_000L + timeout;
         }
 
-        private void AckFromReceive(InboundMessageDispatch envelope)
+        private async Task AckFromReceiveAsync(InboundMessageDispatch envelope)
         {
             if (envelope?.Message != null)
             {
                 NmsMessage message = envelope.Message;
                 if (message.NmsAcknowledgeCallback != null)
                 {
-                    DoAckDelivered(envelope);
+                    await DoAckDeliveredAsync(envelope).Await();
                 }
                 else
                 {
-                    DoAckConsumed(envelope);
+                    await DoAckConsumedAsync(envelope).Await();
                 }
             }
         }
 
-        private void DoAckDelivered(InboundMessageDispatch envelope)
+        private Task DoAckDeliveredAsync(InboundMessageDispatch envelope)
         {
-            Session.Acknowledge(AckType.DELIVERED, envelope);
+            return Session.AcknowledgeAsync(AckType.DELIVERED, envelope);
         }
 
-        private void DoAckConsumed(InboundMessageDispatch envelope)
+        private Task DoAckConsumedAsync(InboundMessageDispatch envelope)
         {
-            Session.Acknowledge(AckType.ACCEPTED, envelope);
+            return Session.AcknowledgeAsync(AckType.ACCEPTED, envelope);
         }
 
-        private void DoAckExpired(InboundMessageDispatch envelope)
+        private Task DoAckExpiredAsync(InboundMessageDispatch envelope)
         {
-            Session.Acknowledge(AckType.MODIFIED_FAILED_UNDELIVERABLE, envelope);
+            return Session.AcknowledgeAsync(AckType.MODIFIED_FAILED_UNDELIVERABLE, envelope);
         }
 
         private void SetAcknowledgeCallback(InboundMessageDispatch envelope)
@@ -587,7 +627,10 @@
                 int size = messageQueue.Count;
                 for (int i = 0; i < size; i++)
                 {
-                    Session.EnqueueForDispatch(deliveryTask);
+                    using (syncRoot.Exclude()) // Exclude lock for a time of dispatching, so it does not pass along to actionblock
+                    {
+                        Session.EnqueueForDispatch(deliveryTask);
+                    }
                 }
             }
         }
@@ -599,13 +642,13 @@
 
         public async Task OnConnectionRecovered(IProvider provider)
         {
-            await provider.StartResource(Info).ConfigureAwait(false);
+            await provider.StartResource(Info).Await();
             DrainMessageQueueToListener();
         }
 
         public void Stop()
         {
-            lock (syncRoot)
+            using(syncRoot.Lock())
             {
                 started.Set(false);
             }
@@ -621,13 +664,13 @@
             messageQueue.Clear();
         }
 
-        public void SuspendForRollback()
+        public async Task SuspendForRollbackAsync()
         {
             Stop();
 
             try
             {
-                Session.Connection.StopResource(Info).ConfigureAwait(false).GetAwaiter().GetResult();
+                await Session.Connection.StopResource(Info).Await();
             }
             finally
             {
@@ -638,17 +681,17 @@
             }
         }
 
-        public void ResumeAfterRollback()
+        public async Task ResumeAfterRollbackAsync()
         {
             Start();
-            StartConsumerResource();
+            await StartConsumerResourceAsync().Await();
         }
 
-        private void StartConsumerResource()
+        private async Task StartConsumerResourceAsync()
         {
             try
             {
-                Session.Connection.StartResource(Info).ConfigureAwait(false).GetAwaiter().GetResult();
+                await Session.Connection.StartResource(Info).Await();
             }
             catch (NMSException)
             {
@@ -666,9 +709,9 @@
                 this.consumer = consumer;
             }
 
-            public void DeliverNextPending()
+            public Task DeliverNextPending()
             {
-                consumer.DeliverNextPending();
+                return consumer.DeliverNextPendingAsync();
             }
         }
     }
diff --git a/src/NMS.AMQP/NmsMessageProducer.cs b/src/NMS.AMQP/NmsMessageProducer.cs
index 3f7e3db..e215861 100644
--- a/src/NMS.AMQP/NmsMessageProducer.cs
+++ b/src/NMS.AMQP/NmsMessageProducer.cs
@@ -21,6 +21,7 @@
 using Apache.NMS.AMQP.Meta;
 using Apache.NMS.AMQP.Provider;
 using Apache.NMS.AMQP.Util;
+using Apache.NMS.AMQP.Util.Synchronization;
 
 namespace Apache.NMS.AMQP
 {
@@ -39,19 +40,22 @@
         private bool disableMessageId;
         private bool disableMessageTimestamp;
 
-        public NmsMessageProducer(NmsProducerId producerId, NmsSession session, IDestination destination)
+        internal NmsMessageProducer(NmsProducerId producerId, NmsSession session, IDestination destination)
         {
             this.session = session;
             Info = new NmsProducerInfo(producerId)
             {
                 Destination = destination
             };
+        }
 
-            session.Connection.CreateResource(Info).ConfigureAwait(false).GetAwaiter().GetResult();
+        internal async  Task Init()
+        {
+            await session.Connection.CreateResource(Info).Await();
 
             session.Add(this);
         }
-
+        
         public NmsProducerId ProducerId => Info.Id;
         public NmsProducerInfo Info { get; }
         public INmsMessageIdBuilder MessageIdBuilder { get; } = new DefaultMessageIdBuilder();
@@ -113,11 +117,16 @@
 
         public void Close()
         {
+            CloseAsync().GetAsyncResult();
+        }
+
+        public async Task CloseAsync()
+        {
             if (closed)
                 return;
 
             Shutdown();
-            session.Connection.DestroyResource(Info).ConfigureAwait(false).GetAwaiter().GetResult();
+            await session.Connection.DestroyResource(Info).Await();
         }
 
         public IMessage CreateMessage()
diff --git a/src/NMS.AMQP/NmsNoTxTransactionContext.cs b/src/NMS.AMQP/NmsNoTxTransactionContext.cs
index a8e1607..f9fc384 100644
--- a/src/NMS.AMQP/NmsNoTxTransactionContext.cs
+++ b/src/NMS.AMQP/NmsNoTxTransactionContext.cs
@@ -19,7 +19,6 @@
 using Apache.NMS.AMQP.Message;
 using Apache.NMS.AMQP.Meta;
 using Apache.NMS.AMQP.Provider;
-using Apache.NMS.AMQP.Util;
 
 namespace Apache.NMS.AMQP
 {
diff --git a/src/NMS.AMQP/NmsProducer.cs b/src/NMS.AMQP/NmsProducer.cs
index 974a185..fad1a2c 100644
--- a/src/NMS.AMQP/NmsProducer.cs
+++ b/src/NMS.AMQP/NmsProducer.cs
@@ -19,6 +19,7 @@
 using System.Collections;
 using System.Threading.Tasks;
 using Apache.NMS.AMQP.Message;
+using Apache.NMS.AMQP.Util.Synchronization;
 using Apache.NMS.Util;
 
 namespace Apache.NMS.AMQP
@@ -122,7 +123,7 @@
                 message.NMSReplyTo = replyTo;
             }
 
-            await producer.SendAsync(destination, message);
+            await producer.SendAsync(destination, message).Await();
             return this;
         }
 
@@ -200,6 +201,11 @@
             producer.Close();
         }
 
+        public Task CloseAsync()
+        {
+            return producer.CloseAsync();
+        }
+
 
         public string NMSCorrelationID
         {
diff --git a/src/NMS.AMQP/NmsQueueBrowser.cs b/src/NMS.AMQP/NmsQueueBrowser.cs
index e1eb348..4f39260 100644
--- a/src/NMS.AMQP/NmsQueueBrowser.cs
+++ b/src/NMS.AMQP/NmsQueueBrowser.cs
@@ -16,14 +16,16 @@
  */
 
 using System.Collections;
+using System.Threading.Tasks;
 using Apache.NMS.AMQP.Meta;
 using Apache.NMS.AMQP.Util;
+using Apache.NMS.AMQP.Util.Synchronization;
 
 namespace Apache.NMS.AMQP
 {
     public class NmsQueueBrowser : IQueueBrowser, IEnumerator
     {
-        private readonly object syncRoot = new object();
+        private readonly NmsSynchronizationMonitor syncRoot = new NmsSynchronizationMonitor();
 
         private readonly NmsSession session;
         private readonly IQueue destination;
@@ -103,8 +105,13 @@
 
         public void Close()
         {
+            CloseAsync().GetAsyncResult();
+        }
+
+        public async Task CloseAsync()
+        {
             if (closed.CompareAndSet(false, true)) {
-                DestroyConsumer();
+                await DestroyConsumerAsync().Await();
             }
         }
 
@@ -121,28 +128,33 @@
 
         private void CreateConsumer()
         {
-            lock (syncRoot)
+            using(syncRoot.Lock())
             {
                 if (consumer == null)
                 {
                     NmsMessageConsumer messageConsumer = new NmsQueueBrowserMessageConsumer(session.GetNextConsumerId(), session,
                         destination, selector, false);
 
-                    messageConsumer.Init().ConfigureAwait(false).GetAwaiter().GetResult();
+                    messageConsumer.Init().GetAsyncResult();
 
                     // Assign only after fully created and initialized.
                     consumer = messageConsumer;
                 }
             }
         }
-        
+
         private void DestroyConsumer()
         {
-            lock (syncRoot)
+            DestroyConsumerAsync().GetAsyncResult();
+        }
+        
+        private async Task DestroyConsumerAsync()
+        {
+            using(await syncRoot.LockAsync().Await())
             {
                 try
                 {
-                    consumer?.Close();
+                    await (consumer != null ? consumer.CloseAsync() : Task.CompletedTask).Await();
                 }
                 catch (NMSException e)
                 {
diff --git a/src/NMS.AMQP/NmsSession.cs b/src/NMS.AMQP/NmsSession.cs
index e1bb898..0eb697c 100644
--- a/src/NMS.AMQP/NmsSession.cs
+++ b/src/NMS.AMQP/NmsSession.cs
@@ -20,11 +20,11 @@
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
-using Amqp;
 using Apache.NMS.AMQP.Message;
 using Apache.NMS.AMQP.Meta;
 using Apache.NMS.AMQP.Provider;
 using Apache.NMS.AMQP.Util;
+using Apache.NMS.AMQP.Util.Synchronization;
 
 namespace Apache.NMS.AMQP
 {
@@ -64,19 +64,19 @@
 
         internal async Task Begin()
         {
-            await Connection.CreateResource(SessionInfo).ConfigureAwait(false);
+            await Connection.CreateResource(SessionInfo).Await();
 
             try
             {
                 // We always keep an open TX if transacted so start now.
-                await TransactionContext.Begin().ConfigureAwait(false);
+                await TransactionContext.Begin().Await();
             }
             catch (Exception)
             {
                 // failed, close the AMQP session before we throw
                 try
                 {
-                    await Connection.DestroyResource(SessionInfo).ConfigureAwait(false);
+                    await Connection.DestroyResource(SessionInfo).Await();
                 }
                 catch (Exception)
                 {
@@ -88,12 +88,23 @@
 
         public void Close()
         {
-            CheckIsOnDeliveryThread();
+            CheckIsOnDeliveryExecutionFlow();
 
             if (!closed)
             {
                 Shutdown();
-                Connection.DestroyResource(SessionInfo);
+                Connection.DestroyResource(SessionInfo).GetAsyncResult();
+            }
+        }
+
+        public async Task CloseAsync()
+        {
+            CheckIsOnDeliveryExecutionFlow();
+
+            if (!closed)
+            {
+                await ShutdownAsync().Await();
+                await Connection.DestroyResource(SessionInfo).Await();
             }
         }
 
@@ -114,9 +125,21 @@
             return CreateProducer(null);
         }
 
+        public Task<IMessageProducer> CreateProducerAsync()
+        {
+            return CreateProducerAsync(null);
+        }
+
         public IMessageProducer CreateProducer(IDestination destination)
         {
-            return new NmsMessageProducer(GetNextProducerId(), this, destination);
+            return CreateProducerAsync(destination).GetAsyncResult();
+        }
+
+        public async Task<IMessageProducer> CreateProducerAsync(IDestination destination)
+        {
+            var producer = new NmsMessageProducer(GetNextProducerId(), this, destination);
+            await producer.Init().Await();
+            return producer;
         }
         
         private NmsProducerId GetNextProducerId()
@@ -129,18 +152,33 @@
             return CreateConsumer(destination, null);
         }
 
+        public Task<IMessageConsumer> CreateConsumerAsync(IDestination destination)
+        {
+            return CreateConsumerAsync(destination, null);
+        }
+
         public IMessageConsumer CreateConsumer(IDestination destination, string selector)
         {
             return CreateConsumer(destination, selector, false);
         }
 
+        public Task<IMessageConsumer> CreateConsumerAsync(IDestination destination, string selector)
+        {
+            return CreateConsumerAsync(destination, selector, false);
+        }
+
         public IMessageConsumer CreateConsumer(IDestination destination, string selector, bool noLocal)
         {
+            return CreateConsumerAsync(destination, selector, noLocal).GetAsyncResult();
+        }
+
+        public async Task<IMessageConsumer> CreateConsumerAsync(IDestination destination, string selector, bool noLocal)
+        {
             CheckClosed();
 
             NmsMessageConsumer messageConsumer = new NmsMessageConsumer(GetNextConsumerId(), this, destination, selector, noLocal);
-            messageConsumer.Init().ConfigureAwait(false).GetAwaiter().GetResult();
-            
+            await messageConsumer.Init().Await();
+
             return messageConsumer;
         }
 
@@ -149,17 +187,32 @@
             return CreateDurableConsumer(destination, name, null, false);
         }
 
+        public Task<IMessageConsumer> CreateDurableConsumerAsync(ITopic destination, string name)
+        {
+            return CreateDurableConsumerAsync(destination, name, null, false);
+        }
+
         public IMessageConsumer CreateDurableConsumer(ITopic destination, string name, string selector)
         {
             return CreateDurableConsumer(destination, name, selector, false);
         }
 
+        public Task<IMessageConsumer> CreateDurableConsumerAsync(ITopic destination, string name, string selector)
+        {
+            return CreateDurableConsumerAsync(destination, name, selector, false);
+        }
+
         public IMessageConsumer CreateDurableConsumer(ITopic destination, string name, string selector, bool noLocal)
         {
+            return CreateDurableConsumerAsync(destination, name, selector, noLocal).GetAsyncResult();
+        }
+
+        public async Task<IMessageConsumer> CreateDurableConsumerAsync(ITopic destination, string name, string selector, bool noLocal)
+        {
             CheckClosed();
 
             NmsMessageConsumer messageConsumer = new NmsDurableMessageConsumer(GetNextConsumerId(), this, destination, name, selector, noLocal);
-            messageConsumer.Init().ConfigureAwait(false).GetAwaiter().GetResult();
+            await messageConsumer.Init().Await();
 
             return messageConsumer;
         }
@@ -169,12 +222,22 @@
             return CreateSharedConsumer(destination, name, null);
         }
 
+        public Task<IMessageConsumer> CreateSharedConsumerAsync(ITopic destination, string name)
+        {
+            return CreateSharedConsumerAsync(destination, name, null);
+        }
+
         public IMessageConsumer CreateSharedConsumer(ITopic destination, string name, string selector)
         {
+            return CreateSharedConsumerAsync(destination, name, selector).GetAsyncResult();
+        }
+
+        public async Task<IMessageConsumer> CreateSharedConsumerAsync(ITopic destination, string name, string selector)
+        {
             CheckClosed();
 
             NmsMessageConsumer messageConsumer = new NmsSharedMessageConsumer(GetNextConsumerId(), this, destination, name, selector, false);
-            messageConsumer.Init().ConfigureAwait(false).GetAwaiter().GetResult();
+            await messageConsumer.Init().Await();
             
             return messageConsumer;
         }
@@ -184,12 +247,22 @@
             return CreateSharedDurableConsumer(destination, name, null);
         }
 
+        public Task<IMessageConsumer> CreateSharedDurableConsumerAsync(ITopic destination, string name)
+        {
+            return CreateSharedDurableConsumerAsync(destination, name, null);
+        }
+
         public IMessageConsumer CreateSharedDurableConsumer(ITopic destination, string name, string selector)
         {
+            return CreateSharedDurableConsumerAsync(destination, name, selector).GetAsyncResult();
+        }
+
+        public async Task<IMessageConsumer> CreateSharedDurableConsumerAsync(ITopic destination, string name, string selector)
+        {
             CheckClosed();
 
             NmsMessageConsumer messageConsumer = new NmsSharedDurableMessageConsumer(GetNextConsumerId(), this, destination, name, selector, false);
-            messageConsumer.Init().ConfigureAwait(false).GetAwaiter().GetResult();
+            await messageConsumer.Init().Await();//.GetAwaiter().GetResult();
             
             return messageConsumer;
         }
@@ -206,16 +279,32 @@
 
         public void Unsubscribe(string name)
         {
-            CheckClosed();
-
-            Connection.Unsubscribe(name);
+            UnsubscribeAsync(name).GetAsyncResult();
         }
 
+        public async Task UnsubscribeAsync(string name)
+        {
+            CheckClosed();
+
+            await Connection.UnsubscribeAsync(name).Await();
+        }
+
+        public Task<IQueueBrowser> CreateBrowserAsync(IQueue queue)
+        {
+            return Task.FromResult(CreateBrowser(queue));
+        }
+       
+        public Task<IQueueBrowser> CreateBrowserAsync(IQueue queue, string selector)
+        {
+            return Task.FromResult(CreateBrowser(queue, selector));
+        }
+        
         public IQueueBrowser CreateBrowser(IQueue queue)
         {
             return CreateBrowser(queue, null);
         }
 
+       
         public IQueueBrowser CreateBrowser(IQueue queue, string selector)
         {
             CheckClosed();
@@ -230,6 +319,11 @@
             return new NmsQueue(name);
         }
 
+        public Task<IQueue> GetQueueAsync(string name)
+        {
+            return Task.FromResult(GetQueue(name));
+        }
+
         public ITopic GetTopic(string name)
         {
             CheckClosed();
@@ -237,31 +331,51 @@
             return new NmsTopic(name);
         }
 
+        public Task<ITopic> GetTopicAsync(string name)
+        {
+            return Task.FromResult(GetTopic(name));
+        }
+
         public ITemporaryQueue CreateTemporaryQueue()
         {
+            return CreateTemporaryQueueAsync().GetAsyncResult();
+        }
+
+        public async Task<ITemporaryQueue> CreateTemporaryQueueAsync()
+        {
             CheckClosed();
 
-            return Connection.CreateTemporaryQueue();
+            return await Connection.CreateTemporaryQueueAsync().Await();
         }
 
         public ITemporaryTopic CreateTemporaryTopic()
         {
+            return CreateTemporaryTopicAsync().GetAsyncResult();
+        }
+
+        public async Task<ITemporaryTopic> CreateTemporaryTopicAsync()
+        {
             CheckClosed();
 
-            return Connection.CreateTemporaryTopic();
+            return await Connection.CreateTemporaryTopicAsync().Await();
         }
 
         public void DeleteDestination(IDestination destination)
         {
+            DeleteDestinationAsync(destination).GetAsyncResult();
+        }
+
+        public async Task DeleteDestinationAsync(IDestination destination)
+        {
             CheckClosed();
 
             if (destination == null)
                 return;
 
             if (destination is ITemporaryQueue temporaryQueue)
-                temporaryQueue.Delete();
+                await temporaryQueue.DeleteAsync().Await();
             else if (destination is ITemporaryTopic temporaryTopic)
-                temporaryTopic.Delete();
+                await temporaryTopic.DeleteAsync().Await();
             else
                 throw new NotSupportedException("AMQP can not delete a Queue or Topic destination.");
         }
@@ -273,6 +387,11 @@
             return Connection.MessageFactory.CreateMessage();
         }
 
+        public Task<IMessage> CreateMessageAsync()
+        {
+            return Task.FromResult(CreateMessage());
+        }
+
         public ITextMessage CreateTextMessage()
         {
             CheckClosed();
@@ -280,6 +399,11 @@
             return Connection.MessageFactory.CreateTextMessage();
         }
 
+        public Task<ITextMessage> CreateTextMessageAsync()
+        {
+            return Task.FromResult(CreateTextMessage());
+        }
+
         public ITextMessage CreateTextMessage(string text)
         {
             CheckClosed();
@@ -287,6 +411,11 @@
             return Connection.MessageFactory.CreateTextMessage(text);
         }
 
+        public Task<ITextMessage> CreateTextMessageAsync(string text)
+        {
+            return Task.FromResult(CreateTextMessage(text));
+        }
+
         public IMapMessage CreateMapMessage()
         {
             CheckClosed();
@@ -294,6 +423,11 @@
             return Connection.MessageFactory.CreateMapMessage();
         }
 
+        public Task<IMapMessage> CreateMapMessageAsync()
+        {
+            return Task.FromResult(CreateMapMessage());
+        }
+
         public IObjectMessage CreateObjectMessage(object body)
         {
             CheckClosed();
@@ -301,6 +435,11 @@
             return Connection.MessageFactory.CreateObjectMessage(body);
         }
 
+        public Task<IObjectMessage> CreateObjectMessageAsync(object body)
+        {
+            return Task.FromResult(CreateObjectMessage(body));
+        }
+
         public IBytesMessage CreateBytesMessage()
         {
             CheckClosed();
@@ -308,6 +447,11 @@
             return Connection.MessageFactory.CreateBytesMessage();
         }
 
+        public Task<IBytesMessage> CreateBytesMessageAsync()
+        {
+            return Task.FromResult(CreateBytesMessage());
+        }
+
         public IBytesMessage CreateBytesMessage(byte[] body)
         {
             CheckClosed();
@@ -315,21 +459,36 @@
             return Connection.MessageFactory.CreateBytesMessage(body);
         }
 
+        public Task<IBytesMessage> CreateBytesMessageAsync(byte[] body)
+        {
+            return Task.FromResult(CreateBytesMessage(body));
+        }
+
         public IStreamMessage CreateStreamMessage()
         {
             CheckClosed();
 
             return Connection.MessageFactory.CreateStreamMessage();
         }
+        
+        public Task<IStreamMessage> CreateStreamMessageAsync()
+        {
+            return Task.FromResult(CreateStreamMessage());
+        }
 
         public void Recover()
         {
+            RecoverAsync().GetAsyncResult();
+        }
+
+        public async Task RecoverAsync()
+        {
             CheckClosed();
 
             bool wasStarted = IsStarted;
             Stop();
             
-            Connection.Recover(SessionInfo.Id).ConfigureAwait(false).GetAwaiter().GetResult();
+            await Connection.Recover(SessionInfo.Id).Await();
 
             if (wasStarted) 
                 Start();
@@ -337,8 +496,13 @@
 
         public void Acknowledge()
         {
+            AcknowledgeAsync().GetAsyncResult();
+        }
+        
+        public async Task AcknowledgeAsync()
+        {
             if (acknowledgementMode == AcknowledgementMode.ClientAcknowledge) {
-                Acknowledge(AckType.ACCEPTED);
+                await AcknowledgeAsync(AckType.ACCEPTED).Await();
             }
         }
 
@@ -346,11 +510,23 @@
         {
             CheckClosed();
 
-            TransactionContext.Commit().ConfigureAwait(false).GetAwaiter().GetResult();
+            TransactionContext.Commit().GetAsyncResult();
+        }
+
+        public async Task CommitAsync()
+        {
+            CheckClosed();
+
+            await TransactionContext.Commit().Await();
         }
 
         public void Rollback()
         {
+            RollbackAsync().GetAsyncResult();
+        }
+
+        public async Task RollbackAsync()
+        {
             CheckClosed();
             
             // Stop processing any new messages that arrive
@@ -358,19 +534,19 @@
             {
                 foreach (NmsMessageConsumer consumer in consumers.Values)
                 {
-                    consumer.SuspendForRollback();
+                    await consumer.SuspendForRollbackAsync().Await();
                 }
             }
             finally
             {
-                TransactionContext.Rollback().ConfigureAwait(false).GetAwaiter().GetResult();    
+                await TransactionContext.Rollback().Await(); //.GetAsyncResult();    
             }
             
             // Currently some consumers won't get suspended and some won't restart
             // after a failed rollback.
             foreach (NmsMessageConsumer consumer in consumers.Values)
             {
-                consumer.ResumeAfterRollback();
+                await consumer.ResumeAfterRollbackAsync().Await();
             }
         }
 
@@ -430,24 +606,24 @@
             }
         }
 
-        public void Acknowledge(AckType ackType)
+        public Task AcknowledgeAsync(AckType ackType)
         {
-            Connection.Acknowledge(SessionInfo.Id, ackType).ConfigureAwait(false).GetAwaiter().GetResult();
+            return Connection.Acknowledge(SessionInfo.Id, ackType);
         }
 
-        public void Acknowledge(AckType ackType, InboundMessageDispatch envelope)
+        public Task AcknowledgeAsync(AckType ackType, InboundMessageDispatch envelope)
         {
-            TransactionContext.Acknowledge(envelope, ackType).ConfigureAwait(false).GetAwaiter().GetResult();
+            return TransactionContext.Acknowledge(envelope, ackType);
         }
 
-        public void AcknowledgeIndividual(AckType ackType, InboundMessageDispatch envelope)
+        public Task AcknowledgeIndividualAsync(AckType ackType, InboundMessageDispatch envelope)
         {
             if (Transacted)
             {
                 throw new IllegalStateException("Message acknowledge called inside a transacted Session");
             }
 
-            Connection.Acknowledge(envelope, ackType).ConfigureAwait(false).GetAwaiter().GetResult();
+            return Connection.Acknowledge(envelope, ackType); //.GetAsyncResult();
         }
 
         public void Send(NmsMessageProducer producer, IDestination destination, IMessage original,
@@ -456,7 +632,7 @@
         {
 
             SendAsync(producer, destination, original, deliveryMode, priority, timeToLive, disableMessageId,
-                disableMessageTimestamp, deliveryDelay).ConfigureAwait(false).GetAwaiter().GetResult();
+                disableMessageTimestamp, deliveryDelay).GetAsyncResult();
             
         }
 
@@ -617,6 +793,11 @@
 
         public void Shutdown(NMSException exception = null)
         {
+            ShutdownAsync(exception).GetAsyncResult();
+        }
+
+        public async Task ShutdownAsync(NMSException exception = null)
+        {
             if (closed.CompareAndSet(false, true))
             {
                 failureCause = exception;
@@ -630,7 +811,7 @@
                     foreach (NmsMessageProducer producer in producers.Values.ToArray()) 
                         producer.Shutdown(exception);
 
-                    TransactionContext.Shutdown().ConfigureAwait(false).GetAwaiter().GetResult();
+                    await TransactionContext.Shutdown().Await();
                 }
                 finally
                 {
@@ -638,7 +819,7 @@
                 }
             }
         }
-
+        
         public void Start()
         {
             if (started.CompareAndSet(false, true))
@@ -652,9 +833,9 @@
             }
         }
 
-        internal void CheckIsOnDeliveryThread()
+        internal void CheckIsOnDeliveryExecutionFlow()
         {
-            if (dispatcher != null && dispatcher.IsOnDeliveryThread())
+            if (dispatcher != null && dispatcher.IsOnDeliveryExecutionFlow())
             {
                 throw new IllegalStateException("Illegal invocation from MessageListener callback");
             }
@@ -662,16 +843,16 @@
 
         public async Task OnConnectionRecovery(IProvider provider)
         {
-            await provider.CreateResource(SessionInfo).ConfigureAwait(false);
+            await provider.CreateResource(SessionInfo).Await();
 
             foreach (NmsMessageConsumer consumer in consumers.Values)
             {
-                await consumer.OnConnectionRecovery(provider).ConfigureAwait(false);
+                await consumer.OnConnectionRecovery(provider).Await();
             }
 
             foreach (NmsMessageProducer producer in producers.Values)
             {
-                await producer.OnConnectionRecovery(provider).ConfigureAwait(false);
+                await producer.OnConnectionRecovery(provider).Await();
             }
         }
 
@@ -679,7 +860,7 @@
         {
             foreach (NmsMessageConsumer consumer in consumers.Values)
             {
-                await consumer.OnConnectionRecovered(provider).ConfigureAwait(false);
+                await consumer.OnConnectionRecovered(provider).Await();
             }
         }
 
diff --git a/src/NMS.AMQP/NmsTemporaryDestination.cs b/src/NMS.AMQP/NmsTemporaryDestination.cs
index a2876d4..1f1d092 100644
--- a/src/NMS.AMQP/NmsTemporaryDestination.cs
+++ b/src/NMS.AMQP/NmsTemporaryDestination.cs
@@ -15,8 +15,9 @@
  * limitations under the License.
  */
 
+using System.Threading.Tasks;
 using Apache.NMS.AMQP.Meta;
-using Apache.NMS.AMQP.Util;
+using Apache.NMS.AMQP.Util.Synchronization;
 
 namespace Apache.NMS.AMQP
 {
@@ -36,9 +37,14 @@
         
         public void Dispose()
         {
+            DeleteAsync().GetAsyncResult();
+        }
+
+        public async Task DeleteAsync()
+        {
             if (Connection != null)
             {
-                Connection.DeleteTemporaryDestination(this);
+                await Connection.DeleteTemporaryDestinationAsync(this).Await();
                 Connection = null;
             }
         }
diff --git a/src/NMS.AMQP/Provider/Amqp/AmqpConnection.cs b/src/NMS.AMQP/Provider/Amqp/AmqpConnection.cs
index 6b40430..0e30ad6 100644
--- a/src/NMS.AMQP/Provider/Amqp/AmqpConnection.cs
+++ b/src/NMS.AMQP/Provider/Amqp/AmqpConnection.cs
@@ -27,6 +27,7 @@
 using Apache.NMS.AMQP.Provider.Amqp.Message;
 using Apache.NMS.AMQP.Transport;
 using Apache.NMS.AMQP.Util;
+using Apache.NMS.AMQP.Util.Synchronization;
 
 namespace Apache.NMS.AMQP.Provider.Amqp
 {
@@ -73,11 +74,11 @@
         {
             Address address = UriUtil.ToAddress(remoteUri, Info.UserName, Info.Password);
             this.tsc = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
-            underlyingConnection = await transport.CreateAsync(address, new AmqpHandler(this)).ConfigureAwait(false);
+            underlyingConnection = await transport.CreateAsync(address, new AmqpHandler(this)).AwaitRunContinuationAsync();
             underlyingConnection.AddClosedCallback(OnClosed);
 
             // Wait for connection to be opened
-            await this.tsc.Task.ConfigureAwait(false);
+            await this.tsc.Task.Await();
 
             // Create a Session for this connection that is used for Temporary Destinations
             // and perhaps later on management and advisory monitoring.
@@ -85,7 +86,7 @@
             sessionInfo.AcknowledgementMode = AcknowledgementMode.AutoAcknowledge;
 
             connectionSession = new AmqpConnectionSession(this, sessionInfo);
-            await connectionSession.Start().ConfigureAwait(false);
+            await connectionSession.Start().Await();
         }
 
         private void OnClosed(IAmqpObject sender, Error error)
@@ -174,7 +175,7 @@
         public async Task CreateSession(NmsSessionInfo sessionInfo)
         {
             var amqpSession = new AmqpSession(this, sessionInfo);
-            await amqpSession.Start().ConfigureAwait(false);
+            await amqpSession.Start().Await();
             sessions.TryAdd(sessionInfo.Id, amqpSession);
         }
 
@@ -192,6 +193,20 @@
             }
         }
 
+        public async Task CloseAsync()
+        {
+            try
+            {
+                if (UnderlyingConnection != null) await UnderlyingConnection.CloseAsync().AwaitRunContinuationAsync();
+            }
+            catch (Exception ex)
+            {
+                // log network errors
+                NMSException nmse = ExceptionSupport.Wrap(ex, "Amqp Connection close failure for NMS Connection {0}", this.Info.Id);
+                Tracer.DebugFormat("Caught Exception while closing Amqp Connection {0}. Exception {1}", this.Info.Id, nmse);
+            }
+        }
+
         public AmqpSession GetSession(NmsSessionId sessionId)
         {
             if (sessions.TryGetValue(sessionId, out AmqpSession session))
@@ -209,7 +224,7 @@
         public async Task CreateTemporaryDestination(NmsTemporaryDestination destination)
         {
             AmqpTemporaryDestination amqpTemporaryDestination = new AmqpTemporaryDestination(connectionSession, destination);
-            await amqpTemporaryDestination.Attach();
+            await amqpTemporaryDestination.Attach().Await();
             temporaryDestinations.TryAdd(destination, amqpTemporaryDestination);
         }
 
diff --git a/src/NMS.AMQP/Provider/Amqp/AmqpConnectionSession.cs b/src/NMS.AMQP/Provider/Amqp/AmqpConnectionSession.cs
index c89133a..ba9399e 100644
--- a/src/NMS.AMQP/Provider/Amqp/AmqpConnectionSession.cs
+++ b/src/NMS.AMQP/Provider/Amqp/AmqpConnectionSession.cs
@@ -22,6 +22,7 @@
 using Amqp.Types;
 using Apache.NMS.AMQP.Meta;
 using Apache.NMS.AMQP.Util;
+using Apache.NMS.AMQP.Util.Synchronization;
 
 namespace Apache.NMS.AMQP.Provider.Amqp
 {
@@ -57,9 +58,9 @@
                 tcs.TrySetException(exception);
             });
 
-            await tcs.Task;
+            await tcs.Task.Await();
             
-            receiverLink.Close(TimeSpan.FromMilliseconds(Connection.Provider.CloseTimeout));
+            await receiverLink.CloseAsync(TimeSpan.FromMilliseconds(Connection.Provider.CloseTimeout)).AwaitRunContinuationAsync();
         }
 
         private Attach CreateAttach(string subscriptionName)
diff --git a/src/NMS.AMQP/Provider/Amqp/AmqpConsumer.cs b/src/NMS.AMQP/Provider/Amqp/AmqpConsumer.cs
index d4ceeac..fc3a5cf 100644
--- a/src/NMS.AMQP/Provider/Amqp/AmqpConsumer.cs
+++ b/src/NMS.AMQP/Provider/Amqp/AmqpConsumer.cs
@@ -27,6 +27,7 @@
 using Apache.NMS.AMQP.Provider.Amqp.Filters;
 using Apache.NMS.AMQP.Provider.Amqp.Message;
 using Apache.NMS.AMQP.Util;
+using Apache.NMS.AMQP.Util.Synchronization;
 
 namespace Apache.NMS.AMQP.Provider.Amqp
 {
@@ -391,15 +392,15 @@
             }
         }
 
-        public void Close()
+        public async Task CloseAsync()
         {
             if (info.IsDurable)
             {
-                receiverLink?.Detach();
+                if (receiverLink != null) await receiverLink.DetachAsync().AwaitRunContinuationAsync();
             }
             else
             {
-                receiverLink?.Close();
+                if (receiverLink != null) await receiverLink.CloseAsync().AwaitRunContinuationAsync();
             }
         }
 
diff --git a/src/NMS.AMQP/Provider/Amqp/AmqpProducer.cs b/src/NMS.AMQP/Provider/Amqp/AmqpProducer.cs
index d52a278..5da3e31 100644
--- a/src/NMS.AMQP/Provider/Amqp/AmqpProducer.cs
+++ b/src/NMS.AMQP/Provider/Amqp/AmqpProducer.cs
@@ -24,6 +24,7 @@
 using Apache.NMS.AMQP.Meta;
 using Apache.NMS.AMQP.Provider.Amqp.Message;
 using Apache.NMS.AMQP.Util;
+using Apache.NMS.AMQP.Util.Synchronization;
 
 namespace Apache.NMS.AMQP.Provider.Amqp
 {
@@ -55,7 +56,7 @@
             };
 
             string linkName = info.Id + ":" + target.Address;
-            var taskCompletionSource = new TaskCompletionSource<bool>();
+            var taskCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
             senderLink = new SenderLink(session.UnderlyingSession, linkName, frame, HandleOpened(taskCompletionSource));
 
             senderLink.AddClosedCallback((sender, error) =>
@@ -75,7 +76,7 @@
 
             return taskCompletionSource.Task;
         }
-        
+
         private OnAttached HandleOpened(TaskCompletionSource<bool> tsc) => (link, attach) =>
         {
             if (IsClosePending(attach))
@@ -141,7 +142,7 @@
                         SendSync(message, transactionalState);
                         return;
                     }
-                    await SendAsync(message, transactionalState).ConfigureAwait(false);
+                    await SendAsync(message, transactionalState).Await();
                 }
                 catch (AmqpException amqpEx)
                 {
@@ -178,7 +179,7 @@
             try
             {
                 senderLink.Send(message, deliveryState, _onOutcome, tcs);
-                await tcs.Task.ConfigureAwait(false);
+                await tcs.Task.Await();
             }
             finally
             {
@@ -210,12 +211,12 @@
             }
         }
 
-        public void Close()
+        public async Task CloseAsync()
         {
             try
             {
                 var closeTimeout = session.Connection.Provider.CloseTimeout;
-                senderLink.Close(TimeSpan.FromMilliseconds(closeTimeout));
+                await senderLink.CloseAsync(TimeSpan.FromMilliseconds(closeTimeout)).AwaitRunContinuationAsync();
             }
             catch (NMSException)
             {
diff --git a/src/NMS.AMQP/Provider/Amqp/AmqpProvider.cs b/src/NMS.AMQP/Provider/Amqp/AmqpProvider.cs
index c31bdeb..0720820 100644
--- a/src/NMS.AMQP/Provider/Amqp/AmqpProvider.cs
+++ b/src/NMS.AMQP/Provider/Amqp/AmqpProvider.cs
@@ -18,11 +18,10 @@
 using System;
 using System.Threading.Tasks;
 using Amqp;
-using Amqp.Framing;
 using Apache.NMS.AMQP.Message;
 using Apache.NMS.AMQP.Meta;
 using Apache.NMS.AMQP.Transport;
-using Apache.NMS.AMQP.Util;
+using Apache.NMS.AMQP.Util.Synchronization;
 
 namespace Apache.NMS.AMQP.Provider.Amqp
 {
@@ -135,6 +134,11 @@
             connection?.Close();
         }
 
+        public Task CloseAsync()
+        {
+            return connection?.CloseAsync();
+        }
+        
         public void SetProviderListener(IProviderListener providerListener)
         {
             Listener = providerListener;
@@ -166,49 +170,49 @@
             }
         }
 
-        public Task DestroyResource(INmsResource resourceInfo)
+        public async Task DestroyResource(INmsResource resourceInfo)
         {
             switch (resourceInfo)
             {
                 case NmsSessionInfo sessionInfo:
                 {
                     AmqpSession session = connection.GetSession(sessionInfo.Id);
-                    session.Close();
-                    return Task.CompletedTask;
+                    await session.CloseAsync().Await();
+                    return;
                 }
                 case NmsConsumerInfo consumerInfo:
                 {
                     AmqpSession session = connection.GetSession(consumerInfo.SessionId);
                     AmqpConsumer consumer = session.GetConsumer(consumerInfo.Id);
-                    consumer.Close();
+                    await consumer.CloseAsync().Await();;
                     session.RemoveConsumer(consumerInfo.Id);
-                    return Task.CompletedTask;
+                    return;
                 }
                 case NmsProducerInfo producerInfo:
                 {
                     AmqpSession session = connection.GetSession(producerInfo.SessionId);
                     AmqpProducer producer = session.GetProducer(producerInfo.Id);
-                    producer.Close();
+                    await producer.CloseAsync().Await();;
                     session.RemoveProducer(producerInfo.Id);
-                    return Task.CompletedTask;
+                    return;
                 }
                 case NmsTemporaryDestination temporaryDestination:
                 {
                     AmqpTemporaryDestination amqpTemporaryDestination = connection.GetTemporaryDestination(temporaryDestination);
                     if (amqpTemporaryDestination != null)
                     {
-                        amqpTemporaryDestination.Close();
+                        await amqpTemporaryDestination.CloseAsync().Await();;
                         connection.RemoveTemporaryDestination(temporaryDestination);
                     }
                     else
                         Tracer.Debug($"Could not find temporary destination {temporaryDestination} to delete.");
 
-                    return Task.CompletedTask;
+                    return;
                 }
                 default:
                     throw new ArgumentOutOfRangeException(nameof(resourceInfo), "Not supported resource type.");
             }
-        }
+          }
 
         public Task StartResource(INmsResource resourceInfo)
         {
@@ -270,7 +274,7 @@
         {
             AmqpSession session = connection.GetSession(envelope.ProducerInfo.SessionId);
             AmqpProducer producer = session.GetProducer(envelope.ProducerId);
-            await producer.Send(envelope).ConfigureAwait(false);
+            await producer.Send(envelope).Await();
             envelope.Message.IsReadOnly = false;
         }
 
diff --git a/src/NMS.AMQP/Provider/Amqp/AmqpSendTask.cs b/src/NMS.AMQP/Provider/Amqp/AmqpSendTask.cs
index d53c0fc..5676780 100644
--- a/src/NMS.AMQP/Provider/Amqp/AmqpSendTask.cs
+++ b/src/NMS.AMQP/Provider/Amqp/AmqpSendTask.cs
@@ -29,7 +29,8 @@
     {
         private readonly Timer timer;
         
-        public AmqpSendTask(SenderLink link, global::Amqp.Message message, DeliveryState deliveryState, long timeoutMillis)
+        public AmqpSendTask(SenderLink link, global::Amqp.Message message, DeliveryState deliveryState, long timeoutMillis) 
+            : base(TaskCreationOptions.RunContinuationsAsynchronously)
         {
             if (timeoutMillis != NmsConnectionInfo.INFINITE)
             {
diff --git a/src/NMS.AMQP/Provider/Amqp/AmqpSession.cs b/src/NMS.AMQP/Provider/Amqp/AmqpSession.cs
index d2c5c81..2be170c 100644
--- a/src/NMS.AMQP/Provider/Amqp/AmqpSession.cs
+++ b/src/NMS.AMQP/Provider/Amqp/AmqpSession.cs
@@ -25,6 +25,7 @@
 using Amqp.Framing;
 using Apache.NMS.AMQP.Meta;
 using Apache.NMS.AMQP.Util;
+using Apache.NMS.AMQP.Util.Synchronization;
 
 namespace Apache.NMS.AMQP.Provider.Amqp
 {
@@ -83,14 +84,14 @@
             return tcs.Task;
         }
 
-        public void Close()
+        public async Task CloseAsync()
         {
             long closeTimeout = Connection.Provider.CloseTimeout;
             TimeSpan timeout = TimeSpan.FromMilliseconds(closeTimeout);
-            UnderlyingSession.Close(timeout);
+            await UnderlyingSession.CloseAsync(timeout).AwaitRunContinuationAsync();
             Connection.RemoveSession(SessionInfo.Id);
         }
-
+        
         public Task BeginTransaction(NmsTransactionInfo transactionInfo)
         {
             if (!SessionInfo.IsTransacted)
@@ -115,14 +116,14 @@
         public async Task CreateConsumer(NmsConsumerInfo consumerInfo)
         {
             AmqpConsumer amqpConsumer = new AmqpConsumer(this, consumerInfo);
-            await amqpConsumer.Attach();
+            await amqpConsumer.Attach().Await();;
             consumers.TryAdd(consumerInfo.Id, amqpConsumer);
         }
 
         public async Task CreateProducer(NmsProducerInfo producerInfo)
         {
             var amqpProducer = new AmqpProducer(this, producerInfo);
-            await amqpProducer.Attach();
+            await amqpProducer.Attach().Await();;
             producers.TryAdd(producerInfo.Id, amqpProducer);
         }
 
diff --git a/src/NMS.AMQP/Provider/Amqp/AmqpTemporaryDestination.cs b/src/NMS.AMQP/Provider/Amqp/AmqpTemporaryDestination.cs
index 6a76632..263e117 100644
--- a/src/NMS.AMQP/Provider/Amqp/AmqpTemporaryDestination.cs
+++ b/src/NMS.AMQP/Provider/Amqp/AmqpTemporaryDestination.cs
@@ -21,6 +21,7 @@
 using Amqp.Framing;
 using Amqp.Types;
 using Apache.NMS.AMQP.Util;
+using Apache.NMS.AMQP.Util.Synchronization;
 
 namespace Apache.NMS.AMQP.Provider.Amqp
 {
@@ -94,12 +95,12 @@
 
             return result;
         }
-
-        public void Close()
+        
+        public async Task CloseAsync()
         {
             try
             {
-                senderLink.Close();
+                await senderLink.CloseAsync().AwaitRunContinuationAsync();
             }
             catch (NMSException)
             {
diff --git a/src/NMS.AMQP/Provider/Amqp/AmqpTransactionContext.cs b/src/NMS.AMQP/Provider/Amqp/AmqpTransactionContext.cs
index 0a77f4a..2e46699 100644
--- a/src/NMS.AMQP/Provider/Amqp/AmqpTransactionContext.cs
+++ b/src/NMS.AMQP/Provider/Amqp/AmqpTransactionContext.cs
@@ -21,7 +21,7 @@
 using Amqp.Framing;
 using Amqp.Transactions;
 using Apache.NMS.AMQP.Meta;
-using Apache.NMS.AMQP.Util;
+using Apache.NMS.AMQP.Util.Synchronization;
 
 namespace Apache.NMS.AMQP.Provider.Amqp
 {
@@ -66,14 +66,14 @@
             Tracer.Debug($"TX Context{this} rolling back current TX[{this.current}]");
 
             this.current = null;
-            await this.coordinator.DischargeAsync(this.txnId, true).ConfigureAwait(false);
+            await this.coordinator.DischargeAsync(this.txnId, true).Await();
             
 
             PostRollback();
 
             if (nextTransactionInfo != null)
             {
-                await Begin(nextTransactionInfo).ConfigureAwait(false);
+                await Begin(nextTransactionInfo).Await();
             }
         }
 
@@ -101,11 +101,11 @@
             Tracer.Debug($"TX Context{this} committing back current TX[{this.current}]");
 
             this.current = null;
-            await this.coordinator.DischargeAsync(this.txnId, false).ConfigureAwait(false);
+            await this.coordinator.DischargeAsync(this.txnId, false).Await();
 
             PostCommit();
 
-            await Begin(nextTransactionInfo).ConfigureAwait(false);
+            await Begin(nextTransactionInfo).Await();
         }
 
         private void PostCommit()
@@ -123,7 +123,7 @@
                 this.coordinator = new AmqpTransactionCoordinator(this.session);
             }
 
-            this.txnId = await this.coordinator.DeclareAsync().ConfigureAwait(false);
+            this.txnId = await this.coordinator.DeclareAsync().Await();
             this.current = transactionInfo.Id;
             transactionInfo.ProviderTxId = this.txnId;
             this.cachedTransactedState = new TransactionalState { TxnId = this.txnId };
diff --git a/src/NMS.AMQP/Provider/Amqp/AmqpTransactionCoordinator.cs b/src/NMS.AMQP/Provider/Amqp/AmqpTransactionCoordinator.cs
index 3fab595..0281b5c 100644
--- a/src/NMS.AMQP/Provider/Amqp/AmqpTransactionCoordinator.cs
+++ b/src/NMS.AMQP/Provider/Amqp/AmqpTransactionCoordinator.cs
@@ -21,6 +21,7 @@
 using Amqp.Framing;
 using Amqp.Transactions;
 using Apache.NMS.AMQP.Util;
+using Apache.NMS.AMQP.Util.Synchronization;
 
 namespace Apache.NMS.AMQP.Provider.Amqp
 {
@@ -51,7 +52,7 @@
 
         public async Task<byte[]> DeclareAsync()
         {
-            var outcome = await this.SendAsync(DeclareMessage, null, this.session.Connection.Provider.RequestTimeout).ConfigureAwait(false);
+            var outcome = await this.SendAsync(DeclareMessage, null, this.session.Connection.Provider.RequestTimeout).Await();
             if (outcome.Descriptor.Code == MessageSupport.DECLARED_INSTANCE.Descriptor.Code)
             {
                 return ((Declared) outcome).TxnId;
@@ -71,7 +72,7 @@
         public async Task DischargeAsync(byte[] txnId, bool fail)
         {
             var message = new global::Amqp.Message(new Discharge { TxnId = txnId, Fail = fail });
-            var outcome = await this.SendAsync(message, null, this.session.Connection.Provider.RequestTimeout).ConfigureAwait(false);
+            var outcome = await this.SendAsync(message, null, this.session.Connection.Provider.RequestTimeout).Await();
 
             if (outcome.Descriptor.Code == MessageSupport.ACCEPTED_INSTANCE.Descriptor.Code)
             {
diff --git a/src/NMS.AMQP/Provider/Failover/FailoverProvider.cs b/src/NMS.AMQP/Provider/Failover/FailoverProvider.cs
index c567687..80d4b89 100644
--- a/src/NMS.AMQP/Provider/Failover/FailoverProvider.cs
+++ b/src/NMS.AMQP/Provider/Failover/FailoverProvider.cs
@@ -22,6 +22,7 @@
 using Apache.NMS.AMQP.Message;
 using Apache.NMS.AMQP.Meta;
 using Apache.NMS.AMQP.Util;
+using Apache.NMS.AMQP.Util.Synchronization;
 using Apache.NMS.Util;
 
 namespace Apache.NMS.AMQP.Provider.Failover
@@ -128,8 +129,8 @@
                             {
                                 Tracer.Debug($"Connection attempt:[{reconnectAttempts}] to: {target.Scheme}://{target.Host}:{target.Port} in-progress");
                                 provider = ProviderFactory.Create(target);
-                                await provider.Connect(connectionInfo).ConfigureAwait(false);
-                                await InitializeNewConnection(provider).ConfigureAwait(false);
+                                await provider.Connect(connectionInfo).Await();
+                                await InitializeNewConnection(provider).Await();
                                 return;
                             }
                             catch (Exception e)
@@ -173,7 +174,7 @@
                         }
                         else
                         {
-                            await reconnectControl.ScheduleReconnect(Reconnect).ConfigureAwait(false);
+                            await reconnectControl.ScheduleReconnect(Reconnect).Await();
                         }
                     }
                 }
@@ -220,21 +221,21 @@
                 Tracer.Debug($"Signalling connection recovery: {provider}");
 
                 // Allow listener to recover its resources
-                await listener.OnConnectionRecovery(provider).ConfigureAwait(false);
+                await listener.OnConnectionRecovery(provider).Await();
 
                 // Restart consumers, send pull commands, etc.
-                await listener.OnConnectionRecovered(provider).ConfigureAwait(false);
+                await listener.OnConnectionRecovered(provider).Await();
 
                 // Let the client know that connection has restored.
                 listener.OnConnectionRestored(connectedUri);
 
                 // If we try to run pending requests right after the connection is reestablished 
                 // it will result in timeout on the first send request
-                await Task.Delay(50).ConfigureAwait(false);
+                await Task.Delay(50).Await();
 
                 foreach (FailoverRequest request in GetPendingRequests())
                 {
-                    await request.Run().ConfigureAwait(false);
+                    await request.Run().Await();
                 }
 
                 reconnectControl.ConnectionEstablished();
@@ -266,6 +267,21 @@
             }
         }
 
+        public async Task CloseAsync()
+        {
+            if (closed.CompareAndSet(false, true))
+            {
+                try
+                {
+                    if (provider != null) await provider.CloseAsync().Await();
+                }
+                catch (Exception e)
+                {
+                    Tracer.Warn("Error caught while closing Provider: " + e.Message);
+                }
+            }
+        }
+
         public void SetProviderListener(IProviderListener providerListener)
         {
             CheckClosed();
@@ -599,20 +615,20 @@
                     if (ReconnectAttempts == 0)
                     {
                         Tracer.Debug("Initial connect attempt will be performed immediately");
-                        await action();
+                        await action().Await();;
                     }
                     else if (ReconnectAttempts == 1 && failoverProvider.InitialReconnectDelay > 0)
                     {
                         Tracer.Debug($"Delayed initial reconnect attempt will be in {failoverProvider.InitialReconnectDelay} milliseconds");
-                        await Task.Delay(TimeSpan.FromMilliseconds(failoverProvider.InitialReconnectDelay));
-                        await action();
+                        await Task.Delay(TimeSpan.FromMilliseconds(failoverProvider.InitialReconnectDelay)).Await();;
+                        await action().Await();;
                     }
                     else
                     {
                         double delay = NextReconnectDelay();
                         Tracer.Debug($"Next reconnect attempt will be in {delay} milliseconds");
-                        await Task.Delay(TimeSpan.FromMilliseconds(delay));
-                        await action();
+                        await Task.Delay(TimeSpan.FromMilliseconds(delay)).Await();;
+                        await action().Await();;
                     }
                 }
                 else if (ReconnectAttempts == 0)
@@ -620,21 +636,21 @@
                     if (failoverProvider.InitialReconnectDelay > 0)
                     {
                         Tracer.Debug($"Delayed initial reconnect attempt will be in {failoverProvider.InitialReconnectDelay} milliseconds");
-                        await Task.Delay(TimeSpan.FromMilliseconds(failoverProvider.InitialReconnectDelay));
-                        await action();
+                        await Task.Delay(TimeSpan.FromMilliseconds(failoverProvider.InitialReconnectDelay)).Await();
+                        await action().Await();;
                     }
                     else
                     {
                         Tracer.Debug("Initial Reconnect attempt will be performed immediately");
-                        await action();
+                        await action().Await();;
                     }
                 }
                 else
                 {
                     double delay = NextReconnectDelay();
                     Tracer.Debug($"Next reconnect attempt will be in {delay} milliseconds");
-                    await Task.Delay(TimeSpan.FromMilliseconds(delay));
-                    await action();
+                    await Task.Delay(TimeSpan.FromMilliseconds(delay)).Await();
+                    await action().Await();;
                 }
             }
 
diff --git a/src/NMS.AMQP/Provider/Failover/FailoverRequest.cs b/src/NMS.AMQP/Provider/Failover/FailoverRequest.cs
index 6c2ef6f..84f5822 100644
--- a/src/NMS.AMQP/Provider/Failover/FailoverRequest.cs
+++ b/src/NMS.AMQP/Provider/Failover/FailoverRequest.cs
@@ -20,6 +20,7 @@
 using System.Threading;
 using System.Threading.Tasks;
 using Apache.NMS.AMQP.Meta;
+using Apache.NMS.AMQP.Util.Synchronization;
 
 namespace Apache.NMS.AMQP.Provider.Failover
 {
@@ -61,7 +62,7 @@
             {
                 try
                 {
-                    await this.DoTask(activeProvider).ConfigureAwait(false);
+                    await this.DoTask(activeProvider).Await();
                     this.taskCompletionSource.TrySetResult(true);
                     this.failoverProvider.RemoveFailoverRequest(this);
                     this.cancellationTokenSource?.Dispose();
diff --git a/src/NMS.AMQP/Provider/IProvider.cs b/src/NMS.AMQP/Provider/IProvider.cs
index afe3402..b92b829 100644
--- a/src/NMS.AMQP/Provider/IProvider.cs
+++ b/src/NMS.AMQP/Provider/IProvider.cs
@@ -30,6 +30,7 @@
         void Start();
         Task Connect(NmsConnectionInfo connectionInfo);
         void Close();
+        Task CloseAsync();
         void SetProviderListener(IProviderListener providerListener);
         Task CreateResource(INmsResource resourceInfo);
         Task DestroyResource(INmsResource resourceInfo);
diff --git a/src/NMS.AMQP/SessionDispatcher.cs b/src/NMS.AMQP/SessionDispatcher.cs
index fed3327..70d26e4 100644
--- a/src/NMS.AMQP/SessionDispatcher.cs
+++ b/src/NMS.AMQP/SessionDispatcher.cs
@@ -18,13 +18,14 @@
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks.Dataflow;
+using Apache.NMS.AMQP.Util.Synchronization;
 
 namespace Apache.NMS.AMQP
 {
     internal class SessionDispatcher
     {
         private readonly ActionBlock<NmsMessageConsumer.MessageDeliveryTask> actionBlock;
-        private int dispatchThreadId;
+        private readonly AsyncLocal<bool> isOnDispatcherFlow = new AsyncLocal<bool>();
         private readonly CancellationTokenSource cts;
 
         public SessionDispatcher()
@@ -40,18 +41,18 @@
 
         public void Post(NmsMessageConsumer.MessageDeliveryTask task) => actionBlock.Post(task);
 
-        public bool IsOnDeliveryThread() => dispatchThreadId == Thread.CurrentThread.ManagedThreadId;
+        public bool IsOnDeliveryExecutionFlow() => isOnDispatcherFlow.Value;
 
-        private void HandleTask(NmsMessageConsumer.MessageDeliveryTask messageDeliveryTask)
+        private async Task HandleTask(NmsMessageConsumer.MessageDeliveryTask messageDeliveryTask)
         {
             try
             {
-                dispatchThreadId = Thread.CurrentThread.ManagedThreadId;
-                messageDeliveryTask.DeliverNextPending();
+                isOnDispatcherFlow.Value = true;
+                await messageDeliveryTask.DeliverNextPending().Await();
             }
             finally
             {
-                dispatchThreadId = -1;
+                isOnDispatcherFlow.Value = false;
             }
         }
 
diff --git a/src/NMS.AMQP/Util/PriorityMessageQueue.cs b/src/NMS.AMQP/Util/PriorityMessageQueue.cs
index 3b11e1d..9d49618 100644
--- a/src/NMS.AMQP/Util/PriorityMessageQueue.cs
+++ b/src/NMS.AMQP/Util/PriorityMessageQueue.cs
@@ -17,8 +17,9 @@
 
 using System;
 using System.Collections.Generic;
-using System.Threading;
+using System.Threading.Tasks;
 using Apache.NMS.AMQP.Message;
+using Apache.NMS.AMQP.Util.Synchronization;
 
 namespace Apache.NMS.AMQP.Util
 {
@@ -26,8 +27,8 @@
     {
         private readonly LinkedList<InboundMessageDispatch>[] lists;
 
-        private readonly object syncRoot = new object();
-
+        private readonly NmsSynchronizationMonitor syncRoot = new NmsSynchronizationMonitor();
+        
         private bool disposed;
         private int count;
 
@@ -46,8 +47,10 @@
         {
             get
             {
-                lock (syncRoot)
+                using(syncRoot.Lock())
+                {
                     return count;
+                }
             }
         }
 
@@ -78,22 +81,26 @@
 
         public void Enqueue(InboundMessageDispatch envelope)
         {
-            lock (syncRoot)
+            using(syncRoot.Lock())
             {
                 GetList(envelope).AddLast(envelope);
                 this.count++;
-                Monitor.Pulse(syncRoot);
+
+                syncRoot.Pulse();
             }
+            
+            
         }
         
         public void EnqueueFirst(InboundMessageDispatch envelope)
         {
-            lock (syncRoot)
+            using(syncRoot.Lock())
             {
                 lists[(int) MsgPriority.Highest].AddFirst(envelope);
                 count++;
-                Monitor.Pulse(syncRoot);
-            }            
+                syncRoot.Pulse();
+            }
+            
         }
 
         private LinkedList<InboundMessageDispatch> GetList(InboundMessageDispatch envelope)
@@ -102,20 +109,20 @@
             return lists[(int) priority];
         }
 
-        public InboundMessageDispatch Dequeue(int timeout)
+        public async Task<InboundMessageDispatch> DequeueAsync(int timeout)
         {
-            lock (syncRoot)
+            using(await syncRoot.LockAsync())
             {
                 while (timeout != 0 && IsEmpty && !disposed)
                 {
                     if (timeout == -1)
                     {
-                        Monitor.Wait(syncRoot);
+                        await syncRoot.WaitAsync();
                     }
                     else
                     {
                         long start = DateTime.UtcNow.Ticks / 10_000L;
-                        Monitor.Wait(syncRoot, timeout);
+                        await syncRoot.WaitAsync(timeout);
                         timeout = Math.Max(timeout + (int) (start - DateTime.UtcNow.Ticks / 10_000L), 0);
                     }
                 }
@@ -127,27 +134,61 @@
 
                 return RemoveFirst();
             }
+
+        }
+
+
+        public InboundMessageDispatch Dequeue(int timeout)
+        {
+            using(syncRoot.Lock())
+            {
+                while (timeout != 0 && IsEmpty && !disposed)
+                {
+                    if (timeout == -1)
+                    {
+                        syncRoot.Wait();
+                    }
+                    else
+                    {
+                        long start = DateTime.UtcNow.Ticks / 10_000L;
+                        syncRoot.Wait(timeout);
+                        timeout = Math.Max(timeout + (int) (start - DateTime.UtcNow.Ticks / 10_000L), 0);
+                    }
+                }
+
+                if (IsEmpty || disposed)
+                {
+                    return null;
+                }
+
+                return RemoveFirst();
+            }
+            
         }
         
         public void Clear()
         {
-            lock (syncRoot)
-            {                
+            using(syncRoot.Lock())
+            {
                 for (int i = (int) MsgPriority.Highest; i >= 0; i--)
                 {
                     lists[i].Clear();
                 }
+
                 count = 0;
             }
+            
         }
 
         public void Dispose()
         {
-            lock (syncRoot)
+            
+            using(syncRoot.Lock())
             {
                 disposed = true;
-                Monitor.PulseAll(syncRoot);
+                syncRoot.PulseAll();
             }
+            
         }
     }
 }
\ No newline at end of file
diff --git a/src/NMS.AMQP/Util/SymbolUtil.cs b/src/NMS.AMQP/Util/SymbolUtil.cs
index e3fe2c3..5b23b57 100644
--- a/src/NMS.AMQP/Util/SymbolUtil.cs
+++ b/src/NMS.AMQP/Util/SymbolUtil.cs
@@ -14,13 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+
 using Amqp.Types;
-using Apache.NMS;
 
 namespace Apache.NMS.AMQP.Util
 {
diff --git a/src/NMS.AMQP/Util/Synchronization/NmsSynchronizationMonitor.cs b/src/NMS.AMQP/Util/Synchronization/NmsSynchronizationMonitor.cs
new file mode 100644
index 0000000..98a13f9
--- /dev/null
+++ b/src/NMS.AMQP/Util/Synchronization/NmsSynchronizationMonitor.cs
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.NMS.AMQP.Util.Synchronization
+{
+    /// <summary>
+    /// Goal of this is to replace lock(syncRoot) for sync and async methods, and also have Wait and Pulse(All) capabilities
+    /// Relies on AsyncLocal construct, and should be valid along the flow of executioncontext
+    /// </summary>
+    public class NmsSynchronizationMonitor
+    {
+        // Main locking mechanism
+        private readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
+
+        // Lists of executions sleeping in Wait
+        private readonly List<SemaphoreSlim> waitingLocks = new List<SemaphoreSlim>();
+
+        // SyncRoot used in locking related to Wait/Pulse
+        private readonly object waitSyncRoot = new object();
+
+        // Holds RALock in current flow of execution, should be like ThreadStatic but for async flow
+        private readonly AsyncLocal<NmsLock> asyncLocal;
+
+        public NmsSynchronizationMonitor()
+        {
+            asyncLocal = new AsyncLocal<NmsLock>();
+        }
+
+        /// <summary>
+        /// Synchronous Wait operation
+        /// </summary>
+        /// <param name="timeout"></param>
+        public void Wait(int? timeout = null)
+        {
+            var raLock = GetCurrentLock();
+
+            if (raLock == null)
+            {
+                throw new IllegalStateException("Wait called without acquiring Lock first");
+            }
+
+            // In one synchronized context we will Release monitor and sign ourself on list of sleeping locks
+            SemaphoreSlim waitSemaphore = new SemaphoreSlim(0, 1);
+            lock (waitSyncRoot)
+            {
+                ReleaseMonitor();
+                waitingLocks.Add(waitSemaphore);
+                // raLock.WaitSemaphore = new SemaphoreSlim(0, 1);
+            }
+
+            try
+            {
+                // Now wait, if our lock was pulsed just before, we will not really sleep, but instead continue ...
+                waitSemaphore.Wait(timeout ?? -1);
+
+                lock (waitSyncRoot)
+                {
+                    waitingLocks.Remove(waitSemaphore);
+                    waitSemaphore.Dispose();
+                }
+            }
+            finally
+            {
+                // Enter again, but we need to use the same raLock as before
+                EnterMonitor();
+            }
+        }
+
+
+        public async Task WaitAsync(int? timeout = null)
+        {
+            var raLock = GetCurrentLock();
+
+            if (raLock == null)
+            {
+                throw new IllegalStateException("Wait called without acquiring Lock first");
+            }
+
+            SemaphoreSlim waitSemaphore = new SemaphoreSlim(0, 1);
+
+            lock (waitSyncRoot)
+            {
+                ReleaseMonitor();
+                waitingLocks.Add(waitSemaphore);
+            }
+
+            try
+            {
+                // Here between lock and waiting is a problematic thing, two pulses can release the same thing
+                await waitSemaphore.WaitAsync(timeout ?? -1).Await();
+
+                lock (waitSyncRoot)
+                {
+                    waitingLocks.Remove(waitSemaphore);
+                    waitSemaphore.Dispose();
+                    waitSemaphore.Dispose();
+                }
+            }
+            finally
+            {
+                // Enter again, but we need to use the same raLock as before, and also asyncLocal a
+                await EnterMonitorAsync().Await();
+            }
+        }
+
+        public void Pulse()
+        {
+            lock (waitSyncRoot)
+            {
+                var firstWaiting = waitingLocks.FirstOrDefault();
+                if (firstWaiting != null)
+                {
+                    firstWaiting.Release();
+                    waitingLocks.Remove(firstWaiting);
+                }
+            }
+        }
+
+        public void PulseAll()
+        {
+            lock (waitSyncRoot)
+            {
+                waitingLocks.ForEach(a => { a.Release(); });
+                waitingLocks.Clear();
+            }
+        }
+
+
+        /// <summary>
+        /// Allows to create a sub context where asyncLocal will be removed and thus not passed for example to something that could carry it and thus has wrong locks acquired
+        /// </summary>
+        /// <returns></returns>
+        public IDisposable Exclude()
+        {
+            return new ExcludeLock(this);
+        }
+
+
+        public IDisposable Lock()
+        {
+            NmsLock nmsLock = GetOrCreateCurrentLock();
+            nmsLock.Enter();
+            return nmsLock;
+        }
+
+        public Task<IDisposable>
+            LockAsync() // This should not be async method, cause setting asyncLocal inside GetOrCreateCurrentLock may be only limited to this method in such case
+        {
+            NmsLock nmsLock = GetOrCreateCurrentLock();
+            return nmsLock.EnterAsync();
+        }
+
+
+        private NmsLock GetOrCreateCurrentLock()
+        {
+            if (asyncLocal.Value == null)
+            {
+                asyncLocal.Value = new NmsLock(this);
+            }
+
+            return asyncLocal.Value;
+        }
+
+        private NmsLock GetCurrentLock()
+        {
+            var context = asyncLocal.Value;
+            return context;
+        }
+
+        private void SetCurrentLock(NmsLock nmsLock)
+        {
+            asyncLocal.Value = nmsLock;
+        }
+
+        private void EnterMonitor()
+        {
+            semaphoreSlim.Wait();
+        }
+
+        private Task EnterMonitorAsync()
+        {
+            return semaphoreSlim.WaitAsync();
+        }
+
+        private void ReleaseMonitor()
+        {
+            semaphoreSlim.Release();
+        }
+
+        private class NmsLock : IDisposable
+        {
+            private int NestCounter { get; set; }
+
+            public Guid Id = Guid.NewGuid();
+
+            private readonly NmsSynchronizationMonitor parent;
+
+            public NmsLock(NmsSynchronizationMonitor parent)
+            {
+                this.parent = parent;
+            }
+
+            public void Enter()
+            {
+                if (NestCounter == 0)
+                {
+                    parent.EnterMonitor();
+                }
+
+                NestCounter++;
+            }
+
+            public async Task<IDisposable> EnterAsync()
+            {
+                if (NestCounter == 0)
+                {
+                    await parent.EnterMonitorAsync();
+                }
+
+                NestCounter++;
+                return this;
+            }
+
+            private void Leave()
+            {
+                NestCounter--;
+                if (NestCounter <= 0)
+                {
+                    parent.ReleaseMonitor();
+                    parent.SetCurrentLock(null);
+                }
+            }
+
+            public void Dispose()
+            {
+                Leave();
+            }
+        }
+        
+        private class ExcludeLock : IDisposable
+        {
+            private readonly NmsSynchronizationMonitor parent;
+
+            private readonly NmsLock currentLock;
+
+            public ExcludeLock(NmsSynchronizationMonitor parent)
+            {
+                this.parent = parent;
+
+                currentLock = parent.GetCurrentLock();
+                parent.SetCurrentLock(null);
+            }
+
+            public void Dispose()
+            {
+                parent.SetCurrentLock(this.currentLock);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/NMS.AMQP/Util/Synchronization/TaskExtensions.cs b/src/NMS.AMQP/Util/Synchronization/TaskExtensions.cs
new file mode 100644
index 0000000..e5e4072
--- /dev/null
+++ b/src/NMS.AMQP/Util/Synchronization/TaskExtensions.cs
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+
+namespace Apache.NMS.AMQP.Util.Synchronization
+{
+    public static class TaskExtensions
+    {
+        
+        public static T GetAsyncResult<T>(this Task<T> task)
+        {
+            return task.Await().GetAwaiter().GetResult();
+        }
+        
+        public static void GetAsyncResult(this Task task)
+        {
+            task.Await().GetAwaiter().GetResult();
+        }
+
+        public static async Task AwaitRunContinuationAsync(this Task task)
+        {
+            await task.Await();
+            if (TaskSynchronizationSettings.TryToRunCertainContunuationsAsynchronously)
+            {
+                await Task.Yield();
+            }
+        }
+        
+        public static async Task<T> AwaitRunContinuationAsync<T>(this Task<T> task)
+        {
+            T t = await task.Await();
+            if (TaskSynchronizationSettings.TryToRunCertainContunuationsAsynchronously)
+            {
+                await Task.Yield();
+            }
+            return t;
+        }
+        
+        public static ConfiguredTaskAwaitable Await(this Task task)
+        {
+            return task.ConfigureAwait(TaskSynchronizationSettings.ContinueOnCapturedContext);
+        }
+        
+        public static ConfiguredTaskAwaitable<T> Await<T>(this Task<T> task)
+        {
+            return task.ConfigureAwait(TaskSynchronizationSettings.ContinueOnCapturedContext);
+        }
+        
+    }
+}
\ No newline at end of file
diff --git a/src/NMS.AMQP/Util/Synchronization/TaskSynchronizationSettings.cs b/src/NMS.AMQP/Util/Synchronization/TaskSynchronizationSettings.cs
new file mode 100644
index 0000000..fa2e3b4
--- /dev/null
+++ b/src/NMS.AMQP/Util/Synchronization/TaskSynchronizationSettings.cs
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.NMS.AMQP.Util.Synchronization
+{
+    
+    public static class TaskSynchronizationSettings
+    {
+        /// <summary>
+        /// General settings for awaits, whether to force continuation on captured context
+        /// </summary>
+        public static bool ContinueOnCapturedContext { get; set; } = false;
+
+        /// <summary>
+        /// In AMQP library there is a async pump, that is calling methods, and in turn continuation may be inlined, if this happens
+        /// and continuation is waiting synchronously on something, then it stops processing in that pump, and it may lead to deadlocks
+        /// if continuation is waiting on something (some message) that async pump was supposed to deliver-but now async pump stopped processing
+        /// It seems it was also discussed here https://github.com/Azure/amqpnetlite/issues/237
+        /// So by default there is a try to force continuation from different thread (by using) yield, on those tasks that it seems that their
+        /// continuation may be from async pump
+        /// </summary>
+        public static bool TryToRunCertainContunuationsAsynchronously { get; set; } = true;
+    }
+}
\ No newline at end of file
diff --git a/src/NMS.AMQP/Util/UriUtil.cs b/src/NMS.AMQP/Util/UriUtil.cs
index acfbef8..ad06886 100644
--- a/src/NMS.AMQP/Util/UriUtil.cs
+++ b/src/NMS.AMQP/Util/UriUtil.cs
@@ -15,12 +15,7 @@
  * limitations under the License.
  */
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 using Amqp;
-using Apache.NMS;
 
 namespace Apache.NMS.AMQP.Util
 {
diff --git a/test/Apache-NMS-AMQP-Interop-Test/Transactions/NmsTransactedConsumerTest.cs b/test/Apache-NMS-AMQP-Interop-Test/Transactions/NmsTransactedConsumerTest.cs
index 37dbd29..55c4eee 100644
--- a/test/Apache-NMS-AMQP-Interop-Test/Transactions/NmsTransactedConsumerTest.cs
+++ b/test/Apache-NMS-AMQP-Interop-Test/Transactions/NmsTransactedConsumerTest.cs
@@ -46,6 +46,8 @@
         [Test, Timeout(60_000)]
         public void TestConsumedInTxAreAcked()
         {
+            PurgeQueue(TimeSpan.FromSeconds(2));
+            
             Connection = CreateAmqpConnection();
             Connection.Start();
 
diff --git a/test/Apache-NMS-AMQP-Test/Integration/AmqpAcknowledgmentsIntegrationTest.cs b/test/Apache-NMS-AMQP-Test/Integration/AmqpAcknowledgmentsIntegrationTest.cs
index ba09d9c..2609400 100644
--- a/test/Apache-NMS-AMQP-Test/Integration/AmqpAcknowledgmentsIntegrationTest.cs
+++ b/test/Apache-NMS-AMQP-Test/Integration/AmqpAcknowledgmentsIntegrationTest.cs
@@ -18,6 +18,7 @@
 using System;
 using System.Collections.Generic;
 using System.Threading;
+using System.Threading.Tasks;
 using Amqp.Framing;
 using Apache.NMS;
 using Apache.NMS.AMQP.Util;
diff --git a/test/Apache-NMS-AMQP-Test/Integration/Async/AmqpAcknowledgmentsIntegrationTest.cs b/test/Apache-NMS-AMQP-Test/Integration/Async/AmqpAcknowledgmentsIntegrationTest.cs
new file mode 100644
index 0000000..b419ad4
--- /dev/null
+++ b/test/Apache-NMS-AMQP-Test/Integration/Async/AmqpAcknowledgmentsIntegrationTest.cs
@@ -0,0 +1,344 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Amqp.Framing;
+using Apache.NMS;
+using Apache.NMS.AMQP.Util;
+using NMS.AMQP.Test.TestAmqp;
+using NUnit.Framework;
+
+namespace NMS.AMQP.Test.Integration.Async
+{
+    [TestFixture]
+    public class AmqpAcknowledgmentsIntegrationTestAsync : IntegrationTestFixture
+    {
+        [Test, Timeout(20_000)]
+        public async Task TestAcknowledgeFailsAfterSessionIsClosed()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.ClientAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithNullContent(), count: 1);
+                testPeer.ExpectEnd();
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+
+                IMessage receivedMessage = await consumer.ReceiveAsync(TimeSpan.FromSeconds(6));
+                Assert.NotNull(receivedMessage, "Message was not received");
+                
+                await session.CloseAsync();
+
+                Assert.CatchAsync<NMSException>(async () => await receivedMessage.AcknowledgeAsync(), "Should not be able to acknowledge the message after session closed");
+                
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(3000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestClientAcknowledgeMessages()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                int msgCount = 3;
+                
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.ClientAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithNullContent(), count: msgCount);
+                
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+                
+                IMessage lastReceivedMessage = null;
+                for (int i = 0; i < msgCount; i++)
+                {
+                    lastReceivedMessage = await consumer.ReceiveAsync();
+                    Assert.NotNull(lastReceivedMessage, "Message " + i + " was not received");
+                }
+                
+                for (int i = 0; i < msgCount; i++)
+                {
+                    testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+                }
+                
+                await lastReceivedMessage.AcknowledgeAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(2000);
+                
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestClientAcknowledgeMessagesAsync()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                int msgCount = 3;
+
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.ClientAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithNullContent(), count: msgCount);
+                
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+
+                CountdownEvent latch = new CountdownEvent(3);
+
+                IMessage lastReceivedMessage = null;
+                consumer.Listener += message =>
+                {
+                    lastReceivedMessage = message;
+                    latch.Signal();
+                };
+                
+                Assert.True(latch.Wait(2000));
+                
+                for (int i = 0; i < msgCount; i++)
+                {
+                    testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+                }
+                
+                await lastReceivedMessage.AcknowledgeAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(2000);
+                
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestAcknowledgeIndividualMessages()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                int msgCount = 6;
+
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.IndividualAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(
+                    message: CreateMessageWithNullContent(),
+                    count: msgCount,
+                    drain: false,
+                    nextIncomingId: 1,
+                    addMessageNumberProperty: true,
+                    sendDrainFlowResponse: false,
+                    sendSettled: false,
+                    creditMatcher: credit => Assert.Greater(credit, msgCount));
+                
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+                
+                var messages = new List<IMessage>();
+                for (int i = 0; i < msgCount; i++)
+                {
+                    IMessage message = await consumer.ReceiveAsync(TimeSpan.FromMilliseconds(3000));
+                    Assert.NotNull(message, "Message " + i + " was not received");
+                    messages.Add(message);
+                    
+                    Assert.AreEqual(i, message.Properties.GetInt(TestAmqpPeer.MESSAGE_NUMBER), "unexpected message number property");
+                }
+                
+                Action<DeliveryState> dispositionMatcher = state => { Assert.AreEqual(state.Descriptor.Code, MessageSupport.ACCEPTED_INSTANCE.Descriptor.Code); };
+                
+                // Acknowledge the messages in a random order and verify the individual dispositions have expected delivery state.
+                Random random = new Random();
+                for (int i = 0; i < msgCount; i++)
+                {
+                    var message = messages[random.Next(msgCount - i)];
+                    messages.Remove(message);
+
+                    uint deliveryNumber = (uint) message.Properties.GetInt(TestAmqpPeer.MESSAGE_NUMBER) + 1;
+
+                    testPeer.ExpectDisposition(settled: true, stateMatcher: dispositionMatcher, firstDeliveryId: deliveryNumber, lastDeliveryId: deliveryNumber);
+                    
+                    await message.AcknowledgeAsync();
+                    
+                    testPeer.WaitForAllMatchersToComplete(3000);
+                }
+                
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(3000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestAcknowledgeIndividualMessagesAsync()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                int msgCount = 6;
+
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.IndividualAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(
+                    message: CreateMessageWithNullContent(),
+                    count: msgCount,
+                    drain: false,
+                    nextIncomingId: 1,
+                    addMessageNumberProperty: true,
+                    sendDrainFlowResponse: false,
+                    sendSettled: false,
+                    creditMatcher: credit => Assert.Greater(credit, msgCount));
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+                
+                CountdownEvent latch = new CountdownEvent(msgCount);
+                List<ITextMessage> messages = new List<ITextMessage>();
+                consumer.Listener += message =>
+                {
+                    messages.Add((ITextMessage) message);
+                    latch.Signal();
+                };
+                
+                Assert.True(latch.Wait(TimeSpan.FromMilliseconds(1000)), $"Should receive: {msgCount}, but received: {messages.Count}");
+                
+                Action<DeliveryState> dispositionMatcher = state => { Assert.AreEqual(state.Descriptor.Code, MessageSupport.ACCEPTED_INSTANCE.Descriptor.Code); };
+                
+                // Acknowledge the messages in a random order and verify the individual dispositions have expected delivery state.
+                Random random = new Random();
+                for (int i = 0; i < msgCount; i++)
+                {
+                    var message = messages[random.Next(msgCount - i)];
+                    messages.Remove(message);
+
+                    uint deliveryNumber = (uint) message.Properties.GetInt(TestAmqpPeer.MESSAGE_NUMBER) + 1;
+
+                    testPeer.ExpectDisposition(settled: true, stateMatcher: dispositionMatcher, firstDeliveryId: deliveryNumber, lastDeliveryId: deliveryNumber);
+                    
+                    await message.AcknowledgeAsync();
+                    
+                    testPeer.WaitForAllMatchersToComplete(3000);
+                }
+                
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(3000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestAutoAcknowledgeMessages()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                int msgCount = 6;
+
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithNullContent(), count: msgCount);
+                
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+
+                for (int i = 0; i < msgCount; i++) 
+                    testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+
+                for (int i = 0; i < msgCount; i++) 
+                    Assert.NotNull(await consumer.ReceiveAsync(TimeSpan.FromMilliseconds(3000)), $"Message {i} not received within given timeout.");
+                
+                testPeer.WaitForAllMatchersToComplete(3000);
+                
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(3000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestAutoAcknowledgeMessagesAsync()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                int msgCount = 6;
+
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithNullContent(), count: msgCount);
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+
+                for (int i = 0; i < msgCount; i++)
+                    testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+                
+                consumer.Listener += (message) => { };
+                
+                testPeer.WaitForAllMatchersToComplete(3000);
+                
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(3000);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Apache-NMS-AMQP-Test/Integration/Async/ConnectionIntegrationTest.cs b/test/Apache-NMS-AMQP-Test/Integration/Async/ConnectionIntegrationTest.cs
new file mode 100644
index 0000000..03e2b29
--- /dev/null
+++ b/test/Apache-NMS-AMQP-Test/Integration/Async/ConnectionIntegrationTest.cs
@@ -0,0 +1,254 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Apache.NMS;
+using Apache.NMS.AMQP;
+using NMS.AMQP.Test.TestAmqp;
+using NMS.AMQP.Test.TestAmqp.BasicTypes;
+using NUnit.Framework;
+
+namespace NMS.AMQP.Test.Integration.Async
+{
+    [TestFixture]
+    public class ConnectionIntegrationTestAsync : IntegrationTestFixture
+    {
+        [Test, Timeout(20_000)]
+        public async Task TestCreateAndCloseConnection()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestExplicitConnectionCloseListenerIsNotInvoked()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                ManualResetEvent exceptionFired = new ManualResetEvent(false);
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                connection.ExceptionListener += exception => { exceptionFired.Set(); };
+                
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+                
+                Assert.IsFalse(exceptionFired.WaitOne(TimeSpan.FromMilliseconds(100)));
+            }
+        }
+        
+        [Test, Timeout(20_000)]
+        public async Task TestCreateAutoAckSession()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                Assert.NotNull(session, "Session should not be null");
+                testPeer.ExpectClose();
+                Assert.AreEqual(AcknowledgementMode.AutoAcknowledge, session.AcknowledgementMode);
+                await connection.CloseAsync();
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateAutoAckSessionByDefault()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync();
+                Assert.NotNull(session, "Session should not be null");
+                Assert.AreEqual(AcknowledgementMode.AutoAcknowledge, session.AcknowledgementMode);
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestRemotelyCloseConnectionDuringSessionCreation()
+        {
+            string errorMessage = "buba";
+
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+
+                // Expect the begin, then explicitly close the connection with an error
+                testPeer.ExpectBegin(sendResponse: false);
+                testPeer.RemotelyCloseConnection(expectCloseResponse: true, errorCondition: AmqpError.NOT_ALLOWED, errorMessage: errorMessage);
+
+                try
+                {
+                    await connection.CreateSessionAsync();
+                    Assert.Fail("Expected exception to be thrown");
+                }
+                catch (NMSException e)
+                {
+                    Assert.True(e.Message.Contains(errorMessage));
+                }
+
+                await connection.CloseAsync();
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestRemotelyEndConnectionListenerInvoked()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                ManualResetEvent done = new ManualResetEvent(false);
+
+                // Don't set a ClientId, so that the underlying AMQP connection isn't established yet
+                IConnection connection = await EstablishConnectionAsync(testPeer: testPeer, setClientId: false);
+
+                // Tell the test peer to close the connection when executing its last handler
+                testPeer.RemotelyCloseConnection(expectCloseResponse: true, errorCondition: ConnectionError.CONNECTION_FORCED, errorMessage: "buba");
+
+                connection.ExceptionListener += exception => done.Set();
+
+                // Trigger the underlying AMQP connection
+                await connection.StartAsync();
+
+                Assert.IsTrue(done.WaitOne(TimeSpan.FromSeconds(5)), "Connection should report failure");
+
+                await connection.CloseAsync();
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestRemotelyEndConnectionWithSessionWithConsumer()
+        {
+            string errorMessage = "buba";
+
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                // Create a consumer, then remotely end the connection afterwards.
+                testPeer.ExpectReceiverAttach();
+ 
+                testPeer.ExpectLinkFlow();
+                testPeer.RemotelyCloseConnection(expectCloseResponse: true, errorCondition: AmqpError.RESOURCE_LIMIT_EXCEEDED, errorMessage: errorMessage);
+
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+
+                Assert.That(() => ((NmsConnection) connection).IsConnected, Is.False.After(10_000, 100), "Connection never closes.");
+
+                try
+                {
+                    await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                    Assert.Fail("Expected ISE to be thrown due to being closed");
+                }
+                catch (NMSConnectionException e)
+                {
+                    Assert.True(e.ToString().Contains(AmqpError.RESOURCE_LIMIT_EXCEEDED));
+                    Assert.True(e.ToString().Contains(errorMessage));
+                }
+
+                // Verify the session is now marked closed
+                try
+                {
+                    var _ = session.AcknowledgementMode;
+                    Assert.Fail("Expected ISE to be thrown due to being closed");
+                }
+                catch (IllegalStateException e)
+                {
+                    Assert.True(e.ToString().Contains(AmqpError.RESOURCE_LIMIT_EXCEEDED));
+                    Assert.True(e.ToString().Contains(errorMessage));
+                }
+
+                // Verify the consumer is now marked closed
+                try
+                {
+                    consumer.Listener += message => { };
+                }
+                catch (IllegalStateException e)
+                {
+                    Assert.True(e.ToString().Contains(AmqpError.RESOURCE_LIMIT_EXCEEDED));
+                    Assert.True(e.ToString().Contains(errorMessage));
+                }
+                
+                // Try closing them explicitly, should effectively no-op in client.
+                // The test peer will throw during close if it sends anything.
+                await consumer.CloseAsync();
+                await session.CloseAsync();
+                await connection.CloseAsync();
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestConnectionStartStop()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                int msgCount = 10;
+
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.ClientAcknowledge);
+
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithNullContent(), count: msgCount);
+                
+                var consumer = await session.CreateConsumerAsync(queue);
+
+                for (int i = 0; i < 5; i++)
+                {
+                    IMessage message = await consumer.ReceiveAsync(TimeSpan.FromMilliseconds(1000));
+                    Assert.IsNotNull(message);
+                }
+                
+                // stop the connection, consumers shouldn't receive any more messages
+                await connection.StopAsync();
+                
+                // No messages should arrive to consumer as connection has been stopped
+                Assert.IsNull(await consumer.ReceiveAsync(TimeSpan.FromMilliseconds(100)), "Message arrived despite the fact, that the connection was stopped.");
+                
+                // restart the connection
+                await connection.StartAsync();
+                
+                // The second batch of messages should be delivered
+                for (int i = 0; i < 5; i++)
+                {
+                    IMessage message = await consumer.ReceiveAsync(TimeSpan.FromMilliseconds(1000));
+                    Assert.IsNotNull(message);
+                }
+                
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Apache-NMS-AMQP-Test/Integration/Async/ConsumerIntegrationTest.cs b/test/Apache-NMS-AMQP-Test/Integration/Async/ConsumerIntegrationTest.cs
new file mode 100644
index 0000000..b4f9885
--- /dev/null
+++ b/test/Apache-NMS-AMQP-Test/Integration/Async/ConsumerIntegrationTest.cs
@@ -0,0 +1,974 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Amqp.Framing;
+using Apache.NMS;
+using Apache.NMS.AMQP;
+using Apache.NMS.AMQP.Message;
+using Apache.NMS.AMQP.Util;
+using Moq;
+using NMS.AMQP.Test.TestAmqp;
+using NMS.AMQP.Test.TestAmqp.BasicTypes;
+using NUnit.Framework;
+
+namespace NMS.AMQP.Test.Integration.Async
+{
+    [TestFixture]
+    public class ConsumerIntegrationTestAsync : IntegrationTestFixture
+    {
+        [Test, Timeout(20_000)]
+        public async Task TestCloseConsumer()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                await consumer.CloseAsync();
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestRemotelyCloseConsumer()
+        {
+            Mock<INmsConnectionListener> mockConnectionListener = new Mock<INmsConnectionListener>();
+            string errorMessage = "buba";
+
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                ManualResetEvent consumerClosed = new ManualResetEvent(false);
+                ManualResetEvent exceptionFired = new ManualResetEvent(false);
+
+                mockConnectionListener
+                    .Setup(listener => listener.OnConsumerClosed(It.IsAny<IMessageConsumer>(), It.IsAny<Exception>()))
+                    .Callback(() => consumerClosed.Set());
+
+                NmsConnection connection = (NmsConnection) await EstablishConnectionAsync(testPeer);
+                connection.AddConnectionListener(mockConnectionListener.Object);
+                connection.ExceptionListener += exception => { exceptionFired.Set(); };
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                // Create a consumer, then remotely end it afterwards.
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+                testPeer.RemotelyDetachLastOpenedLinkOnLastOpenedSession(expectDetachResponse: true, closed: true, errorType: AmqpError.RESOURCE_DELETED, errorMessage: errorMessage, delayBeforeSend: 400);
+
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+
+                // Verify the consumer gets marked closed
+                testPeer.WaitForAllMatchersToComplete(1000);
+
+                Assert.True(consumerClosed.WaitOne(2000), "Consumer closed callback didn't trigger");
+                Assert.False(exceptionFired.WaitOne(20), "Exception listener shouldn't fire with no MessageListener");
+
+                // Try closing it explicitly, should effectively no-op in client.
+                // The test peer will throw during close if it sends anything.
+                await consumer.CloseAsync();
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestRemotelyCloseConsumerWithMessageListenerFiresExceptionListener()
+        {
+            Mock<INmsConnectionListener> mockConnectionListener = new Mock<INmsConnectionListener>();
+            string errorMessage = "buba";
+
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                ManualResetEvent consumerClosed = new ManualResetEvent(false);
+                ManualResetEvent exceptionFired = new ManualResetEvent(false);
+
+                mockConnectionListener
+                    .Setup(listener => listener.OnConsumerClosed(It.IsAny<IMessageConsumer>(), It.IsAny<Exception>()))
+                    .Callback(() => consumerClosed.Set());
+
+                NmsConnection connection = (NmsConnection) await EstablishConnectionAsync(testPeer);
+                connection.AddConnectionListener(mockConnectionListener.Object);
+                connection.ExceptionListener += exception => { exceptionFired.Set(); };
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                // Create a consumer, then remotely end it afterwards.
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+                testPeer.RemotelyDetachLastOpenedLinkOnLastOpenedSession(expectDetachResponse: true, closed: true, errorType: AmqpError.RESOURCE_DELETED, errorMessage: errorMessage, 10);
+
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+
+                consumer.Listener += message => { };
+
+                // Verify the consumer gets marked closed
+                testPeer.WaitForAllMatchersToComplete(1000);
+
+                Assert.True(consumerClosed.WaitOne(2000), "Consumer closed callback didn't trigger");
+                Assert.True(exceptionFired.WaitOne(2000), "Exception listener should have fired with a MessageListener");
+
+                // Try closing it explicitly, should effectively no-op in client.
+                // The test peer will throw during close if it sends anything.
+                await consumer.CloseAsync();
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestReceiveMessageWithReceiveZeroTimeout()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                testPeer.ExpectReceiverAttach();
+
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: new Amqp.Message() { BodySection = new AmqpValue() { Value = null } }, count: 1);
+                testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+                IMessage message = await consumer.ReceiveAsync();
+                Assert.NotNull(message, "A message should have been received");
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(10000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestExceptionInOnMessageReleasesInAutoAckMode()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: new Amqp.Message() { BodySection = new AmqpValue() { Value = null } }, count: 1);
+                testPeer.ExpectDispositionThatIsReleasedAndSettled();
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+                consumer.Listener += message => throw new Exception();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(10000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCloseDurableTopicSubscriberDetachesWithCloseFalse()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                string topicName = "myTopic";
+                string subscriptionName = "mySubscription";
+                ITopic topic = await session.GetTopicAsync(topicName);
+
+                testPeer.ExpectDurableSubscriberAttach(topicName, subscriptionName);
+                testPeer.ExpectLinkFlow();
+
+                IMessageConsumer durableConsumer = await session.CreateDurableConsumerAsync(topic, subscriptionName, null, false);
+
+                testPeer.ExpectDetach(expectClosed: false, sendResponse: true, replyClosed: false);
+                await durableConsumer.CloseAsync();
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestConsumerReceiveThrowsIfConnectionLost()
+        {
+            await DoTestConsumerReceiveThrowsIfConnectionLost(false);
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestConsumerTimedReceiveThrowsIfConnectionLost()
+        {
+            await DoTestConsumerReceiveThrowsIfConnectionLost(true);
+        }
+
+        private async Task DoTestConsumerReceiveThrowsIfConnectionLost(bool useTimeout)
+        {
+            ManualResetEvent consumerReady = new ManualResetEvent(false);
+
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("queue");
+                await connection.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+                testPeer.RunAfterLastHandler(() => { consumerReady.WaitOne(2000); });
+                testPeer.DropAfterLastMatcher(delay: 10);
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+                consumerReady.Set();
+
+                try
+                {
+                    if (useTimeout)
+                    {
+                        await consumer.ReceiveAsync(TimeSpan.FromMilliseconds(10_0000));
+                    }
+                    else
+                    {
+                        await consumer.ReceiveAsync();
+                    }
+
+
+                    Assert.Fail("An exception should have been thrown");
+                }
+                catch (NMSException)
+                {
+                    // Expected
+                }
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestConsumerReceiveNoWaitThrowsIfConnectionLost()
+        {
+            ManualResetEvent disconnected = new ManualResetEvent(false);
+
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                NmsConnection connection = (NmsConnection) await EstablishConnectionAsync(testPeer);
+                Mock<INmsConnectionListener> connectionListener = new Mock<INmsConnectionListener>();
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionFailure(It.IsAny<NMSException>()))
+                    .Callback(() => { disconnected.Set(); });
+
+                connection.AddConnectionListener(connectionListener.Object);
+
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("queue");
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+                testPeer.RemotelyCloseConnection(expectCloseResponse: true, errorCondition: ConnectionError.CONNECTION_FORCED, errorMessage: "buba");
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+
+                Assert.True(disconnected.WaitOne(), "Connection should be disconnected");
+
+                try
+                {
+                    consumer.ReceiveNoWait();
+                    Assert.Fail("An exception should have been thrown");
+                }
+                catch (NMSException)
+                {
+                    // Expected
+                }
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSetMessageListenerAfterStartAndSend()
+        {
+            int messageCount = 4;
+            CountdownEvent latch = new CountdownEvent(messageCount);
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                await connection.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), messageCount);
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(destination);
+
+                for (int i = 0; i < messageCount; i++)
+                {
+                    testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+                }
+
+                consumer.Listener += message => latch.Signal();
+
+                Assert.True(latch.Wait(4000), "Messages not received within given timeout. Count remaining: " + latch.CurrentCount);
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+
+                await consumer.CloseAsync();
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestNoReceivedMessagesWhenConnectionNotStarted()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), count: 3);
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(destination);
+
+                // Wait for a message to arrive then try and receive it, which should not happen
+                // since the connection is not started.
+                Assert.Null(await consumer.ReceiveAsync(TimeSpan.FromMilliseconds(100)));
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestNoReceivedNoWaitMessagesWhenConnectionNotStarted()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), count: 3);
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(destination);
+
+                // Wait for a message to arrive then try and receive it, which should not happen
+                // since the connection is not started.
+                Assert.Null(consumer.ReceiveNoWait());
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSyncReceiveFailsWhenListenerSet()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(destination);
+
+                consumer.Listener += message => { };
+
+                Assert.CatchAsync<NMSException>(async () => await consumer.ReceiveAsync(), "Should have thrown an exception.");
+                Assert.CatchAsync<NMSException>(async () => await consumer.ReceiveAsync(TimeSpan.FromMilliseconds(1000)), "Should have thrown an exception.");
+                Assert.CatchAsync<NMSException>(async () => consumer.ReceiveNoWait(), "Should have thrown an exception.");
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateProducerInOnMessage()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                IQueue outbound = await session.GetQueueAsync("ForwardDest");
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), 1);
+
+                testPeer.ExpectSenderAttach();
+                testPeer.ExpectTransfer(messageMatcher: Assert.NotNull);
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+
+                testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(destination);
+
+                consumer.Listener += message =>
+                {
+                    IMessageProducer producer = session.CreateProducer(outbound);
+                    producer.Send(message);
+                    producer.Close();
+                };
+
+                testPeer.WaitForAllMatchersToComplete(10_000);
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestMessageListenerCallsConnectionCloseThrowsIllegalStateException()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                await connection.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), 1);
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(destination);
+
+                testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+
+                ManualResetEvent latch = new ManualResetEvent(false);
+                Exception exception = null;
+                consumer.Listener += message =>
+                {
+                    try
+                    {
+                        connection.Close();
+                    }
+                    catch (Exception e)
+                    {
+                        exception = e;
+                    }
+
+                    latch.Set();
+                };
+
+                Assert.True(latch.WaitOne(4000), "Messages not received within given timeout.");
+                Assert.IsNotNull(exception);
+                Assert.IsInstanceOf<IllegalStateException>(exception);
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                await consumer.CloseAsync();
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestMessageListenerCallsConnectionStopThrowsIllegalStateException()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                await connection.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), 1);
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(destination);
+
+                testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+
+                ManualResetEvent latch = new ManualResetEvent(false);
+                Exception exception = null;
+                consumer.Listener += message =>
+                {
+                    try
+                    {
+                        connection.Stop();
+                    }
+                    catch (Exception e)
+                    {
+                        exception = e;
+                    }
+
+                    latch.Set();
+                };
+
+                Assert.True(latch.WaitOne(3000), "Messages not received within given timeout.");
+                Assert.IsNotNull(exception);
+                Assert.IsInstanceOf<IllegalStateException>(exception);
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                await consumer.CloseAsync();
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestMessageListenerCallsSessionCloseThrowsIllegalStateException()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                await connection.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), 1);
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(destination);
+
+                testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+
+                ManualResetEvent latch = new ManualResetEvent(false);
+                Exception exception = null;
+                consumer.Listener += message =>
+                {
+                    try
+                    {
+                        session.Close();
+                    }
+                    catch (Exception e)
+                    {
+                        exception = e;
+                    }
+
+                    latch.Set();
+                };
+
+                Assert.True(latch.WaitOne(3000), "Messages not received within given timeout.");
+                Assert.IsNotNull(exception);
+                Assert.IsInstanceOf<IllegalStateException>(exception);
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                await consumer.CloseAsync();
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        // TODO: To be fixed
+        [Test, Timeout(20_000), Ignore("Ignore")]
+        public async Task TestMessageListenerClosesItsConsumer()
+        {
+            var latch = new ManualResetEvent(false);
+            var exceptionListenerFired = new ManualResetEvent(false);
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                connection.ExceptionListener += _ => exceptionListenerFired.Set();
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                await connection.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), 1);
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(destination);
+
+                testPeer.ExpectLinkFlow(drain: true, sendDrainFlowResponse: true, creditMatcher: credit => Assert.AreEqual(99, credit)); // Not sure if expected credit is right
+                testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+
+                Exception exception = null;
+                consumer.Listener += message =>
+                {
+                    try
+                    {
+                        consumer.Close();
+                        latch.Set();
+                    }
+                    catch (Exception e)
+                    {
+                        exception = e;
+                    }
+                };
+
+                Assert.True(latch.WaitOne(TimeSpan.FromMilliseconds(1000)), "Process not completed within given timeout");
+                Assert.IsNull(exception, "No error expected during close");
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                Assert.False(exceptionListenerFired.WaitOne(20), "Exception listener shouldn't have fired");
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestRecoverOrderingWithAsyncConsumer()
+        {
+            ManualResetEvent latch = new ManualResetEvent(false);
+            Exception asyncError = null;
+
+            int recoverCount = 5;
+            int messageCount = 8;
+            int testPayloadLength = 255;
+            string payload = Encoding.UTF8.GetString(new byte[testPayloadLength]);
+
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.ClientAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                await connection.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(
+                    message: new Amqp.Message() { BodySection = new AmqpValue() { Value = payload } },
+                    count: messageCount,
+                    drain: false,
+                    nextIncomingId: 1,
+                    addMessageNumberProperty: true,
+                    sendDrainFlowResponse: false,
+                    sendSettled: false,
+                    creditMatcher: credit => Assert.Greater(credit, messageCount)
+                );
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(destination);
+
+                bool complete = false;
+                int messageSeen = 0;
+                int expectedIndex = 0;
+                consumer.Listener += message =>
+                {
+                    if (complete)
+                    {
+                        return;
+                    }
+
+                    try
+                    {
+                        int actualIndex = message.Properties.GetInt(TestAmqpPeer.MESSAGE_NUMBER);
+                        Assert.AreEqual(expectedIndex, actualIndex, "Received Message Out Of Order");
+
+                        // don't ack the message until we receive it X times
+                        if (messageSeen < recoverCount)
+                        {
+                            session.Recover();
+                            messageSeen++;
+                        }
+                        else
+                        {
+                            messageSeen = 0;
+                            expectedIndex++;
+
+                            // Have the peer expect the accept the disposition (1-based, hence pre-incremented).
+                            testPeer.ExpectDisposition(settled: true,
+                                stateMatcher: state => Assert.AreEqual(state.Descriptor.Code, MessageSupport.ACCEPTED_INSTANCE.Descriptor.Code
+                                ));
+
+                            message.Acknowledge();
+
+                            if (expectedIndex == messageCount)
+                            {
+                                complete = true;
+                                latch.Set();
+                            }
+                        }
+                    }
+                    catch (Exception e)
+                    {
+                        complete = true;
+                        asyncError = e;
+                        latch.Set();
+                    }
+                };
+
+                Assert.True(latch.WaitOne(TimeSpan.FromSeconds(15)), "Messages not received within given timeout.");
+                Assert.IsNull(asyncError, "Unexpected exception");
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestConsumerCloseWaitsForAsyncDeliveryToComplete()
+        {
+            ManualResetEvent latch = new ManualResetEvent(false);
+
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                await connection.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), count: 1);
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(destination);
+
+                testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+
+                consumer.Listener += _ =>
+                {
+                    latch.Set();
+                    Task.Delay(TimeSpan.FromMilliseconds(100)).GetAwaiter().GetResult();
+                };
+
+                Assert.True(latch.WaitOne(TimeSpan.FromMilliseconds(3000)), "Messages not received within given timeout.");
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                await consumer.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSessionCloseWaitsForAsyncDeliveryToComplete()
+        {
+            ManualResetEvent latch = new ManualResetEvent(false);
+
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                await connection.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), count: 1);
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(destination);
+
+                testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+
+                consumer.Listener += _ =>
+                {
+                    latch.Set();
+                    Task.Delay(TimeSpan.FromMilliseconds(100)).GetAwaiter().GetResult();
+                };
+
+                Assert.True(latch.WaitOne(TimeSpan.FromMilliseconds(3000)), "Messages not received within given timeout.");
+
+                testPeer.ExpectEnd();
+                await session.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestConnectionCloseWaitsForAsyncDeliveryToComplete()
+        {
+            ManualResetEvent latch = new ManualResetEvent(false);
+
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                await connection.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), count: 1);
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(destination);
+
+                testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+
+                consumer.Listener += _ =>
+                {
+                    latch.Set();
+                    Task.Delay(TimeSpan.FromMilliseconds(100)).GetAwaiter().GetResult();
+                };
+
+                Assert.True(latch.WaitOne(TimeSpan.FromMilliseconds(3000)), "Messages not received within given timeout.");
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestRecoveredMessageShouldNotBeMutated()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.ClientAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                string originalPayload = "testMessage";
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: new Amqp.Message { BodySection = new AmqpValue() { Value = originalPayload } }, count: 1);
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(destination);
+                NmsTextMessage message = await consumer.ReceiveAsync() as NmsTextMessage;
+                Assert.NotNull(message);
+                message.IsReadOnlyBody = false;
+                message.Text = message.Text + "Received";
+                await session.RecoverAsync();
+
+                ITextMessage recoveredMessage = await consumer.ReceiveAsync() as ITextMessage;
+                Assert.IsNotNull(recoveredMessage);
+                Assert.AreNotEqual(message.Text, recoveredMessage.Text);
+                Assert.AreEqual(originalPayload, recoveredMessage.Text);
+                Assert.AreNotSame(message, recoveredMessage);
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Apache-NMS-AMQP-Test/Integration/Async/FailoverIntegrationTest.cs b/test/Apache-NMS-AMQP-Test/Integration/Async/FailoverIntegrationTest.cs
new file mode 100644
index 0000000..74f66ad
--- /dev/null
+++ b/test/Apache-NMS-AMQP-Test/Integration/Async/FailoverIntegrationTest.cs
@@ -0,0 +1,1164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using Amqp.Framing;
+using Amqp.Types;
+using Apache.NMS;
+using Apache.NMS.AMQP;
+using Moq;
+using NLog;
+using NMS.AMQP.Test.TestAmqp;
+using NMS.AMQP.Test.TestAmqp.BasicTypes;
+using NUnit.Framework;
+
+namespace NMS.AMQP.Test.Integration.Async
+{
+    [TestFixture]
+    public class FailoverIntegrationTestAsync : IntegrationTestFixture
+    {
+        private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
+        
+        [Test, Timeout(20_000), Category("Windows")]
+        public async Task TestFailoverHandlesDropThenRejectionCloseAfterConnect()
+        {
+            using (TestAmqpPeer originalPeer = new TestAmqpPeer())
+            using (TestAmqpPeer rejectingPeer = new TestAmqpPeer())
+            using (TestAmqpPeer finalPeer = new TestAmqpPeer())
+            {
+                ManualResetEvent originalConnected = new ManualResetEvent(false);
+                ManualResetEvent finalConnected = new ManualResetEvent(false);
+
+                // Create a peer to connect to, one to fail to reconnect to, and a final one to reconnect to
+                var originalUri = CreatePeerUri(originalPeer);
+                var rejectingUri = CreatePeerUri(rejectingPeer);
+                var finalUri = CreatePeerUri(finalPeer);
+                
+                Logger.Info($"Original peer is at: {originalUri}");
+                Logger.Info($"Rejecting peer is at: {rejectingUri}");
+                Logger.Info($"Final peer is at: {finalUri}");
+
+                // Connect to the first
+                originalPeer.ExpectSaslAnonymous();
+                originalPeer.ExpectOpen();
+                originalPeer.ExpectBegin();
+
+                long ird = 0;
+                long rd = 2000;
+
+                NmsConnection connection = await EstablishAnonymousConnection("failover.initialReconnectDelay=" + ird + "&failover.reconnectDelay=" + rd + "&failover.maxReconnectAttempts=10", originalPeer,
+                    rejectingPeer, finalPeer);
+
+                Mock<INmsConnectionListener> connectionListener = new Mock<INmsConnectionListener>();
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionEstablished(It.Is<Uri>(uri => originalUri == uri.ToString())))
+                    .Callback(() => { originalConnected.Set(); });
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionRestored(It.Is<Uri>(uri => finalUri == uri.ToString())))
+                    .Callback(() => { finalConnected.Set(); });
+
+                connection.AddConnectionListener(connectionListener.Object);
+
+                await connection.StartAsync();
+
+                Assert.True(originalConnected.WaitOne(TimeSpan.FromSeconds(5)), "Should connect to original peer");
+                Assert.False(finalConnected.WaitOne(TimeSpan.FromMilliseconds(100)), "Should not yet have connected to final peer");
+
+                // Set expectations on rejecting and final peer
+                rejectingPeer.RejectConnect(AmqpError.NOT_FOUND, "Resource could not be located");
+
+                finalPeer.ExpectSaslAnonymous();
+                finalPeer.ExpectOpen();
+                finalPeer.ExpectBegin();
+
+                // Close the original peer and wait for things to shake out.
+                originalPeer.Close(sendClose: true);
+
+                rejectingPeer.WaitForAllMatchersToComplete(2000);
+
+                Assert.True(finalConnected.WaitOne(TimeSpan.FromSeconds(10)), "Should connect to final peer");
+
+                finalPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                finalPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestFailoverHandlesDropWithModifiedInitialReconnectDelay()
+        {
+            using (TestAmqpPeer originalPeer = new TestAmqpPeer())
+            using (TestAmqpPeer finalPeer = new TestAmqpPeer())
+            {
+                ManualResetEvent originalConnected = new ManualResetEvent(false);
+                ManualResetEvent finalConnected = new ManualResetEvent(false);
+
+                // Create a peer to connect to, then one to reconnect to
+                var originalUri = CreatePeerUri(originalPeer);
+                var finalUri = CreatePeerUri(finalPeer);
+
+                // Connect to the first peer
+                originalPeer.ExpectSaslAnonymous();
+                originalPeer.ExpectOpen();
+                originalPeer.ExpectBegin();
+                originalPeer.ExpectBegin();
+                originalPeer.DropAfterLastMatcher();
+
+                NmsConnection connection = await EstablishAnonymousConnection("failover.initialReconnectDelay=1&failover.reconnectDelay=600&failover.maxReconnectAttempts=10", originalPeer, finalPeer);
+                Mock<INmsConnectionListener> connectionListener = new Mock<INmsConnectionListener>();
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionEstablished(It.Is<Uri>(uri => originalUri == uri.ToString())))
+                    .Callback(() => { originalConnected.Set(); });
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionRestored(It.Is<Uri>(uri => finalUri == uri.ToString())))
+                    .Callback(() => { finalConnected.Set(); });
+
+                connection.AddConnectionListener(connectionListener.Object);
+
+                await connection.StartAsync();
+
+                Assert.True(originalConnected.WaitOne(TimeSpan.FromSeconds(5)), "Should connect to original peer");
+
+                // Post Failover Expectations of FinalPeer
+                finalPeer.ExpectSaslAnonymous();
+                finalPeer.ExpectOpen();
+                finalPeer.ExpectBegin();
+                finalPeer.ExpectBegin();
+
+                await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                Assert.True(finalConnected.WaitOne(TimeSpan.FromSeconds(5)), "Should connect to final peer");
+
+                // Shut it down
+                finalPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                finalPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestFailoverInitialReconnectDelayDoesNotApplyToInitialConnect()
+        {
+            using (TestAmqpPeer originalPeer = new TestAmqpPeer())
+            {
+                // Connect to the first peer
+                originalPeer.ExpectSaslAnonymous();
+                originalPeer.ExpectOpen();
+                originalPeer.ExpectBegin();
+
+                int delay = 20000;
+                Stopwatch watch = new Stopwatch();
+                watch.Start();
+
+                NmsConnection connection = await EstablishAnonymousConnection("failover.initialReconnectDelay=" + delay + "&failover.maxReconnectAttempts=1", originalPeer);
+                await connection.StartAsync();
+
+                watch.Stop();
+
+                Assert.True(watch.ElapsedMilliseconds < delay,
+                    "Initial connect should not have delayed for the specified initialReconnectDelay." + "Elapsed=" + watch.ElapsedMilliseconds + ", delay=" + delay);
+                Assert.True(watch.ElapsedMilliseconds < 5000, $"Connection took longer than reasonable: {watch.ElapsedMilliseconds}");
+
+                // Shut it down
+                originalPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                originalPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestFailoverHandlesDropAfterSessionCloseRequested()
+        {
+            using (TestAmqpPeer originalPeer = new TestAmqpPeer())
+            {
+                ManualResetEvent originalConnected = new ManualResetEvent(false);
+
+                // Create a peer to connect to
+                var originalUri = CreatePeerUri(originalPeer);
+
+                // Connect to the first peer
+                originalPeer.ExpectSaslAnonymous();
+                originalPeer.ExpectOpen();
+                originalPeer.ExpectBegin();
+
+                Mock<INmsConnectionListener> connectionListener = new Mock<INmsConnectionListener>();
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionEstablished(It.Is<Uri>(uri => originalUri == uri.ToString())))
+                    .Callback(() => { originalConnected.Set(); });
+
+                NmsConnection connection = await EstablishAnonymousConnection(originalPeer);
+                connection.AddConnectionListener(connectionListener.Object);
+
+                await connection.StartAsync();
+
+                Assert.True(originalConnected.WaitOne(TimeSpan.FromSeconds(5)), "Should connect to peer");
+
+                originalPeer.ExpectBegin();
+                originalPeer.ExpectEnd(sendResponse: false);
+                originalPeer.DropAfterLastMatcher();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                ManualResetEvent sessionCloseCompleted = new ManualResetEvent(false);
+                Exception sessionClosedThrew = null;
+
+                Task.Run(() =>
+                {
+                    try
+                    {
+                        session.Close();
+                    }
+                    catch (Exception e)
+                    {
+                        sessionClosedThrew = e;
+                    }
+                    finally
+                    {
+                        sessionCloseCompleted.Set();
+                    }
+                });
+
+                originalPeer.WaitForAllMatchersToComplete(2000);
+
+                Assert.IsTrue(sessionCloseCompleted.WaitOne(TimeSpan.FromSeconds(3)), "Session close should have completed by now");
+                Assert.IsNull(sessionClosedThrew, "Session close should have completed normally");
+
+                await connection.CloseAsync();
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateConsumerFailsWhenLinkRefused()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                testPeer.ExpectSaslAnonymous();
+                testPeer.ExpectOpen();
+                testPeer.ExpectBegin();
+
+                NmsConnection connection = await EstablishAnonymousConnection(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                string topicName = "myTopic";
+                ITopic topic = await session.GetTopicAsync(topicName);
+
+                // Expect a link to a topic node, which we will then refuse
+                testPeer.ExpectReceiverAttach(sourceMatcher: source =>
+                {
+                    Assert.AreEqual(topicName, source.Address);
+                    Assert.IsFalse(source.Dynamic);
+                    Assert.AreEqual((uint) TerminusDurability.NONE, source.Durable);
+                }, targetMatcher: Assert.NotNull, linkNameMatcher: Assert.NotNull, refuseLink: true);
+
+                //Expect the detach response to the test peer closing the consumer link after refusal.
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: false, replyClosed: false);
+
+                Assert.CatchAsync<NMSException>(async () => await session.CreateConsumerAsync(topic));
+
+                // Shut it down
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestFailoverEnforcesRequestTimeoutSession()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                ManualResetEvent connected = new ManualResetEvent(false);
+                ManualResetEvent disconnected = new ManualResetEvent(false);
+
+                // Connect to the test peer
+                testPeer.ExpectSaslAnonymous();
+                testPeer.ExpectOpen();
+                testPeer.ExpectBegin();
+                testPeer.DropAfterLastMatcher(delay: 10);
+
+                NmsConnection connection = await EstablishAnonymousConnection("nms.requestTimeout=1000&failover.reconnectDelay=2000&failover.maxReconnectAttempts=60", testPeer);
+
+                Mock<INmsConnectionListener> connectionListener = new Mock<INmsConnectionListener>();
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionInterrupted(It.IsAny<Uri>()))
+                    .Callback(() => { disconnected.Set(); });
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionEstablished(It.IsAny<Uri>()))
+                    .Callback(() => { connected.Set(); });
+
+
+                connection.AddConnectionListener(connectionListener.Object);
+
+                await connection.StartAsync();
+
+                Assert.True(connected.WaitOne(TimeSpan.FromSeconds(5)), "Should connect to peer");
+                Assert.True(disconnected.WaitOne(TimeSpan.FromSeconds(5)), "Should lose connection to peer");
+
+                Assert.CatchAsync<NMSException>(async () => await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge));
+
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestFailoverEnforcesSendTimeout()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                ManualResetEvent connected = new ManualResetEvent(false);
+                ManualResetEvent disconnected = new ManualResetEvent(false);
+
+                // Connect to the test peer
+                testPeer.ExpectSaslAnonymous();
+                testPeer.ExpectOpen();
+                testPeer.ExpectBegin();
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+                testPeer.DropAfterLastMatcher();
+
+                NmsConnection connection = await EstablishAnonymousConnection("nms.sendTimeout=1000&failover.reconnectDelay=2000&failover.maxReconnectAttempts=60", testPeer);
+
+                Mock<INmsConnectionListener> connectionListener = new Mock<INmsConnectionListener>();
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionEstablished(It.IsAny<Uri>()))
+                    .Callback(() => { connected.Set(); });
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionInterrupted(It.IsAny<Uri>()))
+                    .Callback(() => { disconnected.Set(); });
+
+                connection.AddConnectionListener(connectionListener.Object);
+
+                await connection.StartAsync();
+
+                Assert.True(connected.WaitOne(TimeSpan.FromSeconds(5)), "Should connect to peer");
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+
+                Assert.True(disconnected.WaitOne(TimeSpan.FromSeconds(5)), "Should lose connection to peer");
+
+                Assert.CatchAsync<NMSException>(async () => await producer.SendAsync(producer.CreateTextMessage("test")));
+
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestFailoverPassthroughOfCompletedSyncSend()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                NmsConnection connection = await EstablishAnonymousConnection((testPeer));
+
+                testPeer.ExpectSaslAnonymous();
+                testPeer.ExpectOpen();
+                testPeer.ExpectBegin();
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+
+                // Do a warm up
+                string messageContent1 = "myMessage1";
+                testPeer.ExpectTransfer(messageMatcher: m => { Assert.AreEqual(messageContent1, (m.BodySection as AmqpValue).Value); });
+
+                ITextMessage message1 = await session.CreateTextMessageAsync(messageContent1);
+                await producer.SendAsync(message1);
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+
+                // Create and send a new message, which is accepted
+                String messageContent2 = "myMessage2";
+                int delay = 15;
+                testPeer.ExpectTransfer(messageMatcher: m => Assert.AreEqual(messageContent2, (m.BodySection as AmqpValue).Value),
+                    settled: false,
+                    sendResponseDisposition: true,
+                    responseState: new Accepted(),
+                    responseSettled: true,
+                    stateMatcher: Assert.IsNull,
+                    dispositionDelay: delay);
+                testPeer.ExpectClose();
+
+                ITextMessage message2 = await session.CreateTextMessageAsync(messageContent2);
+
+                DateTime start = DateTime.UtcNow;
+                await producer.SendAsync(message2);
+
+                TimeSpan elapsed = DateTime.UtcNow - start;
+                Assert.That(elapsed.TotalMilliseconds, Is.GreaterThanOrEqualTo(delay));
+
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+
+        [Test, Timeout(20_000)]
+        public async Task TestFailoverPassthroughOfRejectedSyncSend()
+        {
+            await DoFailoverPassthroughOfFailingSyncSendTestImpl(new Rejected());
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestFailoverPassthroughOfReleasedSyncSend()
+        {
+            await DoFailoverPassthroughOfFailingSyncSendTestImpl(new Released());
+        }
+
+        [Test, Timeout(20_000), Ignore("TODO: It should be fixed.")]
+        public async Task TestFailoverPassthroughOfModifiedFailedSyncSend()
+        {
+            var modified = new Modified()
+            {
+                DeliveryFailed = true
+            };
+            await DoFailoverPassthroughOfFailingSyncSendTestImpl(modified);
+        }
+
+        private async Task DoFailoverPassthroughOfFailingSyncSendTestImpl(Outcome failingState)
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                NmsConnection connection = await EstablishAnonymousConnection((testPeer));
+
+                testPeer.ExpectSaslAnonymous();
+                testPeer.ExpectOpen();
+                testPeer.ExpectBegin();
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+
+                // Do a warm up that succeeds
+                string messageContent1 = "myMessage1";
+                testPeer.ExpectTransfer(messageMatcher: m => { Assert.AreEqual(messageContent1, (m.BodySection as AmqpValue).Value); });
+
+                ITextMessage message1 = await session.CreateTextMessageAsync(messageContent1);
+                await producer.SendAsync(message1);
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+
+                // Create and send a new message, which fails as it is not accepted
+                Assert.False(failingState is Accepted);
+
+                String messageContent2 = "myMessage2";
+                int delay = 15;
+                testPeer.ExpectTransfer(messageMatcher: m => Assert.AreEqual(messageContent2, (m.BodySection as AmqpValue).Value),
+                    settled: false,
+                    sendResponseDisposition: true,
+                    responseState: failingState,
+                    responseSettled: true,
+                    stateMatcher: Assert.IsNull,
+                    dispositionDelay: delay);
+
+                ITextMessage message2 = await session.CreateTextMessageAsync(messageContent2);
+
+                DateTime start = DateTime.UtcNow;
+                Assert.Catch(() => producer.Send(message2), "Expected an exception for this send.");
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+
+                //Do a final send that succeeds
+                string messageContent3 = "myMessage3";
+                testPeer.ExpectTransfer(messageMatcher: m => { Assert.AreEqual(messageContent3, (m.BodySection as AmqpValue).Value); });
+
+                ITextMessage message3 = await session.CreateTextMessageAsync(messageContent3);
+                await producer.SendAsync(message3);
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateSessionAfterConnectionDrops()
+        {
+            using (TestAmqpPeer originalPeer = new TestAmqpPeer())
+            using (TestAmqpPeer finalPeer = new TestAmqpPeer())
+            {
+                ManualResetEvent originalConnected = new ManualResetEvent(false);
+                ManualResetEvent finalConnected = new ManualResetEvent(false);
+
+                // Create a peer to connect to, then one to reconnect to
+                var originalUri = CreatePeerUri(originalPeer);
+                var finalUri = CreatePeerUri(finalPeer);
+
+                // Connect to the first peer
+                originalPeer.ExpectSaslAnonymous();
+                originalPeer.ExpectOpen();
+                originalPeer.ExpectBegin();
+                originalPeer.ExpectBegin(sendResponse: false);
+                originalPeer.DropAfterLastMatcher();
+
+                NmsConnection connection = await EstablishAnonymousConnection(originalPeer, finalPeer);
+
+                Mock<INmsConnectionListener> connectionListener = new Mock<INmsConnectionListener>();
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionEstablished(It.Is<Uri>(uri => originalUri == uri.ToString())))
+                    .Callback(() => { originalConnected.Set(); });
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionRestored(It.Is<Uri>(uri => finalUri == uri.ToString())))
+                    .Callback(() => { finalConnected.Set(); });
+
+                connection.AddConnectionListener(connectionListener.Object);
+
+                await connection.StartAsync();
+
+                Assert.True(originalConnected.WaitOne(TimeSpan.FromSeconds(5)), "Should connect to original peer");
+
+                // Post Failover Expectations of FinalPeer
+
+                finalPeer.ExpectSaslAnonymous();
+                finalPeer.ExpectOpen();
+                finalPeer.ExpectBegin();
+                finalPeer.ExpectBegin();
+                finalPeer.ExpectEnd();
+                finalPeer.ExpectClose();
+
+                ISession session = await connection.CreateSessionAsync();
+
+                Assert.True(finalConnected.WaitOne(TimeSpan.FromSeconds(5)), "Should connect to final peer");
+
+                await session.CloseAsync();
+                await connection.CloseAsync();
+
+                finalPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateConsumerAfterConnectionDrops()
+        {
+            using (TestAmqpPeer originalPeer = new TestAmqpPeer())
+            using (TestAmqpPeer finalPeer = new TestAmqpPeer())
+            {
+                ManualResetEvent originalConnected = new ManualResetEvent(false);
+                ManualResetEvent finalConnected = new ManualResetEvent(false);
+
+                // Create a peer to connect to, then one to reconnect to
+                var originalUri = CreatePeerUri(originalPeer);
+                var finalUri = CreatePeerUri(finalPeer);
+
+                // Connect to the first peer
+                originalPeer.ExpectSaslAnonymous();
+                originalPeer.ExpectOpen();
+                originalPeer.ExpectBegin();
+                originalPeer.ExpectBegin();
+                originalPeer.DropAfterLastMatcher();
+
+                NmsConnection connection = await EstablishAnonymousConnection(originalPeer, finalPeer);
+
+                Mock<INmsConnectionListener> connectionListener = new Mock<INmsConnectionListener>();
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionEstablished(It.Is<Uri>(uri => originalUri == uri.ToString())))
+                    .Callback(() => { originalConnected.Set(); });
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionRestored(It.Is<Uri>(uri => finalUri == uri.ToString())))
+                    .Callback(() => { finalConnected.Set(); });
+
+                connection.AddConnectionListener(connectionListener.Object);
+
+                await connection.StartAsync();
+
+                Assert.True(originalConnected.WaitOne(TimeSpan.FromSeconds(5)), "Should connect to original peer");
+
+                // Post Failover Expectations of FinalPeer
+                finalPeer.ExpectSaslAnonymous();
+                finalPeer.ExpectOpen();
+                finalPeer.ExpectBegin();
+                finalPeer.ExpectBegin();
+                finalPeer.ExpectReceiverAttach();
+                finalPeer.ExpectLinkFlow(drain: false, sendDrainFlowResponse: false, creditMatcher: credit => Assert.AreEqual(credit, 200));
+                finalPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                finalPeer.ExpectClose();
+
+                ISession session = await connection.CreateSessionAsync();
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+
+                Assert.IsNull(await consumer.ReceiveAsync(TimeSpan.FromMilliseconds(500)));
+
+                Assert.True(finalConnected.WaitOne(TimeSpan.FromSeconds(5)), "Should connect to final peer");
+
+                await consumer.CloseAsync();
+
+                // Shut it down
+                await connection.CloseAsync();
+
+                finalPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateProducerAfterConnectionDrops()
+        {
+            using (TestAmqpPeer originalPeer = new TestAmqpPeer())
+            using (TestAmqpPeer finalPeer = new TestAmqpPeer())
+            {
+                ManualResetEvent originalConnected = new ManualResetEvent(false);
+                ManualResetEvent finalConnected = new ManualResetEvent(false);
+
+                // Create a peer to connect to, then one to reconnect to
+                var originalUri = CreatePeerUri(originalPeer);
+                var finalUri = CreatePeerUri(finalPeer);
+
+                // Connect to the first peer
+                originalPeer.ExpectSaslAnonymous();
+                originalPeer.ExpectOpen();
+                originalPeer.ExpectBegin();
+                originalPeer.ExpectBegin();
+                originalPeer.DropAfterLastMatcher();
+
+                NmsConnection connection = await EstablishAnonymousConnection(originalPeer, finalPeer);
+
+                Mock<INmsConnectionListener> connectionListener = new Mock<INmsConnectionListener>();
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionEstablished(It.Is<Uri>(uri => originalUri == uri.ToString())))
+                    .Callback(() => { originalConnected.Set(); });
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionRestored(It.Is<Uri>(uri => finalUri == uri.ToString())))
+                    .Callback(() => { finalConnected.Set(); });
+
+                connection.AddConnectionListener(connectionListener.Object);
+
+                await connection.StartAsync();
+
+                Assert.True(originalConnected.WaitOne(TimeSpan.FromSeconds(5)), "Should connect to original peer");
+
+                // Post Failover Expectations of FinalPeer
+                finalPeer.ExpectSaslAnonymous();
+                finalPeer.ExpectOpen();
+                finalPeer.ExpectBegin();
+                finalPeer.ExpectBegin();
+                finalPeer.ExpectSenderAttach();
+                finalPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                finalPeer.ExpectClose();
+
+                ISession session = await connection.CreateSessionAsync();
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+
+                Assert.True(finalConnected.WaitOne(TimeSpan.FromSeconds(5)), "Should connect to final peer");
+
+                await producer.CloseAsync();
+
+                await connection.CloseAsync();
+
+                finalPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000), Ignore("TODO: Fix")]
+        public async Task TestStartMaxReconnectAttemptsTriggeredWhenRemotesAreRejecting()
+        {
+            using (TestAmqpPeer firstPeer = new TestAmqpPeer())
+            using (TestAmqpPeer secondPeer = new TestAmqpPeer())
+            using (TestAmqpPeer thirdPeer = new TestAmqpPeer())
+            using (TestAmqpPeer fourthPeer = new TestAmqpPeer())
+            {
+                ManualResetEvent failedConnection = new ManualResetEvent(false);
+
+                firstPeer.RejectConnect(AmqpError.NOT_FOUND, "Resource could not be located");
+                secondPeer.RejectConnect(AmqpError.NOT_FOUND, "Resource could not be located");
+                thirdPeer.RejectConnect(AmqpError.NOT_FOUND, "Resource could not be located");
+
+                // This shouldn't get hit, but if it does accept the connect so we don't pass the failed
+                // to connect assertion.
+                fourthPeer.ExpectSaslAnonymous();
+                fourthPeer.ExpectOpen();
+                fourthPeer.ExpectBegin();
+                fourthPeer.ExpectClose();
+
+                NmsConnection connection = await EstablishAnonymousConnection("failover.startupMaxReconnectAttempts=3&failover.reconnectDelay=15&failover.useReconnectBackOff=false",
+                    firstPeer, secondPeer, thirdPeer, fourthPeer);
+
+                Mock<INmsConnectionListener> connectionListener = new Mock<INmsConnectionListener>();
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionFailure(It.IsAny<NMSException>()))
+                    .Callback(() => { failedConnection.Set(); });
+
+                connection.AddConnectionListener(connectionListener.Object);
+
+                Assert.CatchAsync<NMSException>(async () => await connection.StartAsync(), "Should not be able to connect");
+
+                Assert.True(failedConnection.WaitOne(TimeSpan.FromSeconds(5)));
+
+                try
+                {
+                    await connection.CloseAsync();
+                }
+                catch (NMSException e)
+                {
+                }
+                
+                firstPeer.WaitForAllMatchersToComplete(2000);
+                secondPeer.WaitForAllMatchersToComplete(2000);
+                thirdPeer.WaitForAllMatchersToComplete(2000);
+                
+                // Shut down last peer and verify no connection made to it
+                fourthPeer.PurgeExpectations();
+                fourthPeer.Close();
+                Assert.NotNull(firstPeer.ClientSocket, "Peer 1 should have accepted a TCP connection");
+                Assert.NotNull(secondPeer.ClientSocket, "Peer 2 should have accepted a TCP connection");
+                Assert.NotNull(thirdPeer.ClientSocket, "Peer 3 should have accepted a TCP connection");
+                Assert.IsNull(fourthPeer.ClientSocket, "Peer 4 should not have accepted any TCP connection");
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestRemotelyCloseConsumerWithMessageListenerFiresNMSExceptionListener()
+        {
+            Symbol errorCondition = AmqpError.RESOURCE_DELETED;
+            string errorDescription = nameof(TestRemotelyCloseConsumerWithMessageListenerFiresNMSExceptionListener);
+            
+            await DoRemotelyCloseConsumerWithMessageListenerFiresNMSExceptionListenerTestImpl(errorCondition, errorDescription);
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestRemotelyCloseConsumerWithMessageListenerWithoutErrorFiresNMSExceptionListener()
+        {
+            await DoRemotelyCloseConsumerWithMessageListenerFiresNMSExceptionListenerTestImpl(null, null);
+        }
+
+        private async Task DoRemotelyCloseConsumerWithMessageListenerFiresNMSExceptionListenerTestImpl(Symbol errorType, string errorMessage)
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                ManualResetEvent consumerClosed = new ManualResetEvent(false);
+                ManualResetEvent exceptionListenerFired = new ManualResetEvent(false);
+                
+                testPeer.ExpectSaslAnonymous();
+                testPeer.ExpectOpen();
+                testPeer.ExpectBegin();
+                
+                NmsConnection connection = await EstablishAnonymousConnection("failover.maxReconnectAttempts=1", testPeer);
+                
+                connection.ExceptionListener += exception => { exceptionListenerFired.Set(); };
+
+                Mock<INmsConnectionListener> connectionListener = new Mock<INmsConnectionListener>();
+
+                connectionListener
+                    .Setup(listener => listener.OnConsumerClosed(It.IsAny<IMessageConsumer>(), It.IsAny<Exception>()))
+                    .Callback(() => { consumerClosed.Set(); });
+                
+                connection.AddConnectionListener(connectionListener.Object);
+                
+                testPeer.ExpectBegin();
+                testPeer.ExpectBegin();
+
+                ISession session1 = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                ISession session2 = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session2.GetQueueAsync("myQueue");
+                
+                // Create a consumer, then remotely end it afterwards.
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+                testPeer.ExpectEnd();
+                testPeer.RemotelyDetachLastOpenedLinkOnLastOpenedSession(expectDetachResponse: true, closed: true,  errorType: errorType, errorMessage: errorMessage, delayBeforeSend: 10);
+
+                IMessageConsumer consumer = await session2.CreateConsumerAsync(queue);
+                consumer.Listener += message => { };
+                
+                // Close first session to allow the receiver remote close timing to be deterministic
+                await session1.CloseAsync();
+                
+                // Verify the consumer gets marked closed
+                testPeer.WaitForAllMatchersToComplete(1000);
+                
+                Assert.True(consumerClosed.WaitOne(TimeSpan.FromMilliseconds(2000)), "Consumer closed callback didn't trigger");
+                Assert.True(exceptionListenerFired.WaitOne(TimeSpan.FromMilliseconds(2000)), "NMS Exception listener should have fired with a MessageListener");
+                
+                // Try closing it explicitly, should effectively no-op in client.
+                // The test peer will throw during close if it sends anything.
+                await consumer.CloseAsync();
+                
+                // Shut the connection down
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestFailoverDoesNotFailPendingSend()
+        {
+            using (TestAmqpPeer originalPeer = new TestAmqpPeer())
+            using (TestAmqpPeer finalPeer = new TestAmqpPeer())
+            {
+                originalPeer.ExpectSaslAnonymous();
+                originalPeer.ExpectOpen();
+                originalPeer.ExpectBegin();
+                originalPeer.ExpectBegin();
+                
+                // Ensure our send blocks in the provider waiting for credit so that on failover
+                // the message will actually get sent from the Failover bits once we grant some
+                // credit for the recovered sender.
+                originalPeer.ExpectSenderAttachWithoutGrantingCredit();
+                originalPeer.DropAfterLastMatcher(delay: 10); // Wait for sender to get into wait state
+                
+                // Post Failover Expectations of sender
+                finalPeer.ExpectSaslAnonymous();
+                finalPeer.ExpectOpen();
+                finalPeer.ExpectBegin();
+                finalPeer.ExpectBegin();
+                finalPeer.ExpectSenderAttach();
+                finalPeer.ExpectTransfer(messageMatcher: Assert.IsNotNull);
+                finalPeer.ExpectClose();
+                
+                NmsConnection connection = await EstablishAnonymousConnection("failover.initialReconnectDelay=25", originalPeer, finalPeer);
+                ISession session = await connection.CreateSessionAsync();
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+                
+                // Create and transfer a new message
+                string text = "myMessage";
+                ITextMessage message = await session.CreateTextMessageAsync(text);
+                
+                Assert.DoesNotThrow(() =>
+                {
+                    producer.Send(message);
+                });
+                
+                await connection.CloseAsync();
+                
+                finalPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestTempDestinationRecreatedAfterConnectionFailsOver()
+        {
+            using (TestAmqpPeer originalPeer = new TestAmqpPeer())
+            using (TestAmqpPeer finalPeer = new TestAmqpPeer())
+            {
+                ManualResetEvent originalConnected = new ManualResetEvent(false);
+                ManualResetEvent finalConnected = new ManualResetEvent(false);
+                
+                // Create a peer to connect to, then one to reconnect to
+                var originalUri = CreatePeerUri(originalPeer);
+                var finalUri = CreatePeerUri(finalPeer);
+                
+                originalPeer.ExpectSaslAnonymous();
+                originalPeer.ExpectOpen();
+                originalPeer.ExpectBegin();
+                originalPeer.ExpectBegin();
+                string dynamicAddress1 = "myTempTopicAddress";
+                originalPeer.ExpectTempTopicCreationAttach(dynamicAddress1);
+                originalPeer.DropAfterLastMatcher();
+                
+                NmsConnection connection = await EstablishAnonymousConnection(originalPeer, finalPeer);
+
+                Mock<INmsConnectionListener> connectionListener = new Mock<INmsConnectionListener>();
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionEstablished(It.Is<Uri>(uri => originalUri == uri.ToString())))
+                    .Callback(() => { originalConnected.Set(); });
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionRestored(It.Is<Uri>(uri => finalUri == uri.ToString())))
+                    .Callback(() => { finalConnected.Set(); });
+
+                connection.AddConnectionListener(connectionListener.Object);
+
+                await connection.StartAsync();
+                
+                Assert.True(originalConnected.WaitOne(TimeSpan.FromSeconds(5)), "Should connect to original peer");
+                
+                // Post Failover Expectations of FinalPeer
+                finalPeer.ExpectSaslAnonymous();
+                finalPeer.ExpectOpen();
+                finalPeer.ExpectBegin();
+                String dynamicAddress2 = "myTempTopicAddress2";
+                finalPeer.ExpectTempTopicCreationAttach(dynamicAddress2);
+                
+                // Session is recreated after previous temporary destinations are recreated on failover.
+                finalPeer.ExpectBegin();
+                
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                ITemporaryTopic temporaryTopic = await session.CreateTemporaryTopicAsync();
+                
+                Assert.True(finalConnected.WaitOne(TimeSpan.FromSeconds(5)), "Should connect to final peer");
+                
+                // Delete the temporary Topic and close the session.
+                finalPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                finalPeer.ExpectEnd();
+                
+                await temporaryTopic.DeleteAsync();
+                
+                await session.CloseAsync();
+                
+                // Shut it down
+                finalPeer.ExpectClose();
+                await connection.CloseAsync();
+                
+                originalPeer.WaitForAllMatchersToComplete(2000);
+                finalPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestConsumerCanReceivesMessagesWhenConnectionLostDuringAutoAck()
+        {
+            using (TestAmqpPeer originalPeer = new TestAmqpPeer())
+            using (TestAmqpPeer finalPeer = new TestAmqpPeer())
+            {
+                ManualResetEvent consumerReady = new ManualResetEvent(false);
+                ManualResetEvent originalConnected = new ManualResetEvent(false);
+                ManualResetEvent finalConnected = new ManualResetEvent(false);
+
+                // Connect to the first peer
+                originalPeer.ExpectSaslAnonymous();
+                originalPeer.ExpectOpen();
+                originalPeer.ExpectBegin();
+                originalPeer.ExpectBegin();
+
+                NmsConnection connection = await EstablishAnonymousConnection(originalPeer, finalPeer);
+
+                Mock<INmsConnectionListener> connectionListener = new Mock<INmsConnectionListener>();
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionEstablished(It.IsAny<Uri>()))
+                    .Callback(() => { originalConnected.Set(); });
+
+                connectionListener
+                    .Setup(listener => listener.OnConnectionRestored(It.IsAny<Uri>()))
+                    .Callback(() => { finalConnected.Set(); });
+
+                connection.AddConnectionListener(connectionListener.Object);
+
+                await connection.StartAsync();
+
+                Assert.True(originalConnected.WaitOne(TimeSpan.FromSeconds(5)), "Should connect to original peer");
+
+                originalPeer.ExpectReceiverAttach();
+                originalPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), 1);
+                originalPeer.RunAfterLastHandler(() => consumerReady.WaitOne(TimeSpan.FromSeconds(2)));
+                originalPeer.DropAfterLastMatcher();
+
+                // Post Failover Expectations of FinalPeer
+                finalPeer.ExpectSaslAnonymous();
+                finalPeer.ExpectOpen();
+                finalPeer.ExpectBegin();
+                finalPeer.ExpectBegin();
+                finalPeer.ExpectReceiverAttach();
+                finalPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), 1);
+                finalPeer.ExpectDispositionThatIsAcceptedAndSettled();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                IMessageConsumer messageConsumer = await session.CreateConsumerAsync(queue);
+                CountdownEvent msgReceivedLatch = new CountdownEvent(2);
+                messageConsumer.Listener += message =>
+                {
+                    if (msgReceivedLatch.CurrentCount == 2)
+                    {
+                        consumerReady.Set();
+                        finalConnected.WaitOne(2000);
+                    }
+
+                    msgReceivedLatch.Signal();
+                };
+
+                finalPeer.WaitForAllMatchersToComplete(5000);
+
+                Assert.IsTrue(msgReceivedLatch.Wait(TimeSpan.FromSeconds(10)), $"Expected 2 messages, but got {2 - msgReceivedLatch.CurrentCount}");
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateProducerFailsWhenLinkRefused()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                testPeer.ExpectSaslAnonymous();
+                testPeer.ExpectOpen();
+                testPeer.ExpectBegin();
+
+                NmsConnection connection = await EstablishAnonymousConnection(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                string topicName = "myTopic";
+                ITopic topic = await session.GetTopicAsync(topicName);
+
+                // Expect a link to a topic node, which we will then refuse
+                testPeer.ExpectSenderAttach(targetMatcher: x =>
+                {
+                    Target target = (Target) x;
+
+                    Assert.AreEqual(topicName, target.Address);
+                    Assert.IsFalse(target.Dynamic);
+                    Assert.AreEqual((uint) TerminusDurability.NONE, target.Durable);
+                }, sourceMatcher: Assert.NotNull, refuseLink: true);
+
+                //Expect the detach response to the test peer closing the producer link after refusal.
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: false, replyClosed: false);
+
+                Assert.CatchAsync<NMSException>(async () => await session.CreateProducerAsync(topic));
+
+                // Shut it down
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000), Category("Windows")]
+        public async Task TestConnectionInterruptedInvokedWhenConnectionToBrokerLost()
+        {
+            using (TestAmqpPeer originalPeer = new TestAmqpPeer())
+            {
+                ManualResetEvent connectionInterruptedInvoked = new ManualResetEvent(false);
+
+                originalPeer.ExpectSaslAnonymous();
+                originalPeer.ExpectOpen();
+                originalPeer.ExpectBegin();
+                originalPeer.ExpectBegin();
+
+                NmsConnection connection = await EstablishAnonymousConnection(originalPeer);
+
+                connection.ConnectionInterruptedListener += () => connectionInterruptedInvoked.Set();
+
+                await connection.StartAsync();
+                
+                originalPeer.Close();
+
+                Assert.IsTrue(connectionInterruptedInvoked.WaitOne(TimeSpan.FromSeconds(10)));
+            }
+        }
+        
+        [Test, Timeout(20_000), Category("Windows")]
+        public async Task TestConnectionResumedInvokedWhenConnectionToBrokerLost()
+        {
+            using (TestAmqpPeer originalPeer = new TestAmqpPeer())
+            using (TestAmqpPeer finalPeer = new TestAmqpPeer())
+            {
+                ManualResetEvent connectionResumedInvoked = new ManualResetEvent(false);
+
+                originalPeer.ExpectSaslAnonymous();
+                originalPeer.ExpectOpen();
+                originalPeer.ExpectBegin();
+                originalPeer.ExpectBegin();
+
+                finalPeer.ExpectSaslAnonymous();
+                finalPeer.ExpectOpen();
+                finalPeer.ExpectBegin();
+                finalPeer.ExpectBegin();
+
+                NmsConnection connection = await EstablishAnonymousConnection(originalPeer, finalPeer);
+
+                connection.ConnectionResumedListener += () => connectionResumedInvoked.Set();
+
+                await connection.StartAsync();
+                
+                originalPeer.Close();
+                Assert.IsTrue(connectionResumedInvoked.WaitOne(TimeSpan.FromSeconds(10)));
+            }
+        }
+
+        private Task<NmsConnection> EstablishAnonymousConnection(params TestAmqpPeer[] peers)
+        {
+            return EstablishAnonymousConnection(null, null, peers);
+        }
+
+        private Task<NmsConnection> EstablishAnonymousConnection(string failoverParams, params TestAmqpPeer[] peers)
+        {
+            return EstablishAnonymousConnection(null, failoverParams, peers);
+        }
+
+        private async Task<NmsConnection> EstablishAnonymousConnection(string connectionParams, string failoverParams, params TestAmqpPeer[] peers)
+        {
+            if (peers.Length == 0)
+            {
+                throw new ArgumentException("No test peers were given, at least 1 required");
+            }
+
+            string remoteUri = "failover:(";
+            bool first = true;
+            foreach (TestAmqpPeer peer in peers)
+            {
+                if (!first)
+                {
+                    remoteUri += ",";
+                }
+
+                remoteUri += CreatePeerUri(peer, connectionParams);
+                first = false;
+            }
+
+            if (failoverParams == null)
+            {
+                remoteUri += ")?failover.maxReconnectAttempts=10";
+            }
+            else
+            {
+                remoteUri += ")?" + failoverParams;
+            }
+
+            NmsConnectionFactory factory = new NmsConnectionFactory(remoteUri);
+            return (NmsConnection) await factory.CreateConnectionAsync();
+        }
+
+        private string CreatePeerUri(TestAmqpPeer peer, string parameters = null)
+        {
+            return $"amqp://127.0.0.1:{peer.ServerPort}/{(parameters != null ? "?" + parameters : "")}";
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Apache-NMS-AMQP-Test/Integration/Async/MessageDeliveryTimeTest.cs b/test/Apache-NMS-AMQP-Test/Integration/Async/MessageDeliveryTimeTest.cs
new file mode 100644
index 0000000..157b159
--- /dev/null
+++ b/test/Apache-NMS-AMQP-Test/Integration/Async/MessageDeliveryTimeTest.cs
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Threading.Tasks;
+using Amqp.Framing;
+using Amqp.Types;
+using Apache.NMS;
+using Apache.NMS.AMQP.Util;
+using NMS.AMQP.Test.TestAmqp;
+using NUnit.Framework;
+
+namespace NMS.AMQP.Test.Integration.Async
+{
+    [TestFixture]
+    public class MessageDeliveryTimeTestAsync : IntegrationTestFixture
+    {
+        [Test, Timeout(20000)]
+        public async Task TestReceiveMessageWithoutDeliveryTimeSet()
+        {
+            await DoReceiveMessageDeliveryTime(null, null);
+        }
+
+        [Test, Timeout(20000)]
+        public async Task TestDeliveryTimeIsDateTime()
+        {
+            DateTime deliveryTime = DateTimeOffset.FromUnixTimeMilliseconds(CurrentTimeInMillis() + 12345).DateTime.ToUniversalTime();
+            await DoReceiveMessageDeliveryTime(deliveryTime, deliveryTime);
+        }
+
+        [Test, Timeout(20000)]
+        public async Task TestDeliveryTimeIsULong()
+        {
+            ulong deliveryTime = (ulong) (CurrentTimeInMillis() + 12345);
+            await DoReceiveMessageDeliveryTime(deliveryTime, DateTimeOffset.FromUnixTimeMilliseconds((long) deliveryTime).DateTime);
+        }
+
+        [Test, Timeout(20000)]
+        public async Task TestDeliveryTimeIsLong()
+        {
+            long deliveryTime = (CurrentTimeInMillis() + 12345);
+            await DoReceiveMessageDeliveryTime(deliveryTime, DateTimeOffset.FromUnixTimeMilliseconds(deliveryTime).DateTime);
+        }
+
+        [Test, Timeout(20000)]
+        public async Task TestDeliveryTimeIsInt()
+        {
+            int deliveryTime = (int) (CurrentTimeInMillis() + 12345);
+            await DoReceiveMessageDeliveryTime(deliveryTime, DateTimeOffset.FromUnixTimeMilliseconds(deliveryTime).DateTime);
+        }
+
+        [Test, Timeout(20000)]
+        public async Task TestDeliveryTimeIsUInt()
+        {
+            uint deliveryTime = (uint) (CurrentTimeInMillis() + 12345);
+            await DoReceiveMessageDeliveryTime(deliveryTime, DateTimeOffset.FromUnixTimeMilliseconds(deliveryTime).DateTime);
+        }
+
+        private long CurrentTimeInMillis()
+        {
+            return new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds();
+        }
+
+        private async Task DoReceiveMessageDeliveryTime(object setDeliveryTimeAnnotation, DateTime? expectedDeliveryTime)
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+                testPeer.ExpectBegin();
+                var session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                var queue = await session.GetQueueAsync("myQueue");
+
+                var message = CreateMessageWithNullContent();
+                if (setDeliveryTimeAnnotation != null)
+                {
+                    message.MessageAnnotations = message.MessageAnnotations ?? new MessageAnnotations();
+                    message.MessageAnnotations[SymbolUtil.NMS_DELIVERY_TIME] = setDeliveryTimeAnnotation;
+                }
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message);
+                testPeer.ExpectDisposition(true, (deliveryState) => { });
+
+                DateTime startingTimeFrom = DateTime.UtcNow;
+                var messageConsumer = await session.CreateConsumerAsync(queue);
+                var receivedMessage = await messageConsumer.ReceiveAsync(TimeSpan.FromMilliseconds(3000));
+                DateTime receivingTime = DateTime.UtcNow;
+
+                testPeer.WaitForAllMatchersToComplete(3000);
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(3000);
+
+                Assert.IsNotNull(receivedMessage);
+                if (expectedDeliveryTime != null)
+                {
+                    Assert.AreEqual(receivedMessage.NMSDeliveryTime, expectedDeliveryTime.Value);
+                }
+                else
+                {
+                    Assert.LessOrEqual(receivedMessage.NMSDeliveryTime, receivingTime);
+                    Assert.GreaterOrEqual(receivedMessage.NMSDeliveryTime, startingTimeFrom);
+                }
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestDeliveryDelayNotSupportedThrowsException()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await base.EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+                Assert.Throws<NotSupportedException>(() => producer.DeliveryDelay = TimeSpan.FromMinutes(17));
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestDeliveryDelayHasItsReflectionInAmqpAnnotations()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                // Determine current time
+                TimeSpan deliveryDelay = TimeSpan.FromMinutes(17);
+                long currentUnixEpochTime = new DateTimeOffset(DateTime.UtcNow + deliveryDelay).ToUnixTimeMilliseconds();
+                long currentUnixEpochTime2 = new DateTimeOffset(DateTime.UtcNow + deliveryDelay + deliveryDelay).ToUnixTimeMilliseconds();
+
+                IConnection connection = await base.EstablishConnectionAsync(testPeer,
+                    serverCapabilities: new Symbol[] {SymbolUtil.OPEN_CAPABILITY_DELAYED_DELIVERY, SymbolUtil.OPEN_CAPABILITY_SOLE_CONNECTION_FOR_CONTAINER});
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+                producer.DeliveryDelay = deliveryDelay;
+
+                // Create and transfer a new message
+                testPeer.ExpectTransfer(message =>
+                {
+                    Assert.GreaterOrEqual((long) message.MessageAnnotations[SymbolUtil.NMS_DELIVERY_TIME], currentUnixEpochTime);
+                    Assert.Less((long) message.MessageAnnotations[SymbolUtil.NMS_DELIVERY_TIME], currentUnixEpochTime2);
+
+                    Assert.IsTrue(message.Header.Durable);
+                });
+                testPeer.ExpectClose();
+
+                ITextMessage textMessage = await session.CreateTextMessageAsync();
+
+                await producer.SendAsync(textMessage);
+                Assert.AreEqual(MsgDeliveryMode.Persistent, textMessage.NMSDeliveryMode);
+
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Apache-NMS-AMQP-Test/Integration/Async/MessageExpirationIntegrationTest.cs b/test/Apache-NMS-AMQP-Test/Integration/Async/MessageExpirationIntegrationTest.cs
new file mode 100644
index 0000000..427acdd
--- /dev/null
+++ b/test/Apache-NMS-AMQP-Test/Integration/Async/MessageExpirationIntegrationTest.cs
@@ -0,0 +1,252 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Amqp.Framing;
+using Apache.NMS;
+using NMS.AMQP.Test.TestAmqp;
+using NUnit.Framework;
+
+namespace NMS.AMQP.Test.Integration.Async
+{
+    [TestFixture]
+    public class MessageExpirationIntegrationTestAsync : IntegrationTestFixture
+    {
+        [Test, Timeout(20_000)]
+        public async Task TestIncomingExpiredMessageGetsFiltered()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                // Expected the consumer to attach and send credit, then send it an
+                // already-expired message followed by a live message.
+                testPeer.ExpectReceiverAttach();
+                string expiredMsgContent = "already-expired";
+                Amqp.Message message = CreateExpiredMessage(expiredMsgContent);
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: message);
+
+                string liveMsgContent = "valid";
+                testPeer.SendTransferToLastOpenedLinkOnLastOpenedSession(message: new Amqp.Message() { BodySection = new AmqpValue() { Value = liveMsgContent } }, nextIncomingId: 2);
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+
+                // Call receive, expect the first message to be filtered due to expiry,
+                // and the second message to be given to the test app and accepted.
+                Action<DeliveryState> modifiedMatcher = state =>
+                {
+                    var modified = state as Modified;
+                    Assert.IsNotNull(modified);
+                    Assert.IsTrue(modified.DeliveryFailed);
+                    Assert.IsTrue(modified.UndeliverableHere);
+                };
+                testPeer.ExpectDisposition(settled: true, stateMatcher: modifiedMatcher, firstDeliveryId: 1, lastDeliveryId: 1);
+                testPeer.ExpectDisposition(settled: true, stateMatcher: Assert.IsInstanceOf<Accepted>, firstDeliveryId: 2, lastDeliveryId: 2);
+
+                IMessage m = await consumer.ReceiveAsync(TimeSpan.FromMilliseconds(3000));
+                Assert.NotNull(m, "Message should have been received");
+                Assert.IsInstanceOf<ITextMessage>(m);
+                Assert.AreEqual(liveMsgContent, (m as ITextMessage).Text, "Unexpected message content");
+
+                // Verify the other message is not there. Will drain to be sure there are no messages.
+                Assert.IsNull(await consumer.ReceiveAsync(TimeSpan.FromMilliseconds(10)), "Message should not have been received");
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(3000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestIncomingExpiredMessageGetsConsumedWhenFilterDisabled()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer, "?nms.localMessageExpiry=false");
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                // Expected the consumer to attach and send credit, then send it an
+                // already-expired message followed by a live message.
+                testPeer.ExpectReceiverAttach();
+
+                string expiredMsgContent = "already-expired";
+                Amqp.Message message = CreateExpiredMessage(expiredMsgContent);
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: message);
+
+                string liveMsgContent = "valid";
+                testPeer.SendTransferToLastOpenedLinkOnLastOpenedSession(message: new Amqp.Message() { BodySection = new AmqpValue() { Value = liveMsgContent } }, nextIncomingId: 2);
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+
+                // Call receive, expect the expired message as we disabled local expiry.
+                testPeer.ExpectDisposition(settled: true, stateMatcher: Assert.IsInstanceOf<Accepted>, firstDeliveryId: 1, lastDeliveryId: 1);
+
+                IMessage m = await consumer.ReceiveAsync(TimeSpan.FromMilliseconds(3000));
+                Assert.NotNull(m, "Message should have been received");
+                Assert.IsInstanceOf<ITextMessage>(m);
+                Assert.AreEqual(expiredMsgContent, ((ITextMessage) m).Text, "Unexpected message content");
+
+                // Verify the other message is there
+                testPeer.ExpectDisposition(settled: true, stateMatcher: Assert.IsInstanceOf<Accepted>, firstDeliveryId: 2, lastDeliveryId: 2);
+
+                m = await consumer.ReceiveAsync(TimeSpan.FromMilliseconds(3000));
+                Assert.NotNull(m, "Message should have been received");
+                Assert.IsInstanceOf<ITextMessage>(m);
+                Assert.AreEqual(liveMsgContent, ((ITextMessage) m).Text, "Unexpected message content");
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(3000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestIncomingExpiredMessageGetsFilteredAsync()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                // Expected the consumer to attach and send credit, then send it an
+                // already-expired message followed by a live message.
+                testPeer.ExpectReceiverAttach();
+
+                string expiredMsgContent = "already-expired";
+                Amqp.Message message = CreateExpiredMessage(expiredMsgContent);
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: message);
+
+                string liveMsgContent = "valid";
+                testPeer.SendTransferToLastOpenedLinkOnLastOpenedSession(message: new Amqp.Message() { BodySection = new AmqpValue() { Value = liveMsgContent } }, nextIncomingId: 2);
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+
+                // Add message listener, expect the first message to be filtered due to expiry,
+                // and the second message to be given to the test app and accepted.
+                Action<DeliveryState> modifiedMatcher = state =>
+                {
+                    var modified = state as Modified;
+                    Assert.IsNotNull(modified);
+                    Assert.IsTrue(modified.DeliveryFailed);
+                    Assert.IsTrue(modified.UndeliverableHere);
+                };
+                testPeer.ExpectDisposition(settled: true, stateMatcher: modifiedMatcher, firstDeliveryId: 1, lastDeliveryId: 1);
+                testPeer.ExpectDisposition(settled: true, stateMatcher: Assert.IsInstanceOf<Accepted>, firstDeliveryId: 2, lastDeliveryId: 2);
+
+
+                ManualResetEvent success = new ManualResetEvent(false);
+                ManualResetEvent listenerFailure = new ManualResetEvent(false);
+
+                consumer.Listener += m =>
+                {
+                    if (liveMsgContent.Equals(((ITextMessage) m).Text))
+                        success.Set();
+                    else
+                        listenerFailure.Set();
+                };
+
+                Assert.True(success.WaitOne(TimeSpan.FromSeconds(5)), "didn't get expected message");
+                Assert.False(listenerFailure.WaitOne(TimeSpan.FromMilliseconds(100)), "Received message when message should not have been received");
+
+                testPeer.WaitForAllMatchersToComplete(3000);
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(3000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestIncomingExpiredMessageGetsConsumedWhenFilterDisabledAsync()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer, "?nms.localMessageExpiry=false");
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                // Expected the consumer to attach and send credit, then send it an
+                // already-expired message followed by a live message.
+                testPeer.ExpectReceiverAttach();
+
+                string expiredMsgContent = "already-expired";
+                Amqp.Message message = CreateExpiredMessage(expiredMsgContent);
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: message);
+
+                string liveMsgContent = "valid";
+                testPeer.SendTransferToLastOpenedLinkOnLastOpenedSession(message: new Amqp.Message() { BodySection = new AmqpValue() { Value = liveMsgContent } }, nextIncomingId: 2);
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+                
+                // Add message listener, expect both messages as the filter is disabled
+                testPeer.ExpectDisposition(settled: true, stateMatcher: Assert.IsInstanceOf<Accepted>, firstDeliveryId:1, lastDeliveryId:1);
+                testPeer.ExpectDisposition(settled: true, stateMatcher: Assert.IsInstanceOf<Accepted>, firstDeliveryId:2, lastDeliveryId:2);
+
+                CountdownEvent success = new CountdownEvent(2);
+
+                consumer.Listener += m =>
+                {
+                    if (expiredMsgContent.Equals(((ITextMessage) m).Text) || liveMsgContent.Equals(((ITextMessage) m).Text))
+                        success.Signal();
+                };
+                
+                Assert.IsTrue(success.Wait(TimeSpan.FromSeconds(5)), "Didn't get expected messages");
+                
+                testPeer.WaitForAllMatchersToComplete(3000);
+                
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(3000);
+            }
+        }
+
+        private static Amqp.Message CreateExpiredMessage(string value)
+        {
+            return new Amqp.Message
+            {
+                BodySection = new AmqpValue() { Value = value },
+                Properties = new Properties { AbsoluteExpiryTime = DateTime.UtcNow - TimeSpan.FromMilliseconds(100) }
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Apache-NMS-AMQP-Test/Integration/Async/NMSConsumerIntegrationTest.cs b/test/Apache-NMS-AMQP-Test/Integration/Async/NMSConsumerIntegrationTest.cs
new file mode 100644
index 0000000..a27b268
--- /dev/null
+++ b/test/Apache-NMS-AMQP-Test/Integration/Async/NMSConsumerIntegrationTest.cs
@@ -0,0 +1,965 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Amqp.Framing;
+using Amqp.Types;
+using Apache.NMS;
+using Apache.NMS.AMQP.Message;
+using Apache.NMS.AMQP.Util;
+using NMS.AMQP.Test.TestAmqp;
+using NUnit.Framework;
+
+namespace NMS.AMQP.Test.Integration.Async
+{
+    // Adapted from ConsumerIntegrationTest to use NMSContext
+    [TestFixture]
+    public class NMSConsumerIntegrationTestAsync : IntegrationTestFixture
+    {
+        [Test, Timeout(20_000)]
+        public async Task TestCloseConsumer()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+
+                IQueue queue = await context.GetQueueAsync("myQueue");
+                var consumer = await context.CreateConsumerAsync(queue);
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                await consumer.CloseAsync();
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        // TODO No connection Listener in NMSContext
+        // [Test, Timeout(20_000)]
+        // public async Task TestRemotelyCloseConsumer()
+        // {
+        //     Mock<INmsConnectionListener> mockConnectionListener = new Mock<INmsConnectionListener>();
+        //     string errorMessage = "buba";
+        //
+        //     using (TestAmqpPeer testPeer = new TestAmqpPeer())
+        //     {
+        //         ManualResetEvent consumerClosed = new ManualResetEvent(false);
+        //         ManualResetEvent exceptionFired = new ManualResetEvent(false);
+        //
+        //         mockConnectionListener
+        //             .Setup(listener => listener.OnConsumerClosed(It.IsAny<IMessageConsumer>(), It.IsAny<Exception>()))
+        //             .Callback(() => consumerClosed.Set());
+        //
+        //         var context = (NmsContext) EstablishNMSContext(testPeer);
+        //         context.ConnectionInterruptedListener += () => { consumerClosed.Set(); };// AddConnectionListener(mockConnectionListener.Object);}
+        //         // context.list ConnectionInterruptedListener += () => { consumerClosed.Set(); };// AddConnectionListener(mockConnectionListener.Object);}
+        //         context.ExceptionListener += exception => { exceptionFired.Set(); };
+        //
+        //         testPeer.ExpectBegin();
+        //         // ISession session = context.CreateSession(AcknowledgementMode.AutoAcknowledge);
+        //
+        //         // Create a consumer, then remotely end it afterwards.
+        //         testPeer.ExpectReceiverAttach();
+        //         testPeer.ExpectLinkFlow();
+        //         testPeer.RemotelyDetachLastOpenedLinkOnLastOpenedSession(expectDetachResponse: true, closed: true, errorType: AmqpError.RESOURCE_DELETED, errorMessage: errorMessage, delayBeforeSend: 400);
+        //
+        //         IQueue queue = context.GetQueue("myQueue");
+        //         var consumer = context.CreateConsumer(queue);
+        //         
+        //         
+        //         // Verify the consumer gets marked closed
+        //         testPeer.WaitForAllMatchersToComplete(1000);
+        //
+        //         Assert.True(consumerClosed.WaitOne(2000), "Consumer closed callback didn't trigger");
+        //         Assert.False(exceptionFired.WaitOne(20), "Exception listener shouldn't fire with no MessageListener");
+        //
+        //         // Try closing it explicitly, should effectively no-op in client.
+        //         // The test peer will throw during close if it sends anything.
+        //         consumer.Close();
+        //     }
+        // }
+
+        // [Test, Timeout(20_000)]
+        // public async Task TestRemotelyCloseConsumerWithMessageListenerFiresExceptionListener()
+        // {
+        //     Mock<INmsConnectionListener> mockConnectionListener = new Mock<INmsConnectionListener>();
+        //     string errorMessage = "buba";
+        //
+        //     using (TestAmqpPeer testPeer = new TestAmqpPeer())
+        //     {
+        //         ManualResetEvent consumerClosed = new ManualResetEvent(false);
+        //         ManualResetEvent exceptionFired = new ManualResetEvent(false);
+        //
+        //         mockConnectionListener
+        //             .Setup(listener => listener.OnConsumerClosed(It.IsAny<IMessageConsumer>(), It.IsAny<Exception>()))
+        //             .Callback(() => consumerClosed.Set());
+        //
+        //         NmsConnection connection = (NmsConnection) EstablishConnection(testPeer);
+        //         connection.AddConnectionListener(mockConnectionListener.Object);
+        //         connection.ExceptionListener += exception => { exceptionFired.Set(); };
+        //
+        //         testPeer.ExpectBegin();
+        //         ISession session = connection.CreateSession(AcknowledgementMode.AutoAcknowledge);
+        //
+        //         // Create a consumer, then remotely end it afterwards.
+        //         testPeer.ExpectReceiverAttach();
+        //         testPeer.ExpectLinkFlow();
+        //         testPeer.RemotelyDetachLastOpenedLinkOnLastOpenedSession(expectDetachResponse: true, closed: true, errorType: AmqpError.RESOURCE_DELETED, errorMessage: errorMessage, 10);
+        //
+        //         IQueue queue = session.GetQueue("myQueue");
+        //         IMessageConsumer consumer = session.CreateConsumer(queue);
+        //
+        //         consumer.Listener += message => { };
+        //
+        //         // Verify the consumer gets marked closed
+        //         testPeer.WaitForAllMatchersToComplete(1000);
+        //
+        //         Assert.True(consumerClosed.WaitOne(2000), "Consumer closed callback didn't trigger");
+        //         Assert.True(exceptionFired.WaitOne(2000), "Exception listener should have fired with a MessageListener");
+        //
+        //         // Try closing it explicitly, should effectively no-op in client.
+        //         // The test peer will throw during close if it sends anything.
+        //         consumer.Close();
+        //     }
+        // }
+
+        [Test, Timeout(20_000)]
+        public async Task TestReceiveMessageWithReceiveZeroTimeout()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                IQueue queue = await context.GetQueueAsync("myQueue");
+
+                testPeer.ExpectReceiverAttach();
+
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: new Amqp.Message() { BodySection = new AmqpValue() { Value = null } }, count: 1);
+                testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+
+                var consumer = await context.CreateConsumerAsync(queue);
+                IMessage message = await consumer.ReceiveAsync();
+                Assert.NotNull(message, "A message should have been received");
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(10000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestExceptionInOnMessageReleasesInAutoAckMode()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                IQueue queue = await context.GetQueueAsync("myQueue");
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: new Amqp.Message() { BodySection = new AmqpValue() { Value = null } }, count: 1);
+                testPeer.ExpectDispositionThatIsReleasedAndSettled();
+
+                var consumer = await context.CreateConsumerAsync(queue);
+                consumer.Listener += message => throw new Exception();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(10000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCloseDurableTopicSubscriberDetachesWithCloseFalse()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+                
+                string topicName = "myTopic";
+                string subscriptionName = "mySubscription";
+                ITopic topic = await context.GetTopicAsync(topicName);
+
+                testPeer.ExpectDurableSubscriberAttach(topicName, subscriptionName);
+                testPeer.ExpectLinkFlow();
+
+                var durableConsumer = await context.CreateDurableConsumerAsync(topic, subscriptionName, null, false);
+
+                testPeer.ExpectDetach(expectClosed: false, sendResponse: true, replyClosed: false);
+                await durableConsumer.CloseAsync();
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestConsumerReceiveThrowsIfConnectionLost()
+        {
+            await DoTestConsumerReceiveThrowsIfConnectionLost(false);
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestConsumerTimedReceiveThrowsIfConnectionLost()
+        {
+            await DoTestConsumerReceiveThrowsIfConnectionLost(true);
+        }
+
+        private async Task DoTestConsumerReceiveThrowsIfConnectionLost(bool useTimeout)
+        {
+            ManualResetEvent consumerReady = new ManualResetEvent(false);
+
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+
+                testPeer.ExpectBegin();
+
+                IQueue queue = await context.GetQueueAsync("queue");
+                await context.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+                testPeer.RunAfterLastHandler(() => { consumerReady.WaitOne(2000); });
+                testPeer.DropAfterLastMatcher(delay: 10);
+
+                var consumer = await context.CreateConsumerAsync(queue);
+                consumerReady.Set();
+
+                try
+                {
+                    if (useTimeout)
+                    {
+                        await consumer.ReceiveAsync(TimeSpan.FromMilliseconds(10_0000));
+                    }
+                    else
+                    {
+                        await consumer.ReceiveAsync();
+                    }
+
+
+                    Assert.Fail("An exception should have been thrown");
+                }
+                catch (NMSException)
+                {
+                    // Expected
+                }
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        //  TODO No connection Listener in context
+        // [Test, Timeout(20_000)]
+        // public async Task TestConsumerReceiveNoWaitThrowsIfConnectionLost()
+        // {
+        //     ManualResetEvent disconnected = new ManualResetEvent(false);
+        //
+        //     using (TestAmqpPeer testPeer = new TestAmqpPeer())
+        //     {
+        //         NmsContext context = (NmsContext) EstablishNMSContext(testPeer);
+        //         Mock<INmsConnectionListener> connectionListener = new Mock<INmsConnectionListener>();
+        //
+        //         connectionListener
+        //             .Setup(listener => listener.OnConnectionFailure(It.IsAny<NMSException>()))
+        //             .Callback(() => { disconnected.Set(); });
+        //
+        //         context.AddConnectionListener(connectionListener.Object);
+        //
+        //         context.Start();
+        //
+        //         testPeer.ExpectBegin();
+        //
+        //         IQueue queue = context.GetQueue("queue");
+        //
+        //         testPeer.ExpectReceiverAttach();
+        //         testPeer.ExpectLinkFlow();
+        //         testPeer.RemotelyCloseConnection(expectCloseResponse: true, errorCondition: ConnectionError.CONNECTION_FORCED, errorMessage: "buba");
+        //
+        //         var consumer = context.CreateConsumer(queue);
+        //
+        //         Assert.True(disconnected.WaitOne(), "Connection should be disconnected");
+        //
+        //         try
+        //         {
+        //             consumer.ReceiveNoWait();
+        //             Assert.Fail("An exception should have been thrown");
+        //         }
+        //         catch (NMSException)
+        //         {
+        //             // Expected
+        //         }
+        //     }
+        // }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSetMessageListenerAfterStartAndSend()
+        {
+            int messageCount = 4;
+            CountdownEvent latch = new CountdownEvent(messageCount);
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                await context.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), messageCount);
+
+                var consumer = await context.CreateConsumerAsync(destination);
+
+                for (int i = 0; i < messageCount; i++)
+                {
+                    testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+                }
+
+                consumer.Listener += message => latch.Signal();
+
+                Assert.True(latch.Wait(4000), "Messages not received within given timeout. Count remaining: " + latch.CurrentCount);
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+
+                await consumer.CloseAsync();
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSyncReceiveFailsWhenListenerSet()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+
+                testPeer.ExpectBegin();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+
+                var consumer = await context.CreateConsumerAsync(destination);
+
+                consumer.Listener += message => { };
+
+                Assert.CatchAsync<NMSException>(async () => await consumer.ReceiveAsync(), "Should have thrown an exception.");
+                Assert.CatchAsync<NMSException>(async () => await consumer.ReceiveAsync(TimeSpan.FromMilliseconds(1000)), "Should have thrown an exception.");
+                Assert.CatchAsync<NMSException>(async () => consumer.ReceiveNoWait(), "Should have thrown an exception.");
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateProducerInOnMessage()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                IQueue outbound = await context.GetQueueAsync("ForwardDest");
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), 1);
+
+                testPeer.ExpectSenderAttach();
+                testPeer.ExpectTransfer(messageMatcher: Assert.NotNull);
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+
+                testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+
+                var consumer = await context.CreateConsumerAsync(destination);
+
+                consumer.Listener += message =>
+                {
+                    var producer = context.CreateProducer();
+                    producer.Send(outbound, message);
+                    producer.Close();
+                };
+
+                testPeer.WaitForAllMatchersToComplete(10_000);
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestMessageListenerCallsConnectionCloseThrowsIllegalStateException()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                await context.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), 1);
+
+                var consumer = await context.CreateConsumerAsync(destination);
+
+                testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+
+                ManualResetEvent latch = new ManualResetEvent(false);
+                Exception exception = null;
+                consumer.Listener += message =>
+                {
+                    try
+                    {
+                        context.Close();
+                    }
+                    catch (Exception e)
+                    {
+                        exception = e;
+                    }
+
+                    latch.Set();
+                };
+
+                Assert.True(latch.WaitOne(4000), "Messages not received within given timeout.");
+                Assert.IsNotNull(exception);
+                Assert.IsInstanceOf<IllegalStateException>(exception);
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                await consumer.CloseAsync();
+
+                testPeer.ExpectEnd();
+                // testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestMessageListenerCallsConnectionStopThrowsIllegalStateException()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                await context.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), 1);
+
+                var consumer = await context.CreateConsumerAsync(destination);
+
+                testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+
+                ManualResetEvent latch = new ManualResetEvent(false);
+                Exception exception = null;
+                consumer.Listener += message =>
+                {
+                    try
+                    {
+                        context.Stop();
+                    }
+                    catch (Exception e)
+                    {
+                        exception = e;
+                    }
+
+                    latch.Set();
+                };
+
+                Assert.True(latch.WaitOne(3000), "Messages not received within given timeout.");
+                Assert.IsNotNull(exception);
+                Assert.IsInstanceOf<IllegalStateException>(exception);
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                await consumer.CloseAsync();
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestMessageListenerCallsSessionCloseThrowsIllegalStateException()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                await context.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), 1);
+
+                var consumer = await context.CreateConsumerAsync(destination);
+
+                testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+
+                ManualResetEvent latch = new ManualResetEvent(false);
+                Exception exception = null;
+                consumer.Listener += message =>
+                {
+                    try
+                    {
+                        context.Close();
+                    }
+                    catch (Exception e)
+                    {
+                        exception = e;
+                    }
+
+                    latch.Set();
+                };
+
+                Assert.True(latch.WaitOne(3000), "Messages not received within given timeout.");
+                Assert.IsNotNull(exception);
+                Assert.IsInstanceOf<IllegalStateException>(exception);
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                await consumer.CloseAsync();
+
+                testPeer.ExpectEnd();
+                // testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        // TODO: To be fixed
+        [Test, Timeout(20_000), Ignore("Ignore")]
+        public async Task TestMessageListenerClosesItsConsumer()
+        {
+            var latch = new ManualResetEvent(false);
+            var exceptionListenerFired = new ManualResetEvent(false);
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                context.ExceptionListener += _ => exceptionListenerFired.Set();
+
+                testPeer.ExpectBegin();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                await context.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), 1);
+
+                var consumer = await context.CreateConsumerAsync(destination);
+
+                testPeer.ExpectLinkFlow(drain: true, sendDrainFlowResponse: true, creditMatcher: credit => Assert.AreEqual(99, credit)); // Not sure if expected credit is right
+                testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+
+                Exception exception = null;
+                consumer.Listener += message =>
+                {
+                    try
+                    {
+                        consumer.Close();
+                        latch.Set();
+                    }
+                    catch (Exception e)
+                    {
+                        exception = e;
+                    }
+                };
+
+                Assert.True(latch.WaitOne(TimeSpan.FromMilliseconds(1000)), "Process not completed within given timeout");
+                Assert.IsNull(exception, "No error expected during close");
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                Assert.False(exceptionListenerFired.WaitOne(20), "Exception listener shouldn't have fired");
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestRecoverOrderingWithAsyncConsumer()
+        {
+            ManualResetEvent latch = new ManualResetEvent(false);
+            Exception asyncError = null;
+
+            int recoverCount = 5;
+            int messageCount = 8;
+            int testPayloadLength = 255;
+            string payload = Encoding.UTF8.GetString(new byte[testPayloadLength]);
+
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer, acknowledgementMode:AcknowledgementMode.ClientAcknowledge);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                await context.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(
+                    message: new Amqp.Message() { BodySection = new AmqpValue() { Value = payload } },
+                    count: messageCount,
+                    drain: false,
+                    nextIncomingId: 1,
+                    addMessageNumberProperty: true,
+                    sendDrainFlowResponse: false,
+                    sendSettled: false,
+                    creditMatcher: credit => Assert.Greater(credit, messageCount)
+                );
+
+                var consumer = await context.CreateConsumerAsync(destination);
+                
+                bool complete = false;
+                int messageSeen = 0;
+                int expectedIndex = 0;
+                consumer.Listener += message =>
+                {
+                    if (complete)
+                    {
+                        return;
+                    }
+
+                    try
+                    {
+                        int actualIndex = message.Properties.GetInt(TestAmqpPeer.MESSAGE_NUMBER);
+                        Assert.AreEqual(expectedIndex, actualIndex, "Received Message Out Of Order");
+
+                        // don't ack the message until we receive it X times
+                        if (messageSeen < recoverCount)
+                        {
+                            context.Recover();
+                            messageSeen++;
+                        }
+                        else
+                        {
+                            messageSeen = 0;
+                            expectedIndex++;
+
+                            // Have the peer expect the accept the disposition (1-based, hence pre-incremented).
+                            testPeer.ExpectDisposition(settled: true,
+                                stateMatcher: state => Assert.AreEqual(state.Descriptor.Code, MessageSupport.ACCEPTED_INSTANCE.Descriptor.Code
+                                ));
+
+                            message.Acknowledge();
+
+                            if (expectedIndex == messageCount)
+                            {
+                                complete = true;
+                                latch.Set();
+                            }
+                        }
+                    }
+                    catch (Exception e)
+                    {
+                        complete = true;
+                        asyncError = e;
+                        latch.Set();
+                    }
+                };
+
+                Assert.True(latch.WaitOne(TimeSpan.FromSeconds(15)), "Messages not received within given timeout.");
+                Assert.IsNull(asyncError, "Unexpected exception");
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestConsumerCloseWaitsForAsyncDeliveryToComplete()
+        {
+            ManualResetEvent latch = new ManualResetEvent(false);
+
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                await context.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), count: 1);
+
+                var consumer = await context.CreateConsumerAsync(destination);
+
+                testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+
+                consumer.Listener += _ =>
+                {
+                    latch.Set();
+                    Task.Delay(TimeSpan.FromMilliseconds(100)).GetAwaiter().GetResult();
+                };
+
+                Assert.True(latch.WaitOne(TimeSpan.FromMilliseconds(3000)), "Messages not received within given timeout.");
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                await consumer.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSessionCloseWaitsForAsyncDeliveryToComplete()
+        {
+            ManualResetEvent latch = new ManualResetEvent(false);
+
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                await context.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), count: 1);
+
+                var consumer = await context.CreateConsumerAsync(destination);
+
+                testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+
+                consumer.Listener += _ =>
+                {
+                    latch.Set();
+                    Task.Delay(TimeSpan.FromMilliseconds(100)).GetAwaiter().GetResult();
+                };
+
+                Assert.True(latch.WaitOne(TimeSpan.FromMilliseconds(3000)), "Messages not received within given timeout.");
+
+                
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestConnectionCloseWaitsForAsyncDeliveryToComplete()
+        {
+            ManualResetEvent latch = new ManualResetEvent(false);
+
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                await context.StartAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), count: 1);
+
+                var consumer = await context.CreateConsumerAsync(destination);
+
+                testPeer.ExpectDispositionThatIsAcceptedAndSettled();
+
+                consumer.Listener += _ =>
+                {
+                    latch.Set();
+                    Task.Delay(TimeSpan.FromMilliseconds(100)).GetAwaiter().GetResult();
+                };
+
+                Assert.True(latch.WaitOne(TimeSpan.FromMilliseconds(3000)), "Messages not received within given timeout.");
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestRecoveredMessageShouldNotBeMutated()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer, acknowledgementMode:AcknowledgementMode.ClientAcknowledge);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                string originalPayload = "testMessage";
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: new Amqp.Message { BodySection = new AmqpValue() { Value = originalPayload } }, count: 1);
+
+                var consumer = await context.CreateConsumerAsync(destination);
+                NmsTextMessage message = await consumer.ReceiveAsync() as NmsTextMessage;
+                Assert.NotNull(message);
+                message.IsReadOnlyBody = false;
+                message.Text = message.Text + "Received";
+                await context.RecoverAsync();
+
+                ITextMessage recoveredMessage = await consumer.ReceiveAsync() as ITextMessage;
+                Assert.IsNotNull(recoveredMessage);
+                Assert.AreNotEqual(message.Text, recoveredMessage.Text);
+                Assert.AreEqual(originalPayload, recoveredMessage.Text);
+                Assert.AreNotSame(message, recoveredMessage);
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+        
+        [TestCaseSource("TestReceiveBodyCaseSource")]
+        [Timeout(20_000)]
+        public async Task TestReceiveBody<T>(T inputValue)
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(CreateMessageWithValueContent(inputValue));
+                testPeer.ExpectDisposition(true, _ => { } );
+                
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                var consumer = await context.CreateConsumerAsync(destination);
+
+                T body = await consumer.ReceiveBodyAsync<T>();
+                Assert.AreEqual(inputValue, body);
+                Assert.AreNotSame(inputValue, body);
+
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+        
+       
+
+        public static IEnumerable<object> TestReceiveBodyCaseSource()
+        {
+            yield return new Map()
+            {
+                ["Parameter1"] = "test",
+                ["Parameter2"] = 23423
+            };
+            yield return 1233;
+            yield return "test";
+            yield return (uint) 1233;
+            yield return (ulong) 1233;
+            yield return (long) -1233;
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Apache-NMS-AMQP-Test/Integration/Async/NMSContextIntegrationTest.cs b/test/Apache-NMS-AMQP-Test/Integration/Async/NMSContextIntegrationTest.cs
new file mode 100644
index 0000000..16940d4
--- /dev/null
+++ b/test/Apache-NMS-AMQP-Test/Integration/Async/NMSContextIntegrationTest.cs
@@ -0,0 +1,279 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Apache.NMS;
+using NMS.AMQP.Test.TestAmqp;
+using NUnit.Framework;
+
+namespace NMS.AMQP.Test.Integration.Async
+{
+    // Adapted from SessionIntegrationTest to use NMSContext
+    [TestFixture]
+    public class NMSContextIntegrationTestAsync : IntegrationTestFixture
+    {
+        [Test, Timeout(20_000)]
+        public async Task TestClose()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                INMSContext context = await EstablishNMSContextAsync(testPeer);
+
+                testPeer.ExpectClose();
+
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateProducer()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                testPeer.ExpectBegin();
+
+                testPeer.ExpectSenderAttach();
+
+                var producer = await context.CreateProducerAsync();
+
+                testPeer.ExpectDetach(true, true, true);
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+
+                await producer.CloseAsync();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateConsumer()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+
+                var consumer = await context.CreateConsumerAsync(await context.GetQueueAsync("myQueue"));
+
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateConsumerWithEmptySelector()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+
+                IQueue queue = await context.GetQueueAsync("myQueue");
+                await context.CreateConsumerAsync(queue, "");
+                await context.CreateConsumerAsync(queue, "", noLocal: false);
+
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateConsumerWithNullSelector()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+
+                IQueue queue = await context.GetQueueAsync("myQueue");
+                await context.CreateConsumerAsync(queue, null);
+                await context.CreateConsumerAsync(queue, null, noLocal: false);
+
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateDurableConsumer()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                string topicName = "myTopic";
+                ITopic topic = await context.GetTopicAsync(topicName);
+                string subscriptionName = "mySubscription";
+
+                testPeer.ExpectDurableSubscriberAttach(topicName, subscriptionName);
+                testPeer.ExpectLinkFlow();
+
+                var durableConsumer = await context.CreateDurableConsumerAsync(topic, subscriptionName, null, false);
+                Assert.NotNull(durableConsumer, "MessageConsumer object was null");
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateTemporaryQueue()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+
+                testPeer.ExpectBegin();
+
+                string dynamicAddress = "myTempQueueAddress";
+                testPeer.ExpectTempQueueCreationAttach(dynamicAddress);
+
+                ITemporaryQueue temporaryQueue = await context.CreateTemporaryQueueAsync();
+                Assert.NotNull(temporaryQueue, "TemporaryQueue object was null");
+                Assert.NotNull(temporaryQueue.QueueName, "TemporaryQueue queue name was null");
+                Assert.AreEqual(dynamicAddress, temporaryQueue.QueueName, "TemporaryQueue name not as expected");
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateTemporaryTopic()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+
+                testPeer.ExpectBegin();
+
+                string dynamicAddress = "myTempTopicAddress";
+                testPeer.ExpectTempTopicCreationAttach(dynamicAddress);
+
+                ITemporaryTopic temporaryTopic = await context.CreateTemporaryTopicAsync();
+                Assert.NotNull(temporaryTopic, "TemporaryTopic object was null");
+                Assert.NotNull(temporaryTopic.TopicName, "TemporaryTopic name was null");
+                Assert.AreEqual(dynamicAddress, temporaryTopic.TopicName, "TemporaryTopic name not as expected");
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateSharedConsumer()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                string topicName = "myTopic";
+                ITopic topic = await context.GetTopicAsync(topicName);
+                string subscriptionName = "mySubscription";
+
+                testPeer.ExpectSharedSubscriberAttach(topicName, subscriptionName);
+                testPeer.ExpectLinkFlow();
+
+                var durableConsumer = await context.CreateSharedConsumerAsync(topic, subscriptionName, null); //, false);
+                Assert.NotNull(durableConsumer, "MessageConsumer object was null");
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(20000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateSharedDurableConsumer()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                string topicName = "myTopic";
+                ITopic topic = await context.GetTopicAsync(topicName);
+                string subscriptionName = "mySubscription";
+
+                testPeer.ExpectSharedDurableSubscriberAttach(topicName, subscriptionName);
+                testPeer.ExpectLinkFlow();
+
+                var durableConsumer = await context.CreateSharedDurableConsumerAsync(topic, subscriptionName, null); //, false);
+                Assert.NotNull(durableConsumer, "MessageConsumer object was null");
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Apache-NMS-AMQP-Test/Integration/Async/NMSProducerIntegrationTest.cs b/test/Apache-NMS-AMQP-Test/Integration/Async/NMSProducerIntegrationTest.cs
new file mode 100644
index 0000000..4d5ca30
--- /dev/null
+++ b/test/Apache-NMS-AMQP-Test/Integration/Async/NMSProducerIntegrationTest.cs
@@ -0,0 +1,723 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Threading.Tasks;
+using Amqp.Framing;
+using Amqp.Types;
+using Apache.NMS;
+using Apache.NMS.AMQP.Util;
+using NMS.AMQP.Test.TestAmqp;
+using NUnit.Framework;
+
+namespace NMS.AMQP.Test.Integration.Async
+{
+    // Adapted from ProducerIntegrationTest to use NMSContext
+    [TestFixture]
+    public class NMSProducerIntegrationTestAsync : IntegrationTestFixture
+    {
+        private const long TICKS_PER_MILLISECOND = 10000;
+
+        [Test, Timeout(20_000)]
+        public async Task TestCloseSender()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await base.EstablishNMSContextAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                IQueue queue = await context.GetQueueAsync("myQueue");
+                var producer = await context.CreateProducerAsync();
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+
+                await producer.CloseAsync();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSentTextMessageCanBeModified()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await base.EstablishNMSContextAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                IQueue queue = await context.GetQueueAsync("myQueue");
+                var producer = await context.CreateProducerAsync();
+
+                // Create and transfer a new message
+                String text = "myMessage";
+                testPeer.ExpectTransfer(x => Assert.AreEqual(text, (x.BodySection as AmqpValue).Value));
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+
+                ITextMessage message = await context.CreateTextMessageAsync(text);
+                await producer.SendAsync(queue, message);
+
+                Assert.AreEqual(text, message.Text);
+                message.Text = text + text;
+                Assert.AreEqual(text + text, message.Text);
+
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestDefaultDeliveryModeProducesDurableMessages()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await base.EstablishNMSContextAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                IQueue queue = await context.GetQueueAsync("myQueue");
+                var producer = await context.CreateProducerAsync();
+
+                // Create and transfer a new message
+                testPeer.ExpectTransfer(message => Assert.IsTrue(message.Header.Durable));
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+
+                ITextMessage textMessage = await context.CreateTextMessageAsync();
+
+                await producer.SendAsync(queue, textMessage);
+                Assert.AreEqual(MsgDeliveryMode.Persistent, textMessage.NMSDeliveryMode);
+
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestProducerOverridesMessageDeliveryMode()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await base.EstablishNMSContextAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                IQueue queue = await context.GetQueueAsync("myQueue");
+                var producer = await context.CreateProducerAsync();
+
+                // Create and transfer a new message, explicitly setting the deliveryMode on the
+                // message (which applications shouldn't) to NON_PERSISTENT and sending it to check
+                // that the producer ignores this value and sends the message as PERSISTENT(/durable)
+                testPeer.ExpectTransfer(message => Assert.IsTrue(message.Header.Durable));
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+
+                ITextMessage textMessage = await context.CreateTextMessageAsync();
+                textMessage.NMSDeliveryMode = MsgDeliveryMode.NonPersistent;
+                Assert.AreEqual(MsgDeliveryMode.NonPersistent, textMessage.NMSDeliveryMode);
+
+                await producer.SendAsync(queue, textMessage);
+
+                Assert.AreEqual(MsgDeliveryMode.Persistent, textMessage.NMSDeliveryMode);
+
+                await context.CloseAsync();
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageNonPersistentProducerSetDurableFalse()
+        {
+            await DoSendingMessageNonPersistentTestImpl(true);
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageNonPersistentProducerOmitsHeader()
+        {
+            await DoSendingMessageNonPersistentTestImpl(false);
+        }
+
+        private async Task DoSendingMessageNonPersistentTestImpl(bool setPriority)
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                //Add capability to indicate support for ANONYMOUS-RELAY
+                Symbol[] serverCapabilities = {SymbolUtil.OPEN_CAPABILITY_ANONYMOUS_RELAY};
+                var context = await EstablishNMSContextAsync(testPeer, serverCapabilities: serverCapabilities);
+                testPeer.ExpectBegin();
+
+                string queueName = "myQueue";
+                Action<object> targetMatcher = t =>
+                {
+                    var target = t as Target;
+                    Assert.IsNotNull(target);
+                };
+
+
+                testPeer.ExpectSenderAttach(targetMatcher: targetMatcher, sourceMatcher: Assert.NotNull, senderSettled: false);
+
+                IQueue queue = await context.GetQueueAsync(queueName);
+                INMSProducer producer = await context.CreateProducerAsync();
+
+                byte priority = 5;
+                String text = "myMessage";
+                testPeer.ExpectTransfer(messageMatcher: message =>
+                    {
+                        if (setPriority)
+                        {
+                            Assert.IsFalse(message.Header.Durable);
+                            Assert.AreEqual(priority, message.Header.Priority);
+                        }
+
+                        Assert.AreEqual(text, (message.BodySection as AmqpValue).Value);
+                    }, stateMatcher: Assert.IsNull,
+                    settled: false,
+                    sendResponseDisposition: true,
+                    responseState: new Accepted(),
+                    responseSettled: true);
+
+                ITextMessage textMessage = await context.CreateTextMessageAsync(text);
+
+                producer.DeliveryMode = MsgDeliveryMode.NonPersistent;
+                if (setPriority)
+                    producer.Priority = (MsgPriority) priority;
+
+                await producer.SendAsync(queue, textMessage);
+
+                Assert.AreEqual(MsgDeliveryMode.NonPersistent, textMessage.NMSDeliveryMode, "Should have NonPersistent delivery mode set");
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageSetsNMSDestination()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                var producer = await context.CreateProducerAsync();
+
+                string text = "myMessage";
+                ITextMessage message = await context.CreateTextMessageAsync(text);
+
+                testPeer.ExpectTransfer(m => Assert.AreEqual(text, (m.BodySection as AmqpValue).Value));
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+
+                Assert.IsNull(message.NMSDestination, "Should not yet have a NMSDestination");
+
+                await producer.SendAsync(destination, message);
+
+                Assert.AreEqual(destination, message.NMSDestination, "Should have had NMSDestination set");
+
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageSetsNMSTimestamp()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                var producer = await context.CreateProducerAsync();
+
+                // Create matcher to expect the absolute-expiry-time field of the properties section to
+                // be set to a value greater than 'now'+ttl, within a delta.
+
+                DateTime creationLower = DateTime.UtcNow;
+                DateTime creationUpper = creationLower + TimeSpan.FromMilliseconds(3000);
+
+                var text = "myMessage";
+                testPeer.ExpectTransfer(m =>
+                {
+                    Assert.IsTrue(m.Header.Durable);
+                    Assert.That(m.Properties.CreationTime.Ticks, Is.GreaterThanOrEqualTo(creationLower.Ticks).Within(TICKS_PER_MILLISECOND));
+                    Assert.That(m.Properties.CreationTime.Ticks, Is.LessThanOrEqualTo(creationUpper.Ticks).Within(TICKS_PER_MILLISECOND));
+                    Assert.AreEqual(text, (m.BodySection as AmqpValue).Value);
+                });
+
+                ITextMessage message = await context.CreateTextMessageAsync(text);
+                await producer.SendAsync(destination, message);
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageSetsNMSExpirationRelatedAbsoluteExpiryAndTtlFields()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                var producer = await context.CreateProducerAsync();
+
+                uint ttl = 100_000;
+                DateTime currentTime = DateTime.UtcNow;
+                DateTime expirationLower = currentTime + TimeSpan.FromMilliseconds(ttl);
+                DateTime expirationUpper = currentTime + TimeSpan.FromMilliseconds(ttl) + TimeSpan.FromMilliseconds(5000);
+
+                // Create matcher to expect the absolute-expiry-time field of the properties section to
+                // be set to a value greater than 'now'+ttl, within a delta.
+                string text = "myMessage";
+                testPeer.ExpectTransfer(m =>
+                {
+                    Assert.IsTrue(m.Header.Durable);
+                    Assert.AreEqual(ttl, m.Header.Ttl);
+                    Assert.That(m.Properties.AbsoluteExpiryTime.Ticks, Is.GreaterThanOrEqualTo(expirationLower.Ticks).Within(TICKS_PER_MILLISECOND));
+                    Assert.That(m.Properties.AbsoluteExpiryTime.Ticks, Is.LessThanOrEqualTo(expirationUpper.Ticks).Within(TICKS_PER_MILLISECOND));
+                    Assert.AreEqual(text, (m.BodySection as AmqpValue).Value);
+                });
+
+                ITextMessage message = await context.CreateTextMessageAsync(text);
+                producer.TimeToLive = TimeSpan.FromMilliseconds(ttl);
+                producer.Priority = NMSConstants.defaultPriority;
+                producer.DeliveryMode = NMSConstants.defaultDeliveryMode;
+                await producer.SendAsync(destination, message);
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestMessagesAreProducedWithProperDefaultPriorityWhenNoPrioritySpecified()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                var producer = await context.CreateProducerAsync();
+
+                byte priority = 4;
+
+                testPeer.ExpectTransfer(m => Assert.AreEqual(priority, m.Header.Priority));
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+
+                ITextMessage message = await context.CreateTextMessageAsync();
+                Assert.AreEqual(MsgPriority.BelowNormal, message.NMSPriority);
+
+                await producer.SendAsync(destination, message);
+
+                Assert.AreEqual((MsgPriority) priority, message.NMSPriority);
+
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestNonDefaultPriorityProducesMessagesWithPriorityFieldAndSetsNMSPriority()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                var producer = await context.CreateProducerAsync();
+
+                byte priority = 9;
+
+                testPeer.ExpectTransfer(m => Assert.AreEqual(priority, m.Header.Priority));
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+
+                ITextMessage message = await context.CreateTextMessageAsync();
+                Assert.AreEqual(MsgPriority.BelowNormal, message.NMSPriority);
+
+                producer.DeliveryMode = MsgDeliveryMode.Persistent;
+                producer.Priority = (MsgPriority) priority;
+                producer.TimeToLive = NMSConstants.defaultTimeToLive;
+                await producer.SendAsync(destination, message);
+
+                Assert.AreEqual((MsgPriority) priority, message.NMSPriority);
+
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageSetsNMSMessageId()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                var producer = await context.CreateProducerAsync();
+
+                string text = "myMessage";
+                string actualMessageId = null;
+                testPeer.ExpectTransfer(m =>
+                {
+                    Assert.IsTrue(m.Header.Durable);
+                    Assert.IsNotEmpty(m.Properties.MessageId);
+                    actualMessageId = m.Properties.MessageId;
+                });
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+
+                ITextMessage message = await context.CreateTextMessageAsync(text);
+                Assert.IsNull(message.NMSMessageId, "NMSMessageId should not yet be set");
+
+                await producer.SendAsync(destination, message);
+
+                Assert.IsNotNull(message.NMSMessageId);
+                Assert.IsNotEmpty(message.NMSMessageId, "NMSMessageId should be set");
+                Assert.IsTrue(message.NMSMessageId.StartsWith("ID:"), "MMS 'ID:' prefix not found");
+
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+                // Get the value that was actually transmitted/received, verify it is a string, compare to what we have locally
+                Assert.AreEqual(message.NMSMessageId, actualMessageId, "Expected NMSMessageId value to be present in AMQP message");
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageWithDisableMessageIdHint()
+        {
+            await DoSendingMessageWithDisableMessageIdHintTestImpl(false);
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageWithDisableMessageIdHintAndExistingMessageId()
+        {
+            await DoSendingMessageWithDisableMessageIdHintTestImpl(true);
+        }
+
+        private async Task DoSendingMessageWithDisableMessageIdHintTestImpl(bool existingId)
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                var producer = await context.CreateProducerAsync();
+
+                string text = "myMessage";
+                testPeer.ExpectTransfer(m =>
+                {
+                    Assert.IsTrue(m.Header.Durable);
+                    Assert.IsNull(m.Properties.MessageId); // Check there is no message-id value;
+                    Assert.AreEqual(text, (m.BodySection as AmqpValue).Value);
+                });
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+
+                ITextMessage message = await context.CreateTextMessageAsync(text);
+
+                Assert.IsNull(message.NMSMessageId, "NMSMessageId should not yet be set");
+
+                if (existingId)
+                {
+                    string existingMessageId = "ID:this-should-be-overwritten-in-send";
+                    message.NMSMessageId = existingMessageId;
+                    Assert.AreEqual(existingMessageId, message.NMSMessageId, "NMSMessageId should now be se");
+                }
+
+                producer.DisableMessageID = true;
+
+                await producer.SendAsync(destination, message);
+
+                Assert.IsNull(message.NMSMessageId, "NMSMessageID should be null");
+
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        // TODO No connection listener in nms context
+        // [Test, Timeout(20_000)]
+        // public async Task TestRemotelyCloseProducer()
+        // {
+        //     string breadCrumb = "ErrorMessageBreadCrumb";
+        //
+        //     ManualResetEvent producerClosed = new ManualResetEvent(false);
+        //     Mock<INmsConnectionListener> mockConnectionListener = new Mock<INmsConnectionListener>();
+        //     mockConnectionListener
+        //         .Setup(listener => listener.OnProducerClosed(It.IsAny<NmsMessageProducer>(), It.IsAny<Exception>()))
+        //         .Callback(() => { producerClosed.Set(); });
+        //
+        //     using (TestAmqpPeer testPeer = new TestAmqpPeer())
+        //     {
+        //         NmsContext context = (NmsContext) EstablishNMSContext(testPeer);
+        //         context.AddConnectionListener(mockConnectionListener.Object);
+        //
+        //         testPeer.ExpectBegin();
+        //         ISession session = context.CreateSession(AcknowledgementMode.AutoAcknowledge);
+        //
+        //         // Create a producer, then remotely end it afterwards.
+        //         testPeer.ExpectSenderAttach();
+        //         testPeer.RemotelyDetachLastOpenedLinkOnLastOpenedSession(expectDetachResponse: true, closed: true, errorType: AmqpError.RESOURCE_DELETED, breadCrumb, delayBeforeSend: 10);
+        //
+        //         IQueue destination = session.GetQueue("myQueue");
+        //         IMessageProducer producer = session.CreateProducer(destination);
+        //
+        //         // Verify the producer gets marked closed
+        //         testPeer.WaitForAllMatchersToComplete(1000);
+        //
+        //         Assert.True(producerClosed.WaitOne(TimeSpan.FromMilliseconds(1000)), "Producer closed callback didn't trigger");
+        //         Assert.That(() => producer.DisableMessageID, Throws.Exception.InstanceOf<IllegalStateException>(), "Producer never closed");
+        //
+        //         // Try closing it explicitly, should effectively no-op in client.
+        //         // The test peer will throw during close if it sends anything.
+        //         producer.Close();
+        //     }
+        // }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendWhenLinkCreditIsZeroAndTimeout()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer, optionsString: "nms.sendTimeout=500");
+                testPeer.ExpectBegin();
+
+                IQueue queue = await context.GetQueueAsync("myQueue");
+
+                ITextMessage message = await context.CreateTextMessageAsync("text");
+
+                // Expect the producer to attach. Don't send any credit so that the client will
+                // block on a send and we can test our timeouts.
+                testPeer.ExpectSenderAttachWithoutGrantingCredit();
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+
+                var producer = await context.CreateProducerAsync();
+
+                Assert.CatchAsync<Exception>(async () => await producer.SendAsync(queue, message), "Send should time out.");
+
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendTimesOutWhenNoDispositionArrives()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer, optionsString: "nms.sendTimeout=500");
+                testPeer.ExpectBegin();
+
+                IQueue queue = await context.GetQueueAsync("myQueue");
+
+                ITextMessage message = await context.CreateTextMessageAsync("text");
+
+                // Expect the producer to attach and grant it some credit, it should send
+                // a transfer which we will not send any response for which should cause the
+                // send operation to time out.
+                testPeer.ExpectSenderAttach();
+                testPeer.ExpectTransferButDoNotRespond(messageMatcher: Assert.NotNull);
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+
+                var producer = await context.CreateProducerAsync();
+
+                Assert.CatchAsync<Exception>(async () => await producer.SendAsync(queue, message), "Send should time out.");
+
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendWorksWhenConnectionNotStarted()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                var producer = await context.CreateProducerAsync();
+
+                testPeer.ExpectTransfer(Assert.IsNotNull);
+
+                await producer.SendAsync(destination, await context.CreateMessageAsync());
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                await producer.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendWorksAfterConnectionStopped()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                var producer = await context.CreateProducerAsync();
+
+                testPeer.ExpectTransfer(Assert.IsNotNull);
+
+                await context.StopAsync();
+
+                await producer.SendAsync(destination, await context.CreateMessageAsync());
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+
+                await producer.CloseAsync();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessagePersistentSetsBatchableFalse()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                var producer = await context.CreateProducerAsync();
+                testPeer.ExpectTransfer(messageMatcher: Assert.IsNotNull,
+                    stateMatcher: Assert.IsNull,
+                    settled: false,
+                    sendResponseDisposition: true,
+                    responseState: new Accepted(),
+                    responseSettled: true,
+                    batchable: false);
+
+                IMessage message = await context.CreateMessageAsync();
+                producer.DeliveryMode = MsgDeliveryMode.Persistent;
+                producer.Priority = MsgPriority.Normal;
+                producer.TimeToLive = NMSConstants.defaultTimeToLive;
+                await producer.SendAsync(destination, message);
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageNonPersistentSetsBatchableFalse()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = await EstablishNMSContextAsync(testPeer);
+                await context.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                IQueue destination = await context.GetQueueAsync("myQueue");
+                var producer = await context.CreateProducerAsync();
+                testPeer.ExpectTransfer(messageMatcher: Assert.IsNotNull,
+                    stateMatcher: Assert.IsNull,
+                    settled: false,
+                    sendResponseDisposition: true,
+                    responseState: new Accepted(),
+                    responseSettled: true,
+                    batchable: false);
+
+                IMessage message = await context.CreateMessageAsync();
+                producer.DeliveryMode = MsgDeliveryMode.NonPersistent;
+                producer.Priority = MsgPriority.Normal;
+                producer.TimeToLive = NMSConstants.defaultTimeToLive;
+                await producer.SendAsync(destination, message);
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                await context.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Apache-NMS-AMQP-Test/Integration/Async/ProducerIntegrationAsyncTest.cs b/test/Apache-NMS-AMQP-Test/Integration/Async/ProducerIntegrationAsyncTest.cs
new file mode 100644
index 0000000..641b36b
--- /dev/null
+++ b/test/Apache-NMS-AMQP-Test/Integration/Async/ProducerIntegrationAsyncTest.cs
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Threading.Tasks;
+using Amqp.Framing;
+using Apache.NMS;
+using NMS.AMQP.Test.TestAmqp;
+using NUnit.Framework;
+
+namespace NMS.AMQP.Test.Integration.Async
+{
+    [TestFixture]
+    public class ProducerIntegrationAsyncTestAsync : IntegrationTestFixture
+    {
+        [Test, Timeout(20_000)]
+        public async Task TestSentAsyncIsAsynchronous()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await base.EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+
+                // Create and transfer a new message
+                String text = "myMessage";
+                testPeer.ExpectTransfer(messageMatcher: m => Assert.AreEqual(text, (m.BodySection as AmqpValue).Value),
+                    settled: false,
+                    sendResponseDisposition: true,
+                    responseState: new Accepted(),
+                    responseSettled: true,
+                    stateMatcher: Assert.IsNull,
+                    dispositionDelay: 10); // 10ms should be enough
+                testPeer.ExpectClose();
+
+                ITextMessage message = await session.CreateTextMessageAsync(text);
+                var sendTask = producer.SendAsync(message);
+                // Instantly check if its not completed yet, we want async, so it should not be completed right after 
+                Assert.AreEqual(false, sendTask.IsCompleted);
+                
+                // And now wait for task to complete
+                sendTask.Wait(20_000);
+
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+        
+        [Test, Timeout(20_000)]
+        public async Task TestSentAsync()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await base.EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+
+                // Create and transfer a new message
+                String text = "myMessage";
+                testPeer.ExpectTransfer(messageMatcher: m => Assert.AreEqual(text, (m.BodySection as AmqpValue).Value),
+                    settled: false,
+                    sendResponseDisposition: true,
+                    responseState: new Accepted(),
+                    responseSettled: true,
+                    stateMatcher: Assert.IsNull,
+                    dispositionDelay: 10); // 10ms should be enough
+                testPeer.ExpectClose();
+
+                ITextMessage message = await session.CreateTextMessageAsync(text);
+                await producer.SendAsync(message);
+              
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+       
+
+        [Test, Timeout(20_000)]
+        public async Task TestProducerWorkWithAsyncAwait()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await base.EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+
+                // Create and transfer a new message, explicitly setting the deliveryMode on the
+                // message (which applications shouldn't) to NON_PERSISTENT and sending it to check
+                // that the producer ignores this value and sends the message as PERSISTENT(/durable)
+                testPeer.ExpectTransfer(message => Assert.IsTrue(message.Header.Durable));
+                testPeer.ExpectClose();
+
+                ITextMessage textMessage = await session.CreateTextMessageAsync();
+                textMessage.NMSDeliveryMode = MsgDeliveryMode.NonPersistent;
+                Assert.AreEqual(MsgDeliveryMode.NonPersistent, textMessage.NMSDeliveryMode);
+
+                await producer.SendAsync(textMessage);
+
+                Assert.AreEqual(MsgDeliveryMode.Persistent, textMessage.NMSDeliveryMode);
+
+                await connection.CloseAsync();
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/test/Apache-NMS-AMQP-Test/Integration/Async/ProducerIntegrationTest.cs b/test/Apache-NMS-AMQP-Test/Integration/Async/ProducerIntegrationTest.cs
new file mode 100644
index 0000000..f1328a0
--- /dev/null
+++ b/test/Apache-NMS-AMQP-Test/Integration/Async/ProducerIntegrationTest.cs
@@ -0,0 +1,782 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Amqp.Framing;
+using Amqp.Types;
+using Apache.NMS;
+using Apache.NMS.AMQP;
+using Apache.NMS.AMQP.Util;
+using Moq;
+using NMS.AMQP.Test.TestAmqp;
+using NMS.AMQP.Test.TestAmqp.BasicTypes;
+using NUnit.Framework;
+
+namespace NMS.AMQP.Test.Integration.Async
+{
+    [TestFixture]
+    public class ProducerIntegrationTestAsync : IntegrationTestFixture
+    {
+        private const long TICKS_PER_MILLISECOND = 10000;
+
+        [Test, Timeout(20_000)]
+        public async Task TestCloseSender()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await base.EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync();
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                testPeer.ExpectClose();
+
+                await producer.CloseAsync();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSentTextMessageCanBeModified()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await base.EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+
+                // Create and transfer a new message
+                String text = "myMessage";
+                testPeer.ExpectTransfer(x => Assert.AreEqual(text, (x.BodySection as AmqpValue).Value));
+                testPeer.ExpectClose();
+
+                ITextMessage message = await session.CreateTextMessageAsync(text);
+                await producer.SendAsync(message);
+
+                Assert.AreEqual(text, message.Text);
+                message.Text = text + text;
+                Assert.AreEqual(text + text, message.Text);
+
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestDefaultDeliveryModeProducesDurableMessages()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await base.EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+
+                // Create and transfer a new message
+                testPeer.ExpectTransfer(message => Assert.IsTrue(message.Header.Durable));
+                testPeer.ExpectClose();
+
+                ITextMessage textMessage = await session.CreateTextMessageAsync();
+
+                await producer.SendAsync(textMessage);
+                Assert.AreEqual(MsgDeliveryMode.Persistent, textMessage.NMSDeliveryMode);
+
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestProducerOverridesMessageDeliveryMode()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await base.EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+
+                // Create and transfer a new message, explicitly setting the deliveryMode on the
+                // message (which applications shouldn't) to NON_PERSISTENT and sending it to check
+                // that the producer ignores this value and sends the message as PERSISTENT(/durable)
+                testPeer.ExpectTransfer(message => Assert.IsTrue(message.Header.Durable));
+                testPeer.ExpectClose();
+
+                ITextMessage textMessage = await session.CreateTextMessageAsync();
+                textMessage.NMSDeliveryMode = MsgDeliveryMode.NonPersistent;
+                Assert.AreEqual(MsgDeliveryMode.NonPersistent, textMessage.NMSDeliveryMode);
+
+                await producer.SendAsync(textMessage);
+
+                Assert.AreEqual(MsgDeliveryMode.Persistent, textMessage.NMSDeliveryMode);
+
+                await connection.CloseAsync();
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageNonPersistentProducerSetDurableFalse()
+        {
+            await DoSendingMessageNonPersistentTestImpl(false, true, true);
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageNonPersistentProducerSetDurableFalseAnonymousProducer()
+        {
+            await DoSendingMessageNonPersistentTestImpl(true, true, true);
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageNonPersistentSendSetDurableFalse()
+        {
+            await DoSendingMessageNonPersistentTestImpl(false, true, false);
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageNonPersistentSendSetDurableFalseAnonymousProducer()
+        {
+            await DoSendingMessageNonPersistentTestImpl(true, true, false);
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageNonPersistentProducerOmitsHeader()
+        {
+            await DoSendingMessageNonPersistentTestImpl(false, false, true);
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageNonPersistentProducerOmitsHeaderAnonymousProducer()
+        {
+            await DoSendingMessageNonPersistentTestImpl(true, false, true);
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageNonPersistentSendOmitsHeader()
+        {
+            await DoSendingMessageNonPersistentTestImpl(false, false, false);
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageNonPersistentSendOmitsHeaderAnonymousProducer()
+        {
+            await DoSendingMessageNonPersistentTestImpl(true, false, false);
+        }
+
+        private async Task DoSendingMessageNonPersistentTestImpl(bool anonymousProducer, bool setPriority, bool setOnProducer)
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                //Add capability to indicate support for ANONYMOUS-RELAY
+                Symbol[] serverCapabilities = { SymbolUtil.OPEN_CAPABILITY_ANONYMOUS_RELAY };
+                IConnection connection = await EstablishConnectionAsync(testPeer, serverCapabilities: serverCapabilities);
+                testPeer.ExpectBegin();
+
+                string queueName = "myQueue";
+                Action<object> targetMatcher = t =>
+                {
+                    var target = t as Target;
+                    Assert.IsNotNull(target);
+                    if (anonymousProducer)
+                        Assert.IsNull(target.Address);
+                    else
+                        Assert.AreEqual(queueName, target.Address);
+                };
+                
+
+                testPeer.ExpectSenderAttach(targetMatcher: targetMatcher, sourceMatcher: Assert.NotNull, senderSettled: false);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync(queueName);
+                IMessageProducer producer = null;
+                if (anonymousProducer)
+                    producer = await session.CreateProducerAsync();
+                else
+                    producer = await session.CreateProducerAsync(queue);
+
+                byte priority = 5;
+                String text = "myMessage";
+                testPeer.ExpectTransfer(messageMatcher: message =>
+                    {
+                        if (setPriority)
+                        {
+                            Assert.IsFalse(message.Header.Durable);
+                            Assert.AreEqual(5, message.Header.Priority);
+                        }
+
+                        Assert.AreEqual(text, (message.BodySection as AmqpValue).Value);
+                    }, stateMatcher: Assert.IsNull,
+                    settled: false,
+                    sendResponseDisposition: true,
+                    responseState: new Accepted(),
+                    responseSettled: true);
+
+                ITextMessage textMessage = await session.CreateTextMessageAsync(text);
+
+                if (setOnProducer)
+                {
+                    producer.DeliveryMode = MsgDeliveryMode.NonPersistent;
+                    if (setPriority)
+                        producer.Priority = (MsgPriority) 5;
+
+                    if (anonymousProducer)
+                        await producer.SendAsync(queue, textMessage);
+                    else
+                        await producer.SendAsync(textMessage);
+                }
+                else
+                {
+                    if (anonymousProducer)
+                    {
+                        await producer.SendAsync(destination: queue,
+                            message: textMessage,
+                            deliveryMode: MsgDeliveryMode.NonPersistent,
+                            priority: setPriority ? (MsgPriority) priority : NMSConstants.defaultPriority,
+                            timeToLive: NMSConstants.defaultTimeToLive);
+                    }
+                    else
+                    {
+                        await producer.SendAsync(message: textMessage,
+                            deliveryMode: MsgDeliveryMode.NonPersistent,
+                            priority: setPriority ? (MsgPriority) priority : NMSConstants.defaultPriority,
+                            timeToLive: NMSConstants.defaultTimeToLive);
+                    }
+                }
+
+                Assert.AreEqual(MsgDeliveryMode.NonPersistent, textMessage.NMSDeliveryMode, "Should have NonPersistent delivery mode set");
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageSetsNMSDestination()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(destination);
+
+                string text = "myMessage";
+                ITextMessage message = await session.CreateTextMessageAsync(text);
+
+                testPeer.ExpectTransfer(m => Assert.AreEqual(text, (m.BodySection as AmqpValue).Value));
+                testPeer.ExpectClose();
+
+                Assert.IsNull(message.NMSDestination, "Should not yet have a NMSDestination");
+
+                await producer.SendAsync(message);
+
+                Assert.AreEqual(destination, message.NMSDestination, "Should have had NMSDestination set");
+
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageSetsNMSTimestamp()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(destination);
+
+                // Create matcher to expect the absolute-expiry-time field of the properties section to
+                // be set to a value greater than 'now'+ttl, within a delta.
+
+                DateTime creationLower = DateTime.UtcNow;
+                DateTime creationUpper = creationLower + TimeSpan.FromMilliseconds(3000);
+
+                var text = "myMessage";
+                testPeer.ExpectTransfer(m =>
+                {
+                    Assert.IsTrue(m.Header.Durable);
+                    Assert.That(m.Properties.CreationTime.Ticks, Is.GreaterThanOrEqualTo(creationLower.Ticks).Within(TICKS_PER_MILLISECOND));
+                    Assert.That(m.Properties.CreationTime.Ticks, Is.LessThanOrEqualTo(creationUpper.Ticks).Within(TICKS_PER_MILLISECOND));
+                    Assert.AreEqual(text, (m.BodySection as AmqpValue).Value);
+                });
+
+                ITextMessage message = await session.CreateTextMessageAsync(text);
+                await producer.SendAsync(message);
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageSetsNMSExpirationRelatedAbsoluteExpiryAndTtlFields()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(destination);
+
+                uint ttl = 100_000;
+                DateTime currentTime = DateTime.UtcNow;
+                DateTime expirationLower = currentTime + TimeSpan.FromMilliseconds(ttl);
+                DateTime expirationUpper = currentTime + TimeSpan.FromMilliseconds(ttl) + TimeSpan.FromMilliseconds(5000);
+
+                // Create matcher to expect the absolute-expiry-time field of the properties section to
+                // be set to a value greater than 'now'+ttl, within a delta.
+                string text = "myMessage";
+                testPeer.ExpectTransfer(m =>
+                {
+                    Assert.IsTrue(m.Header.Durable);
+                    Assert.AreEqual(ttl, m.Header.Ttl);
+                    Assert.That(m.Properties.AbsoluteExpiryTime.Ticks, Is.GreaterThanOrEqualTo(expirationLower.Ticks).Within(TICKS_PER_MILLISECOND));
+                    Assert.That(m.Properties.AbsoluteExpiryTime.Ticks, Is.LessThanOrEqualTo(expirationUpper.Ticks).Within(TICKS_PER_MILLISECOND));
+                    Assert.AreEqual(text, (m.BodySection as AmqpValue).Value);
+                });
+
+                ITextMessage message = await session.CreateTextMessageAsync(text);
+                await producer.SendAsync(message, NMSConstants.defaultDeliveryMode, NMSConstants.defaultPriority, TimeSpan.FromMilliseconds(ttl));
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+        
+        [Test, Timeout(20_000)]
+        public async Task TestMessagesAreProducedWithProperDefaultPriorityWhenNoPrioritySpecified()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(destination);
+
+                byte priority = 4;
+
+                testPeer.ExpectTransfer(m => Assert.AreEqual(priority, m.Header.Priority));
+                testPeer.ExpectClose();
+
+                ITextMessage message = await session.CreateTextMessageAsync();
+                Assert.AreEqual(MsgPriority.BelowNormal, message.NMSPriority);
+
+                await producer.SendAsync(message);
+
+                Assert.AreEqual((MsgPriority) priority, message.NMSPriority);
+
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestNonDefaultPriorityProducesMessagesWithPriorityFieldAndSetsNMSPriority()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(destination);
+
+                byte priority = 9;
+
+                testPeer.ExpectTransfer(m => Assert.AreEqual(priority, m.Header.Priority));
+                testPeer.ExpectClose();
+
+                ITextMessage message = await session.CreateTextMessageAsync();
+                Assert.AreEqual(MsgPriority.BelowNormal, message.NMSPriority);
+
+                await producer.SendAsync(message, MsgDeliveryMode.Persistent, (MsgPriority) priority, NMSConstants.defaultTimeToLive);
+
+                Assert.AreEqual((MsgPriority) priority, message.NMSPriority);
+
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageSetsNMSMessageId()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(destination);
+
+                string text = "myMessage";
+                string actualMessageId = null;
+                testPeer.ExpectTransfer(m =>
+                {
+                    Assert.IsTrue(m.Header.Durable);
+                    Assert.IsNotEmpty(m.Properties.MessageId);
+                    actualMessageId = m.Properties.MessageId;
+                });
+                testPeer.ExpectClose();
+
+                ITextMessage message = await session.CreateTextMessageAsync(text);
+                Assert.IsNull(message.NMSMessageId, "NMSMessageId should not yet be set");
+
+                await producer.SendAsync(message);
+
+                Assert.IsNotNull(message.NMSMessageId);
+                Assert.IsNotEmpty(message.NMSMessageId, "NMSMessageId should be set");
+                Assert.IsTrue(message.NMSMessageId.StartsWith("ID:"), "MMS 'ID:' prefix not found");
+
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+                // Get the value that was actually transmitted/received, verify it is a string, compare to what we have locally
+                Assert.AreEqual(message.NMSMessageId, actualMessageId, "Expected NMSMessageId value to be present in AMQP message");
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageWithDisableMessageIdHint()
+        {
+            await DoSendingMessageWithDisableMessageIdHintTestImpl(false);
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageWithDisableMessageIdHintAndExistingMessageId()
+        {
+            await DoSendingMessageWithDisableMessageIdHintTestImpl(true);
+        }
+
+        private async Task DoSendingMessageWithDisableMessageIdHintTestImpl(bool existingId)
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(destination);
+
+                string text = "myMessage";
+                testPeer.ExpectTransfer(m =>
+                {
+                    Assert.IsTrue(m.Header.Durable);
+                    Assert.IsNull(m.Properties.MessageId); // Check there is no message-id value;
+                    Assert.AreEqual(text, (m.BodySection as AmqpValue).Value);
+                });
+                testPeer.ExpectClose();
+
+                ITextMessage message = await session.CreateTextMessageAsync(text);
+
+                Assert.IsNull(message.NMSMessageId, "NMSMessageId should not yet be set");
+
+                if (existingId)
+                {
+                    string existingMessageId = "ID:this-should-be-overwritten-in-send";
+                    message.NMSMessageId = existingMessageId;
+                    Assert.AreEqual(existingMessageId, message.NMSMessageId, "NMSMessageId should now be se");
+                }
+
+                producer.DisableMessageID = true;
+
+                await producer.SendAsync(message);
+
+                Assert.IsNull(message.NMSMessageId, "NMSMessageID should be null");
+
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestRemotelyCloseProducer()
+        {
+            string breadCrumb = "ErrorMessageBreadCrumb";
+
+            ManualResetEvent producerClosed = new ManualResetEvent(false);
+            Mock<INmsConnectionListener> mockConnectionListener = new Mock<INmsConnectionListener>();
+            mockConnectionListener
+                .Setup(listener => listener.OnProducerClosed(It.IsAny<NmsMessageProducer>(), It.IsAny<Exception>()))
+                .Callback(() => { producerClosed.Set(); });
+
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                NmsConnection connection = (NmsConnection) await EstablishConnectionAsync(testPeer);
+                connection.AddConnectionListener(mockConnectionListener.Object);
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                // Create a producer, then remotely end it afterwards.
+                testPeer.ExpectSenderAttach();
+                testPeer.RemotelyDetachLastOpenedLinkOnLastOpenedSession(expectDetachResponse: true, closed: true, errorType: AmqpError.RESOURCE_DELETED, breadCrumb, delayBeforeSend: 10);
+
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(destination);
+
+                // Verify the producer gets marked closed
+                testPeer.WaitForAllMatchersToComplete(1000);
+
+                Assert.True(producerClosed.WaitOne(TimeSpan.FromMilliseconds(1000)), "Producer closed callback didn't trigger");
+                Assert.That(() => producer.DisableMessageID, Throws.Exception.InstanceOf<IllegalStateException>(), "Producer never closed");
+
+                // Try closing it explicitly, should effectively no-op in client.
+                // The test peer will throw during close if it sends anything.
+                await producer.CloseAsync();
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendWhenLinkCreditIsZeroAndTimeout()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer, optionsString: "nms.sendTimeout=500");
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                ITextMessage message = await session.CreateTextMessageAsync("text");
+
+                // Expect the producer to attach. Don't send any credit so that the client will
+                // block on a send and we can test our timeouts.
+                testPeer.ExpectSenderAttachWithoutGrantingCredit();
+                testPeer.ExpectClose();
+
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+
+                Assert.CatchAsync<Exception>(async () => await producer.SendAsync(message), "Send should time out.");
+
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendTimesOutWhenNoDispositionArrives()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer, optionsString: "nms.sendTimeout=500");
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                ITextMessage message = await session.CreateTextMessageAsync("text");
+
+                // Expect the producer to attach and grant it some credit, it should send
+                // a transfer which we will not send any response for which should cause the
+                // send operation to time out.
+                testPeer.ExpectSenderAttach();
+                testPeer.ExpectTransferButDoNotRespond(messageMatcher: Assert.NotNull);
+                testPeer.ExpectClose();
+
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+
+                Assert.CatchAsync<Exception>(async () => await producer.SendAsync(message), "Send should time out.");
+
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendWorksWhenConnectionNotStarted()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(destination);
+
+                testPeer.ExpectTransfer(Assert.IsNotNull);
+
+                await producer.SendAsync(await session.CreateMessageAsync());
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                await producer.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendWorksAfterConnectionStopped()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(destination);
+
+                testPeer.ExpectTransfer(Assert.IsNotNull);
+
+                await connection.StopAsync();
+
+                await producer.SendAsync(await session.CreateMessageAsync());
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                testPeer.ExpectClose();
+
+                await producer.CloseAsync();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+  
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessagePersistentSetsBatchableFalse()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(destination);
+                testPeer.ExpectTransfer(messageMatcher: Assert.IsNotNull,
+                    stateMatcher: Assert.IsNull,
+                    settled: false,
+                    sendResponseDisposition: true,
+                    responseState: new Accepted(),
+                    responseSettled: true,
+                    batchable: false);
+
+                IMessage message = await session.CreateMessageAsync();
+                await producer.SendAsync(message: message, deliveryMode: MsgDeliveryMode.Persistent, MsgPriority.Normal, NMSConstants.defaultTimeToLive);
+                
+                testPeer.WaitForAllMatchersToComplete(1000);
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendingMessageNonPersistentSetsBatchableFalse()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectSenderAttach();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                IQueue destination = await session.GetQueueAsync("myQueue");
+                IMessageProducer producer = await session.CreateProducerAsync(destination);
+                testPeer.ExpectTransfer(messageMatcher: Assert.IsNotNull,
+                    stateMatcher: Assert.IsNull,
+                    settled: false,
+                    sendResponseDisposition: true,
+                    responseState: new Accepted(),
+                    responseSettled: true,
+                    batchable: false);
+
+                IMessage message = await session.CreateMessageAsync();
+                await producer.SendAsync(message: message, deliveryMode: MsgDeliveryMode.NonPersistent, MsgPriority.Normal, NMSConstants.defaultTimeToLive);
+                
+                testPeer.WaitForAllMatchersToComplete(1000);
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Apache-NMS-AMQP-Test/Integration/Async/SessionIntegrationTest.cs b/test/Apache-NMS-AMQP-Test/Integration/Async/SessionIntegrationTest.cs
new file mode 100644
index 0000000..ca34e19
--- /dev/null
+++ b/test/Apache-NMS-AMQP-Test/Integration/Async/SessionIntegrationTest.cs
@@ -0,0 +1,289 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Apache.NMS;
+using NMS.AMQP.Test.TestAmqp;
+using NUnit.Framework;
+
+namespace NMS.AMQP.Test.Integration.Async
+{
+    [TestFixture]
+    public class SessionIntegrationTestAsync : IntegrationTestFixture
+    {
+        [Test, Timeout(20_000)]
+        public async Task TestCloseSession()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                Assert.NotNull(session, "Session should not be null");
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+
+                await session.CloseAsync();
+
+                // Should send nothing and throw no error.
+                await session.CloseAsync();
+
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateProducer()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                testPeer.ExpectSenderAttach();
+                testPeer.ExpectClose();
+
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                await session.CreateProducerAsync(queue);
+
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateConsumer()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+                testPeer.ExpectClose();
+
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                await session.CreateConsumerAsync(queue);
+
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateConsumerWithEmptySelector()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+                testPeer.ExpectClose();
+
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                await session.CreateConsumerAsync(queue, "");
+                await session.CreateConsumerAsync(queue, "", noLocal: false);
+                
+                await connection.CloseAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateConsumerWithNullSelector()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+                testPeer.ExpectClose();
+
+                IQueue queue = await session.GetQueueAsync("myQueue");
+                await session.CreateConsumerAsync(queue, null);
+                await session.CreateConsumerAsync(queue, null, noLocal: false);
+                
+                await connection.CloseAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCreateDurableConsumer()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                
+                string topicName = "myTopic";
+                ITopic topic = await session.GetTopicAsync(topicName);
+                string subscriptionName = "mySubscription";
+
+                testPeer.ExpectDurableSubscriberAttach(topicName, subscriptionName);
+                testPeer.ExpectLinkFlow();
+                
+                IMessageConsumer durableConsumer = await session.CreateDurableConsumerAsync(topic, subscriptionName, null, false);
+                Assert.NotNull(durableConsumer, "MessageConsumer object was null");
+                
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+        
+        [Test, Timeout(20_000)]
+        public async Task TestCreateTemporaryQueue()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                
+                string dynamicAddress = "myTempQueueAddress";
+                testPeer.ExpectTempQueueCreationAttach(dynamicAddress);
+                
+                ITemporaryQueue temporaryQueue = await session.CreateTemporaryQueueAsync();
+                Assert.NotNull(temporaryQueue, "TemporaryQueue object was null");
+                Assert.NotNull(temporaryQueue.QueueName, "TemporaryQueue queue name was null");
+                Assert.AreEqual(dynamicAddress, temporaryQueue.QueueName, "TemporaryQueue name not as expected");
+                
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+        
+        [Test, Timeout(20_000)]
+        public async Task TestCreateTemporaryTopic()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                
+                string dynamicAddress = "myTempTopicAddress";
+                testPeer.ExpectTempTopicCreationAttach(dynamicAddress);
+                
+                ITemporaryTopic temporaryTopic = await session.CreateTemporaryTopicAsync();
+                Assert.NotNull(temporaryTopic, "TemporaryTopic object was null");
+                Assert.NotNull(temporaryTopic.TopicName, "TemporaryTopic name was null");
+                Assert.AreEqual(dynamicAddress, temporaryTopic.TopicName, "TemporaryTopic name not as expected");
+                
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+       
+        [Test, Timeout(20_000)]
+        public async Task TestCreateSharedConsumer()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                
+                string topicName = "myTopic";
+                ITopic topic = await session.GetTopicAsync(topicName);
+                string subscriptionName = "mySubscription";
+
+                testPeer.ExpectSharedSubscriberAttach(topicName, subscriptionName);
+                testPeer.ExpectLinkFlow();
+                
+                IMessageConsumer durableConsumer = await session.CreateSharedConsumerAsync(topic, subscriptionName, null);//, false);
+                // IMessageConsumer durableConsumer = session.CreateDurableConsumer(topic, subscriptionName, null, false);
+                Assert.NotNull(durableConsumer, "MessageConsumer object was null");
+                
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(20000);
+            }
+        }
+        
+        [Test, Timeout(20_000)]
+        public async Task TestCreateSharedDurableConsumer()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+                
+                string topicName = "myTopic";
+                ITopic topic = await session.GetTopicAsync(topicName);
+                string subscriptionName = "mySubscription";
+
+                testPeer.ExpectSharedDurableSubscriberAttach(topicName, subscriptionName);
+                testPeer.ExpectLinkFlow();
+                
+                IMessageConsumer durableConsumer = await session.CreateSharedDurableConsumerAsync(topic, subscriptionName, null); //, false);
+                Assert.NotNull(durableConsumer, "MessageConsumer object was null");
+                
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Apache-NMS-AMQP-Test/Integration/Async/SubscriptionsIntegrationTest.cs b/test/Apache-NMS-AMQP-Test/Integration/Async/SubscriptionsIntegrationTest.cs
new file mode 100644
index 0000000..52c5920
--- /dev/null
+++ b/test/Apache-NMS-AMQP-Test/Integration/Async/SubscriptionsIntegrationTest.cs
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Threading.Tasks;
+using Apache.NMS;
+using NMS.AMQP.Test.TestAmqp;
+using NUnit.Framework;
+
+namespace NMS.AMQP.Test.Integration.Async
+{
+    [TestFixture]
+    public class SubscriptionsIntegrationTestAsync : IntegrationTestFixture
+    {
+        [Test, Timeout(20_000)]
+        public async Task TestUnsubscribeExclusiveDurableSubWhileActiveThenInactive()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                String topicName = "myTopic";
+                ITopic dest = await session.GetTopicAsync("myTopic");
+                String subscriptionName = "mySubscription";
+                
+                // Attach the durable exclusive receiver
+                testPeer.ExpectDurableSubscriberAttach(topicName: topicName, subscriptionName: subscriptionName);
+                testPeer.ExpectLinkFlow();
+                
+                IMessageConsumer consumer = await session.CreateDurableConsumerAsync(dest, subscriptionName, null, false);
+                Assert.NotNull(consumer, "TopicSubscriber object was null");
+                
+                // Now try to unsubscribe, should fail
+                Assert.CatchAsync<NMSException>(async () => session.DeleteDurableConsumer(subscriptionName));
+                
+                // Now close the subscriber
+                testPeer.ExpectDetach(expectClosed: false, sendResponse: true, replyClosed: false);
+                
+                await consumer.CloseAsync();
+                
+                // Try to unsubscribe again, should work now
+                testPeer.ExpectDurableSubUnsubscribeNullSourceLookup(failLookup: false, shared: false, subscriptionName: subscriptionName, topicName: topicName, hasClientId: true);
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                
+                session.DeleteDurableConsumer(subscriptionName);
+                
+                testPeer.WaitForAllMatchersToComplete(1000);
+                
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Apache-NMS-AMQP-Test/Integration/Async/TemporaryQueueIntegrationTest.cs b/test/Apache-NMS-AMQP-Test/Integration/Async/TemporaryQueueIntegrationTest.cs
new file mode 100644
index 0000000..8b0ee5f
--- /dev/null
+++ b/test/Apache-NMS-AMQP-Test/Integration/Async/TemporaryQueueIntegrationTest.cs
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Apache.NMS;
+using NMS.AMQP.Test.TestAmqp;
+using NUnit.Framework;
+
+namespace NMS.AMQP.Test.Integration.Async
+{
+    [TestFixture]
+    public class TemporaryQueueIntegrationTestAsync : IntegrationTestFixture
+    {
+        [Test, Timeout(20_000)]
+        public async Task TestCantConsumeFromTemporaryQueueCreatedOnAnotherConnection()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                string dynamicAddress = "myTempQueueAddress";
+                testPeer.ExpectTempQueueCreationAttach(dynamicAddress);
+
+                ITemporaryQueue temporaryQueue = await session.CreateTemporaryQueueAsync();
+
+                IConnection connection2 = await EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+
+                ISession session2 = await connection2.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                Assert.CatchAsync<InvalidDestinationException>(async () => await session2.CreateConsumerAsync(temporaryQueue), "Should not be able to create consumer from temporary queue from another connection");
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCantDeleteTemporaryQueueWithConsumers()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                string dynamicAddress = "myTempQueueAddress";
+                testPeer.ExpectTempQueueCreationAttach(dynamicAddress);
+
+                ITemporaryQueue temporaryQueue = await session.CreateTemporaryQueueAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+                IMessageConsumer consumer = await session.CreateConsumerAsync(temporaryQueue);
+
+                Assert.CatchAsync<IllegalStateException>(async () => await temporaryQueue.DeleteAsync(), "should not be able to delete temporary queue with active consumers");
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                await consumer.CloseAsync();
+
+                // Now it should be allowed
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                await temporaryQueue.DeleteAsync();
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Apache-NMS-AMQP-Test/Integration/Async/TemporaryTopicIntegrationTest.cs b/test/Apache-NMS-AMQP-Test/Integration/Async/TemporaryTopicIntegrationTest.cs
new file mode 100644
index 0000000..ed2122f
--- /dev/null
+++ b/test/Apache-NMS-AMQP-Test/Integration/Async/TemporaryTopicIntegrationTest.cs
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Apache.NMS;
+using NMS.AMQP.Test.TestAmqp;
+using NUnit.Framework;
+
+namespace NMS.AMQP.Test.Integration.Async
+{
+    [TestFixture]
+    public class TemporaryTopicIntegrationTestAsync : IntegrationTestFixture
+    {
+
+        [Test, Timeout(20_000)]
+        public async Task TestCantConsumeFromTemporaryTopicCreatedOnAnotherConnection()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                string dynamicAddress = "myTempTopicAddress";
+                testPeer.ExpectTempTopicCreationAttach(dynamicAddress);
+
+                ITemporaryTopic topic = await session.CreateTemporaryTopicAsync();
+
+                IConnection connection2 = await EstablishConnectionAsync(testPeer);
+                testPeer.ExpectBegin();
+
+                ISession session2 = await connection2.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                Assert.CatchAsync<InvalidDestinationException>(async () => await session2.CreateConsumerAsync(topic), "Should not be able to create consumer from temporary topic from another connection");
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCantDeleteTemporaryQueueWithConsumers()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.AutoAcknowledge);
+
+                string dynamicAddress = "myTempTopicAddress";
+                testPeer.ExpectTempTopicCreationAttach(dynamicAddress);
+
+                ITemporaryTopic topic = await session.CreateTemporaryTopicAsync();
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+                IMessageConsumer consumer = await session.CreateConsumerAsync(topic);
+
+                Assert.CatchAsync<IllegalStateException>(async () => await topic.DeleteAsync(), "should not be able to delete temporary topic with active consumers");
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                await consumer.CloseAsync();
+
+                // Now it should be allowed
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+                await topic.DeleteAsync();
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Apache-NMS-AMQP-Test/Integration/Async/TransactionsIntegrationTest.cs b/test/Apache-NMS-AMQP-Test/Integration/Async/TransactionsIntegrationTest.cs
new file mode 100644
index 0000000..d2fa236
--- /dev/null
+++ b/test/Apache-NMS-AMQP-Test/Integration/Async/TransactionsIntegrationTest.cs
@@ -0,0 +1,1473 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Amqp;
+using Amqp.Framing;
+using Amqp.Transactions;
+using Apache.NMS;
+using Apache.NMS.AMQP.Util;
+using NMS.AMQP.Test.TestAmqp;
+using NUnit.Framework;
+using IConnection = Apache.NMS.IConnection;
+using ISession = Apache.NMS.ISession;
+
+namespace NMS.AMQP.Test.Integration.Async
+{
+    [TestFixture]
+    public class TransactionsIntegrationTestAsync : IntegrationTestFixture
+    {
+        [Test, Timeout(20_000)]
+        public async Task TestTransactionRolledBackOnSessionClose()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                byte[] txnId = { 5, 6, 7, 8 };
+                testPeer.ExpectDeclare(txnId);
+
+                // Closed session should roll-back the TX with a failed discharge
+                testPeer.ExpectDischarge(txnId, true);
+                testPeer.ExpectEnd();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+                await session.CloseAsync();
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestTransactionCommitFailWithEmptyRejectedDisposition()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                // First expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a Declared disposition state containing the txnId.
+
+                byte[] txnId1 = { 5, 6, 7, 8 };
+                testPeer.ExpectDeclare(txnId1);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                // Create a producer to use in provoking creation of the AMQP transaction
+                testPeer.ExpectSenderAttach();
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+
+                // Expect the message which was sent under the current transaction. Check it carries
+                // TransactionalState with the above txnId but has no outcome. Respond with a
+                // TransactionalState with Accepted outcome.
+                Action<DeliveryState> stateMatcher = state =>
+                {
+                    Assert.IsInstanceOf<TransactionalState>(state);
+                    var transactionalState = (TransactionalState) state;
+                    CollectionAssert.AreEqual(txnId1, transactionalState.TxnId);
+                    Assert.IsNull(transactionalState.Outcome);
+                };
+                testPeer.ExpectTransfer(messageMatcher: Assert.NotNull, stateMatcher: stateMatcher, responseState: new TransactionalState
+                {
+                    Outcome = new Accepted(),
+                    TxnId = txnId1
+                }, responseSettled: true);
+
+                await producer.SendAsync(await session.CreateMessageAsync());
+
+                // Expect an unsettled 'discharge' transfer to the txn coordinator containing the txnId,
+                // and reply with rejected and settled disposition to indicate the commit failed
+                testPeer.ExpectDischarge(txnId1, dischargeState: false, responseState: new Rejected());
+
+                // Then expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a declared disposition state containing the txnId.
+                byte[] txnId2 = { 1, 2, 3, 4 };
+                testPeer.ExpectDeclare(txnId2);
+
+                Assert.CatchAsync<TransactionRolledBackException>(async () => await session.CommitAsync(), "Commit operation should have failed.");
+
+                // session should roll back on close
+                testPeer.ExpectDischarge(txnId2, true);
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestProducedMessagesAfterCommitOfSentMessagesFails()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                // First expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a Declared disposition state containing the txnId.
+                byte[] txnId1 = { 5, 6, 7, 8 };
+                testPeer.ExpectDeclare(txnId1);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                // Create a producer to use in provoking creation of the AMQP transaction
+                testPeer.ExpectSenderAttach();
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+
+                // Expect the message which was sent under the current transaction. Check it carries
+                // TransactionalState with the above txnId but has no outcome. Respond with a
+                // TransactionalState with Accepted outcome.
+                Action<DeliveryState> stateMatcher = state =>
+                {
+                    Assert.IsInstanceOf<TransactionalState>(state);
+                    var transactionalState = (TransactionalState) state;
+                    CollectionAssert.AreEqual(txnId1, transactionalState.TxnId);
+                    Assert.IsNull(transactionalState.Outcome);
+                };
+                testPeer.ExpectTransfer(messageMatcher: Assert.NotNull, stateMatcher: stateMatcher, responseState: new TransactionalState
+                {
+                    Outcome = new Accepted(),
+                    TxnId = txnId1
+                }, responseSettled: true);
+
+                await producer.SendAsync(await session.CreateMessageAsync());
+
+                // Expect an unsettled 'discharge' transfer to the txn coordinator containing the txnId,
+                // and reply with rejected and settled disposition to indicate the commit failed
+                testPeer.ExpectDischarge(txnId1, false, new Rejected() { Error = new Error(ErrorCode.InternalError) { Description = "Unknown error" } });
+
+                // Then expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a declared disposition state containing the txnId.
+                byte[] txnId2 = { 1, 2, 3, 4 };
+                testPeer.ExpectDeclare(txnId2);
+
+                Assert.CatchAsync<TransactionRolledBackException>(async () => await session.CommitAsync(), "Commit operation should have failed.");
+
+                // Expect the message which was sent under the current transaction. Check it carries
+                // TransactionalState with the above txnId but has no outcome. Respond with a
+                // TransactionalState with Accepted outcome.
+                stateMatcher = state =>
+                {
+                    Assert.IsInstanceOf<TransactionalState>(state);
+                    var transactionalState = (TransactionalState) state;
+                    CollectionAssert.AreEqual(txnId2, transactionalState.TxnId);
+                    Assert.IsNull(transactionalState.Outcome);
+                };
+                testPeer.ExpectTransfer(messageMatcher: Assert.NotNull, stateMatcher: stateMatcher, responseState: new TransactionalState
+                {
+                    Outcome = new Accepted(),
+                    TxnId = txnId2
+                }, responseSettled: true);
+                testPeer.ExpectDischarge(txnId2, dischargeState: true);
+
+                await producer.SendAsync(await session.CreateMessageAsync());
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestProducedMessagesAfterRollbackSentMessagesFails()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                // First expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a Declared disposition state containing the txnId.
+                byte[] txnId1 = { 5, 6, 7, 8 };
+                byte[] txnId2 = { 1, 2, 3, 4 };
+                testPeer.ExpectDeclare(txnId1);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                // Create a producer to use in provoking creation of the AMQP transaction
+                testPeer.ExpectSenderAttach();
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+
+                // Expect the message which was sent under the current transaction. Check it carries
+                // TransactionalState with the above txnId but has no outcome. Respond with a
+                // TransactionalState with Accepted outcome.
+
+                Action<DeliveryState> stateMatcher = state =>
+                {
+                    Assert.IsInstanceOf<TransactionalState>(state);
+                    var transactionalState = (TransactionalState) state;
+                    CollectionAssert.AreEqual(txnId1, transactionalState.TxnId);
+                    Assert.IsNull(transactionalState.Outcome);
+                };
+                testPeer.ExpectTransfer(messageMatcher: Assert.NotNull, stateMatcher: stateMatcher, responseState: new TransactionalState
+                {
+                    Outcome = new Accepted(),
+                    TxnId = txnId1
+                }, responseSettled: true);
+
+                await producer.SendAsync(await session.CreateMessageAsync());
+
+                // Expect an unsettled 'discharge' transfer to the txn coordinator containing the txnId,
+                // and reply with rejected and settled disposition to indicate the rollback failed
+                testPeer.ExpectDischarge(txnId1, true, new Rejected() { Error = new Error(ErrorCode.InternalError) { Description = "Unknown error" } });
+
+                // Then expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a declared disposition state containing the txnId.
+                testPeer.ExpectDeclare(txnId2);
+
+                Assert.CatchAsync<TransactionRolledBackException>(async () => await session.RollbackAsync(), "Rollback operation should have failed.");
+
+                // Expect the message which was sent under the current transaction. Check it carries
+                // TransactionalState with the above txnId but has no outcome. Respond with a
+                // TransactionalState with Accepted outcome.
+                stateMatcher = state =>
+                {
+                    Assert.IsInstanceOf<TransactionalState>(state);
+                    var transactionalState = (TransactionalState) state;
+                    CollectionAssert.AreEqual(txnId2, transactionalState.TxnId);
+                    Assert.IsNull(transactionalState.Outcome);
+                };
+                testPeer.ExpectTransfer(messageMatcher: Assert.NotNull, stateMatcher: stateMatcher, responseState: new TransactionalState
+                {
+                    Outcome = new Accepted(),
+                    TxnId = txnId2
+                }, responseSettled: true);
+                testPeer.ExpectDischarge(txnId2, dischargeState: true);
+
+                await producer.SendAsync(await session.CreateMessageAsync());
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCommitTransactedSessionWithConsumerReceivingAllMessages()
+        {
+            await DoCommitTransactedSessionWithConsumerTestImpl(1, 1, false, false);
+        }
+
+        [Test, Timeout(20_000), Ignore("Until deferred close is implemented for AmqpConsumer")]
+        public async Task TestCommitTransactedSessionWithConsumerReceivingAllMessagesAndCloseBefore()
+        {
+            await DoCommitTransactedSessionWithConsumerTestImpl(1, 1, true, true);
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCommitTransactedSessionWithConsumerReceivingAllMessagesAndCloseAfter()
+        {
+            await DoCommitTransactedSessionWithConsumerTestImpl(1, 1, true, false);
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCommitTransactedSessionWithConsumerReceivingSomeMessages()
+        {
+            await DoCommitTransactedSessionWithConsumerTestImpl(5, 2, false, false);
+        }
+
+        [Test, Timeout(20_000), Ignore("Until deferred close is implemented for AmqpConsumer")]
+        public async Task TestCommitTransactedSessionWithConsumerReceivingSomeMessagesAndClosesBefore()
+        {
+            await DoCommitTransactedSessionWithConsumerTestImpl(5, 2, true, true);
+        }
+
+        [Test, Timeout(20_000), Ignore("Until deferred close is implemented for AmqpConsumer")]
+        public async Task TestCommitTransactedSessionWithConsumerReceivingSomeMessagesAndClosesAfter()
+        {
+            await DoCommitTransactedSessionWithConsumerTestImpl(5, 2, true, false);
+        }
+
+        private async Task DoCommitTransactedSessionWithConsumerTestImpl(int transferCount, int consumeCount, bool closeConsumer, bool closeBeforeCommit)
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                // First expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a declared disposition state containing the txnId.
+                byte[] txnId = { 1, 2, 3, 4 };
+                testPeer.ExpectDeclare(txnId);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), transferCount);
+
+                for (int i = 1; i <= consumeCount; i++)
+                {
+                    // Then expect a *settled* TransactionalState disposition for each message once received by the consumer
+                    testPeer.ExpectDisposition(settled: true, stateMatcher: state =>
+                    {
+                        Assert.IsInstanceOf<TransactionalState>(state);
+                        var transactionalState = (TransactionalState) state;
+                        Assert.AreEqual(txnId, transactionalState.TxnId);
+                        Assert.IsInstanceOf<Accepted>(transactionalState.Outcome);
+                    });
+                }
+
+                IMessageConsumer messageConsumer = await session.CreateConsumerAsync(queue);
+
+                for (int i = 1; i <= consumeCount; i++)
+                {
+                    IMessage receivedMessage = await messageConsumer.ReceiveAsync(TimeSpan.FromSeconds(3));
+                    Assert.NotNull(receivedMessage);
+                    Assert.IsInstanceOf<ITextMessage>(receivedMessage);
+                }
+
+                // Expect the consumer to close now
+                if (closeConsumer && closeBeforeCommit)
+                {
+                    // Expect the client to then drain off all credit from the link.
+                    testPeer.ExpectLinkFlow(drain: true, sendDrainFlowResponse: true);
+
+                    // Expect the messages that were not consumed to be released
+                    int unconsumed = transferCount - consumeCount;
+                    for (int i = 1; i <= unconsumed; i++)
+                    {
+                        testPeer.ExpectDispositionThatIsReleasedAndSettled();
+                    }
+
+                    // Expect an unsettled 'discharge' transfer to the txn coordinator containing the txnId,
+                    // and reply with accepted and settled disposition to indicate the commit succeeded
+                    testPeer.ExpectDischarge(txnId, dischargeState: false);
+
+                    // Then expect an unsettled 'declare' transfer to the txn coordinator, and
+                    // reply with a declared disposition state containing the txnId.
+                    testPeer.ExpectDeclare(txnId);
+
+                    // Now the deferred close should be performed.
+                    testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+
+                    await messageConsumer.CloseAsync();
+                }
+                else
+                {
+                    // Expect an unsettled 'discharge' transfer to the txn coordinator containing the txnId,
+                    // and reply with accepted and settled disposition to indicate the commit succeeded
+                    testPeer.ExpectDischarge(txnId, dischargeState: false);
+
+                    // Then expect an unsettled 'declare' transfer to the txn coordinator, and
+                    // reply with a declared disposition state containing the txnId.
+                    testPeer.ExpectDeclare(txnId);
+                }
+
+                await session.CommitAsync();
+
+                if (closeConsumer && !closeBeforeCommit)
+                {
+                    testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+
+                    // Expect the messages that were not consumed to be released
+                    int unconsumed = transferCount - consumeCount;
+                    for (int i = 1; i <= unconsumed; i++)
+                    {
+                        testPeer.ExpectDispositionThatIsReleasedAndSettled();
+                    }
+
+                    await messageConsumer.CloseAsync();
+                }
+
+                testPeer.ExpectDischarge(txnId, dischargeState: true);
+                testPeer.ExpectClose();
+
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestConsumerWithNoMessageCanCloseBeforeCommit()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                // First expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a declared disposition state containing the txnId.
+                byte[] txnId = { 1, 2, 3, 4 };
+                testPeer.ExpectDeclare(txnId);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+
+                // TODO: qpid-jms extend 2 additional flow links
+                // 1) Drain related with deferred consumer close, this feature is currently 
+                //    not implemented.
+                // 2) Consumer pull - not implemented
+                // testPeer.ExpectLinkFlow(drain: true, sendDrainFlowResponse: true);
+                // testPeer.ExpectLinkFlow(drain: false, sendDrainFlowResponse: false);
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+
+                IMessageConsumer messageConsumer = await session.CreateConsumerAsync(queue);
+                Assert.IsNull(messageConsumer.ReceiveNoWait());
+
+                await messageConsumer.CloseAsync();
+
+                // Expect an unsettled 'discharge' transfer to the txn coordinator containing the txnId,
+                // and reply with accepted and settled disposition to indicate the commit succeeded
+                testPeer.ExpectDischarge(txnId, dischargeState: false);
+
+                // Then expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a declared disposition state containing the txnId.
+                testPeer.ExpectDeclare(txnId);
+                testPeer.ExpectDischarge(txnId, dischargeState: true);
+
+                await session.CommitAsync();
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestConsumerWithNoMessageCanCloseBeforeRollback()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                // First expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a declared disposition state containing the txnId.
+                byte[] txnId = { 1, 2, 3, 4 };
+                testPeer.ExpectDeclare(txnId);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlow();
+
+                // TODO: qpid-jms extend 2 additional flow links
+                // 1) Drain related with deferred consumer close, this feature is currently 
+                //    not implemented.
+                // 2) Consumer pull - not implemented
+                // testPeer.ExpectLinkFlow(drain: true, sendDrainFlowResponse: true);
+                // testPeer.ExpectLinkFlow(drain: false, sendDrainFlowResponse: false);
+
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+
+                IMessageConsumer messageConsumer = await session.CreateConsumerAsync(queue);
+                Assert.IsNull(messageConsumer.ReceiveNoWait());
+
+                await messageConsumer.CloseAsync();
+
+                // Expect an unsettled 'discharge' transfer to the txn coordinator containing the txnId,
+                // and reply with accepted and settled disposition to indicate the commit succeeded
+                testPeer.ExpectDischarge(txnId, dischargeState: true);
+
+                // Then expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a declared disposition state containing the txnId.
+                testPeer.ExpectDeclare(txnId);
+                testPeer.ExpectDischarge(txnId, dischargeState: true);
+
+                await session.RollbackAsync();
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestProducedMessagesOnTransactedSessionCarryTxnId()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                // First expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a declared disposition state containing the txnId.
+                byte[] txnId = { 1, 2, 3, 4 };
+                testPeer.ExpectDeclare(txnId);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                // Create a producer to use in provoking creation of the AMQP transaction
+                testPeer.ExpectSenderAttach();
+
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+
+                // Expect the message which was sent under the current transaction. Check it carries
+                // TransactionalState with the above txnId but has no outcome. Respond with a
+                // TransactionalState with Accepted outcome.
+                testPeer.ExpectTransfer(messageMatcher: Assert.NotNull,
+                    stateMatcher: state =>
+                    {
+                        Assert.IsInstanceOf<TransactionalState>(state);
+                        TransactionalState transactionalState = (TransactionalState) state;
+                        CollectionAssert.AreEqual(txnId, transactionalState.TxnId);
+                        Assert.IsNull(transactionalState.Outcome);
+                    },
+                    responseState: new TransactionalState() { TxnId = txnId, Outcome = new Accepted() },
+                    responseSettled: true);
+                testPeer.ExpectDischarge(txnId, dischargeState: true);
+
+                await producer.SendAsync(await session.CreateMessageAsync());
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestProducedMessagesOnTransactedSessionCanBeReused()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                // First expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a declared disposition state containing the txnId.
+                byte[] txnId = { 1, 2, 3, 4 };
+                testPeer.ExpectDeclare(txnId);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                // Create a producer to use in provoking creation of the AMQP transaction
+                testPeer.ExpectSenderAttach();
+
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+
+                // Expect the message which was sent under the current transaction. Check it carries
+                // TransactionalState with the above txnId but has no outcome. Respond with a
+                // TransactionalState with Accepted outcome.
+                IMessage message = await session.CreateMessageAsync();
+                for (int i = 0; i < 3; i++)
+                {
+                    testPeer.ExpectTransfer(messageMatcher: Assert.NotNull,
+                        stateMatcher: state =>
+                        {
+                            Assert.IsInstanceOf<TransactionalState>(state);
+                            TransactionalState transactionalState = (TransactionalState) state;
+                            CollectionAssert.AreEqual(txnId, transactionalState.TxnId);
+                            Assert.IsNull(transactionalState.Outcome);
+                        },
+                        responseState: new TransactionalState() { TxnId = txnId, Outcome = new Accepted() },
+                        responseSettled: true);
+
+                    message.Properties.SetInt("sequence", i);
+
+                    await producer.SendAsync(message);
+                }
+
+                // Expect rollback on close without a commit call.
+                testPeer.ExpectDischarge(txnId, dischargeState: true);
+                testPeer.ExpectClose();
+
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestRollbackTransactedSessionWithConsumerReceivingAllMessages()
+        {
+            await DoRollbackTransactedSessionWithConsumerTestImpl(1, 1, false);
+        }
+
+        [Test, Timeout(20_000), Ignore("Until deferred close is implemented for AmqpConsumer")]
+        public async Task TestRollbackTransactedSessionWithConsumerReceivingAllMessagesThenCloses()
+        {
+            await DoRollbackTransactedSessionWithConsumerTestImpl(1, 1, true);
+        }
+
+        [Test, Timeout(20_000), Ignore("TODO: Fix")]
+        public async Task TestRollbackTransactedSessionWithConsumerReceivingSomeMessages()
+        {
+            await DoRollbackTransactedSessionWithConsumerTestImpl(5, 2, false);
+        }
+
+        [Test, Timeout(20_000), Ignore("Until deferred close is implemented for AmqpConsumer")]
+        public async Task TestRollbackTransactedSessionWithConsumerReceivingSomeMessagesThenCloses()
+        {
+            await DoRollbackTransactedSessionWithConsumerTestImpl(5, 2, true);
+        }
+
+        private async Task DoRollbackTransactedSessionWithConsumerTestImpl(int transferCount, int consumeCount, bool closeConsumer)
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                // First expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a declared disposition state containing the txnId.
+                byte[] txnId = { 1, 2, 3, 4 };
+                testPeer.ExpectDeclare(txnId);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), transferCount);
+
+                for (int i = 1; i <= consumeCount; i++)
+                {
+                    // Then expect a *settled* TransactionalState disposition for each message once received by the consumer
+                    testPeer.ExpectDisposition(settled: true, stateMatcher: state =>
+                    {
+                        Assert.IsInstanceOf<TransactionalState>(state);
+                        var transactionalState = (TransactionalState) state;
+                        Assert.AreEqual(txnId, transactionalState.TxnId);
+                        Assert.IsInstanceOf<Accepted>(transactionalState.Outcome);
+                    });
+                }
+
+                IMessageConsumer messageConsumer = await session.CreateConsumerAsync(queue);
+
+                for (int i = 1; i <= consumeCount; i++)
+                {
+                    IMessage receivedMessage = await messageConsumer.ReceiveAsync(TimeSpan.FromSeconds(3));
+                    Assert.IsNotNull(receivedMessage);
+                    Assert.IsInstanceOf<ITextMessage>(receivedMessage);
+                }
+
+                // Expect the consumer to be 'stopped' prior to rollback by issuing a 'drain'
+                testPeer.ExpectLinkFlow(drain: true, sendDrainFlowResponse: true, creditMatcher: c => Assert.AreEqual(0, c));
+
+                if (closeConsumer)
+                {
+                    // Expect the messages that were not consumed to be released
+                    int unconsumed = transferCount - consumeCount;
+                    for (int i = 1; i <= unconsumed; i++)
+                    {
+                        testPeer.ExpectDispositionThatIsReleasedAndSettled();
+                    }
+
+                    // Expect an unsettled 'discharge' transfer to the txn coordinator containing the txnId,
+                    // and reply with accepted and settled disposition to indicate the commit succeeded
+                    testPeer.ExpectDischarge(txnId, dischargeState: false);
+
+                    // Then expect an unsettled 'declare' transfer to the txn coordinator, and
+                    // reply with a declared disposition state containing the txnId.
+                    testPeer.ExpectDeclare(txnId);
+
+                    // Now the deferred close should be performed.
+                    testPeer.ExpectDetach(expectClosed: true, sendResponse: true, replyClosed: true);
+
+                    await messageConsumer.CloseAsync();
+                }
+                else
+                {
+                    // Expect an unsettled 'discharge' transfer to the txn coordinator containing the txnId,
+                    // and reply with accepted and settled disposition to indicate the rollback succeeded
+                    testPeer.ExpectDischarge(txnId, dischargeState: true);
+
+                    // Then expect an unsettled 'declare' transfer to the txn coordinator, and
+                    // reply with a declared disposition state containing the txnId.
+                    testPeer.ExpectDeclare(txnId);
+
+                    // Expect the messages that were not consumed to be released
+                    int unconsumed = transferCount - consumeCount;
+                    for (int i = 1; i <= unconsumed; i++)
+                    {
+                        testPeer.ExpectDispositionThatIsReleasedAndSettled();
+                    }
+
+                    // Expect the consumer to be 'started' again as rollback completes
+                    testPeer.ExpectLinkFlow(drain: false, sendDrainFlowResponse: false, creditMatcher: c => Assert.Greater(c, 0));
+                }
+
+                testPeer.ExpectDischarge(txnId, dischargeState: true);
+                await session.RollbackAsync();
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        // TODO: 
+        // TestRollbackTransactedSessionWithPrefetchFullBeforeStoppingConsumer
+        // TestRollbackTransactedSessionWithPrefetchFullyUtilisedByDrainWhenStoppingConsumer
+
+        [Test, Timeout(20_000)]
+        public async Task TestDefaultOutcomeIsModifiedForConsumerSourceOnTransactedSession()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                // First expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a declared disposition state containing the txnId.
+                byte[] txnId = { 1, 2, 3, 4 };
+                testPeer.ExpectDeclare(txnId);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+                string queueName = "myQueue";
+                IQueue queue = await session.GetQueueAsync(queueName);
+
+                testPeer.ExpectReceiverAttach(linkNameMatcher: Assert.IsNotNull, targetMatcher: Assert.IsNotNull, sourceMatcher: source =>
+                {
+                    Assert.AreEqual(queueName, source.Address);
+                    Assert.IsFalse(source.Dynamic);
+                    CollectionAssert.Contains(source.Outcomes, SymbolUtil.ATTACH_OUTCOME_ACCEPTED);
+                    CollectionAssert.Contains(source.Outcomes, SymbolUtil.ATTACH_OUTCOME_REJECTED);
+                    CollectionAssert.Contains(source.Outcomes, SymbolUtil.ATTACH_OUTCOME_RELEASED);
+                    CollectionAssert.Contains(source.Outcomes, SymbolUtil.ATTACH_OUTCOME_MODIFIED);
+
+                    Assert.IsInstanceOf<Modified>(source.DefaultOutcome);
+                    Modified modified = (Modified) source.DefaultOutcome;
+                    Assert.IsTrue(modified.DeliveryFailed);
+                    Assert.IsFalse(modified.UndeliverableHere);
+                });
+
+                testPeer.ExpectLinkFlow();
+                testPeer.ExpectDischarge(txnId, dischargeState: true);
+                await session.CreateConsumerAsync(queue);
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestCoordinatorLinkSupportedOutcomes()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach(sourceMatcher: s =>
+                {
+                    Source source = (Source) s;
+                    CollectionAssert.Contains(source.Outcomes, SymbolUtil.ATTACH_OUTCOME_ACCEPTED);
+                    CollectionAssert.Contains(source.Outcomes, SymbolUtil.ATTACH_OUTCOME_REJECTED);
+                    CollectionAssert.Contains(source.Outcomes, SymbolUtil.ATTACH_OUTCOME_RELEASED);
+                    CollectionAssert.Contains(source.Outcomes, SymbolUtil.ATTACH_OUTCOME_MODIFIED);
+                });
+
+                // First expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a declared disposition state containing the txnId.
+                byte[] txnId = { 1, 2, 3, 4 };
+                testPeer.ExpectDeclare(txnId);
+
+                await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+
+                //Expect rollback on close
+                testPeer.ExpectDischarge(txnId, dischargeState: true);
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestRollbackErrorCoordinatorClosedOnCommit()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                byte[] txnId1 = { 5, 6, 7, 8 };
+                byte[] txnId2 = { 1, 2, 3, 4 };
+
+                testPeer.ExpectDeclare(txnId1);
+                testPeer.RemotelyCloseLastCoordinatorLinkOnDischarge(txnId: txnId1, dischargeState: false, nextTxnId: txnId2);
+                testPeer.ExpectCoordinatorAttach();
+                testPeer.ExpectDeclare(txnId2);
+                testPeer.ExpectDischarge(txnId2, dischargeState: true);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+
+                Assert.CatchAsync<TransactionRolledBackException>(async () => await session.CommitAsync(), "Transaction should have rolled back");
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestRollbackErrorWhenCoordinatorRemotelyClosed()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                byte[] txnId = { 5, 6, 7, 8 };
+                testPeer.ExpectDeclare(txnId);
+                testPeer.RemotelyCloseLastCoordinatorLink();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectCoordinatorAttach();
+                testPeer.ExpectDeclare(txnId);
+
+                testPeer.ExpectDischarge(txnId, true);
+
+                Assert.CatchAsync<TransactionRolledBackException>(async () => await session.CommitAsync(), "Transaction should have rolled back");
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestNMSErrorCoordinatorClosedOnRollback()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                byte[] txnId1 = { 5, 6, 7, 8 };
+                byte[] txnId2 = { 1, 2, 3, 4 };
+
+                testPeer.ExpectDeclare(txnId1);
+                testPeer.RemotelyCloseLastCoordinatorLinkOnDischarge(txnId: txnId1, dischargeState: true, nextTxnId: txnId2);
+                testPeer.ExpectCoordinatorAttach();
+                testPeer.ExpectDeclare(txnId2);
+                testPeer.ExpectDischarge(txnId2, dischargeState: true);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+
+                Assert.CatchAsync<NMSException>(async () => await session.RollbackAsync(), "Rollback should have thrown a NMSException");
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestNMSExceptionOnRollbackWhenCoordinatorRemotelyClosed()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                byte[] txnId = { 5, 6, 7, 8 };
+                testPeer.ExpectDeclare(txnId);
+                testPeer.RemotelyCloseLastCoordinatorLink();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                testPeer.ExpectCoordinatorAttach();
+                testPeer.ExpectDeclare(txnId);
+
+                testPeer.ExpectDischarge(txnId, dischargeState: true);
+
+                Assert.CatchAsync<NMSException>(async () => await session.RollbackAsync(), "Rollback should have thrown a NMSException");
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSendAfterCoordinatorLinkClosedDuringTX()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                // First expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a Declared disposition state containing the txnId.
+                byte[] txnId = { 5, 6, 7, 8 };
+                testPeer.ExpectDeclare(txnId);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                // Create a producer to use in provoking creation of the AMQP transaction
+                testPeer.ExpectSenderAttach();
+
+                // Close the link, the messages should now just get dropped on the floor.
+                testPeer.RemotelyCloseLastCoordinatorLink();
+
+                IMessageProducer producer = await session.CreateProducerAsync(queue);
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                await producer.SendAsync(await session.CreateMessageAsync());
+
+                // Expect that a new link will be created in order to start the next TX.
+                txnId = new byte[] { 1, 2, 3, 4 };
+                testPeer.ExpectCoordinatorAttach();
+                testPeer.ExpectDeclare(txnId);
+
+                // Expect that the session TX will rollback on close.
+                testPeer.ExpectDischarge(txnId, dischargeState: true);
+
+                Assert.CatchAsync<TransactionRolledBackException>(async () => await session.CommitAsync(), "Commit operation should have failed.");
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestReceiveAfterCoordinatorLinkClosedDuringTX()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                // First expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a Declared disposition state containing the txnId.
+                byte[] txnId = { 5, 6, 7, 8 };
+                testPeer.ExpectDeclare(txnId);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                // Create a consumer and send it an initial message for receive to process.
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithNullContent());
+
+                // Close the link, the messages should now just get dropped on the floor.
+                testPeer.RemotelyCloseLastCoordinatorLink();
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+
+                // receiving the message would normally ack it, since the TX is failed this
+                // should not result in a disposition going out.
+                IMessage received = await consumer.ReceiveAsync();
+                Assert.IsNotNull(received);
+
+                // Expect that a new link will be created in order to start the next TX.
+                txnId = new byte[] { 1, 2, 3, 4 };
+                testPeer.ExpectCoordinatorAttach();
+                testPeer.ExpectDeclare(txnId);
+
+                // Expect that the session TX will rollback on close.
+                testPeer.ExpectDischarge(txnId, dischargeState: true);
+
+                Assert.CatchAsync<TransactionRolledBackException>(async () => await session.CommitAsync(), "Commit operation should have failed.");
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSessionCreateFailsOnDeclareTimeout()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer, "nms.requestTimeout=500");
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+                testPeer.ExpectDeclareButDoNotRespond();
+
+                // Expect the AMQP session to be closed due to the NMS session creation failure.
+                testPeer.ExpectEnd();
+
+                // TODO: Replace NMSException with sth more specific, in qpid-jms it is JmsOperationTimedOutException
+                Assert.CatchAsync<NMSException>(async () => await connection.CreateSessionAsync(AcknowledgementMode.Transactional), "Should have timed out waiting for declare.");
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSessionCreateFailsOnDeclareRejection()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer, "nms.closeTimeout=100");
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                // First expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a Rejected disposition state to indicate failure.
+                testPeer.ExpectDeclareAndReject();
+
+                // Expect the AMQP session to be closed due to the NMS session creation failure.
+                testPeer.ExpectEnd();
+
+                Assert.CatchAsync<NMSException>(async () => await connection.CreateSessionAsync(AcknowledgementMode.Transactional));
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestSessionCreateFailsOnCoordinatorLinkRefusal()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer, "nms.closeTimeout=100");
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+
+                // Expect coordinator link, refuse it, expect detach reply
+                string errorMessage = "CoordinatorLinkRefusal-breadcrumb";
+                testPeer.ExpectCoordinatorAttach(refuseLink: true, error: new Error(ErrorCode.NotImplemented) { Description = errorMessage });
+                testPeer.ExpectDetach(expectClosed: true, sendResponse: false, replyClosed: false);
+
+                // Expect the AMQP session to be closed due to the NMS session creation failure.
+                testPeer.ExpectEnd();
+
+                NMSException exception = Assert.CatchAsync<NMSException>(async () => await connection.CreateSessionAsync(AcknowledgementMode.Transactional));
+                Assert.IsTrue(exception.Message.Contains(errorMessage), "Expected exception message to contain breadcrumb");
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestTransactionRolledBackOnSessionCloseTimesOut()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer, "nms.requestTimeout=500");
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                byte[] txnId = { 5, 6, 7, 8 };
+                testPeer.ExpectDeclare(txnId);
+
+                // Closed session should roll-back the TX with a failed discharge
+                testPeer.ExpectDischargeButDoNotRespond(txnId, dischargeState: true);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+
+                // TODO: Replace NMSException with sth more specific, in qpid-jms it is JmsOperationTimedOutException
+                Assert.CatchAsync<NMSException>(async () => await session.CloseAsync(), "Should have timed out waiting for discharge.");
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestTransactionRolledBackTimesOut()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer, "nms.requestTimeout=500");
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                byte[] txnId1 = { 5, 6, 7, 8 };
+                byte[] txnId2 = { 1, 2, 3, 4 };
+                testPeer.ExpectDeclare(txnId1);
+
+                // Expect discharge but don't respond so that the request timeout kicks in and fails
+                // the discharge.  The pipelined declare should arrive as well and be discharged as the
+                // client attempts to recover to a known good state.
+                testPeer.ExpectDischargeButDoNotRespond(txnId1, dischargeState: true);
+
+                // Session should throw from the rollback and then try and recover.
+                testPeer.ExpectDeclare(txnId2);
+                testPeer.ExpectDischarge(txnId2, dischargeState: true);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+
+                // TODO: Replace NMSException with sth more specific, in qpid-jms it is JmsOperationTimedOutException
+                Assert.CatchAsync<NMSException>(async () => await session.RollbackAsync(), "Should have timed out waiting for discharge.");
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestTransactionCommitTimesOut()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer, "nms.requestTimeout=500");
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                byte[] txnId1 = { 5, 6, 7, 8 };
+                byte[] txnId2 = { 1, 2, 3, 4 };
+                testPeer.ExpectDeclare(txnId1);
+
+                // Expect discharge but don't respond so that the request timeout kicks in and fails
+                // the discharge.  The pipelined declare should arrive as well and be discharged as the
+                // client attempts to recover to a known good state.
+                testPeer.ExpectDischargeButDoNotRespond(txnId1, dischargeState: false);
+                testPeer.ExpectDeclare(txnId2);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+
+                // TODO: Replace NMSException with sth more specific, in qpid-jms it is JmsOperationTimedOutException
+                Assert.CatchAsync<NMSException>(async () => await session.CommitAsync(), "Should have timed out waiting for discharge.");
+
+                // Session rolls back on close
+                testPeer.ExpectDischarge(txnId2, dischargeState: true);
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000), Ignore("TODO: Fix")]
+        public async Task TestTransactionCommitTimesOutAndNoNextBeginTimesOut()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer, "nms.requestTimeout=500");
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                byte[] txnId1 = { 5, 6, 7, 8 };
+                byte[] txnId2 = { 1, 2, 3, 4 };
+                testPeer.ExpectDeclare(txnId1);
+
+                // Expect discharge and don't respond so that the request timeout kicks in
+                // Expect pipelined declare and don't response so that the request timeout kicks in.
+                // The commit operation should throw a timed out exception at that point.
+                testPeer.ExpectDischargeButDoNotRespond(txnId1, dischargeState: false);
+                testPeer.ExpectDeclareButDoNotRespond();
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+
+                // After the pipelined operations both time out, the session should attempt to
+                // recover by creating a new TX, then on close the session should roll it back
+                testPeer.ExpectDeclare(txnId2);
+                testPeer.ExpectDischarge(txnId2, dischargeState: true);
+
+                // TODO: Replace NMSException with sth more specific, in qpid-jms it is JmsOperationTimedOutException
+                Assert.CatchAsync<NMSException>(async () => await session.CommitAsync(), "Should have timed out waiting for discharge.");
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000), Ignore("TODO: Fix")]
+        public async Task TestRollbackWithNoResponseForSuspendConsumer()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                // First expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a declared disposition state containing the txnId.
+                byte[] txnId = { 5, 6, 7, 8 };
+                testPeer.ExpectDeclare(txnId);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithContent(), count: 1);
+
+                // Then expect a *settled* TransactionalState disposition for the message once received by the consumer
+                testPeer.ExpectDisposition(settled: true, state =>
+                {
+                    var transactionalState = (TransactionalState) state;
+                    CollectionAssert.AreEqual(txnId, transactionalState.TxnId);
+                    Assert.IsInstanceOf<Accepted>(transactionalState.Outcome);
+                });
+
+                // Read one so we try to suspend on rollback
+                IMessageConsumer messageConsumer = await session.CreateConsumerAsync(queue);
+                IMessage receivedMessage = await messageConsumer.ReceiveAsync(TimeSpan.FromSeconds(3));
+
+                Assert.NotNull(receivedMessage);
+                Assert.IsInstanceOf<ITextMessage>(receivedMessage);
+
+                // Expect the consumer to be 'stopped' prior to rollback by issuing a 'drain'
+                testPeer.ExpectLinkFlow(drain: true, sendDrainFlowResponse: false);
+
+                // Expect an unsettled 'discharge' transfer to the txn coordinator containing the txnId,
+                // and reply with accepted and settled disposition to indicate the rollback succeeded
+                testPeer.ExpectDischarge(txnId, dischargeState: true);
+
+                testPeer.ExpectDeclare(txnId);
+                testPeer.ExpectDischarge(txnId, dischargeState: true);
+
+                Assert.CatchAsync<NMSException>(async () => await session.RollbackAsync(), "Should throw a timed out exception");
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(1000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestConsumerMessageOrderOnTransactedSession()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                int messageCount = 10;
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                // First expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a declared disposition state containing the txnId.
+                byte[] txnId = { 5, 6, 7, 8 };
+                testPeer.ExpectDeclare(txnId);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                // Expect the browser enumeration to create a underlying consumer
+                testPeer.ExpectReceiverAttach();
+
+                // Expect initial credit to be sent, respond with some messages that are tagged with
+                // a sequence number we can use to determine if order is maintained.
+                testPeer.ExpectLinkFlowRespondWithTransfer(CreateMessageWithNullContent(), count: messageCount, addMessageNumberProperty: true);
+
+                for (int i = 1; i <= messageCount; i++)
+                {
+                    // Then expect an *settled* TransactionalState disposition for each message once received by the consumer
+                    testPeer.ExpectSettledTransactionalDisposition(txnId);
+                }
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+                for (int i = 0; i < messageCount; i++)
+                {
+                    IMessage message = await consumer.ReceiveAsync(TimeSpan.FromMilliseconds(500));
+                    Assert.IsNotNull(message);
+                    Assert.AreEqual(i, message.Properties.GetInt(TestAmqpPeer.MESSAGE_NUMBER));
+                }
+
+                // Expect an unsettled 'discharge' transfer to the txn coordinator containing the txnId,
+                // and reply with accepted and settled disposition to indicate the rollback succeeded
+                testPeer.ExpectDischarge(txnId, true);
+                testPeer.ExpectEnd();
+
+                await session.CloseAsync();
+
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+
+                testPeer.WaitForAllMatchersToComplete(3000);
+            }
+        }
+
+        [Test, Timeout(20_000)]
+        public async Task TestConsumeManyWithSingleTXPerMessage()
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                IConnection connection = await EstablishConnectionAsync(testPeer);
+                await connection.StartAsync();
+
+                int messageCount = 10;
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectCoordinatorAttach();
+
+                var txnIdQueue = new Queue<byte[]>(3);
+                txnIdQueue.Enqueue(new byte[] { 1, 2, 3, 4 });
+                txnIdQueue.Enqueue(new byte[] { 2, 4, 6, 8 });
+                txnIdQueue.Enqueue(new byte[] { 5, 4, 3, 2 });
+
+                // First expect an unsettled 'declare' transfer to the txn coordinator, and
+                // reply with a declared disposition state containing the txnId.
+                byte[] txnId = txnIdQueue.Dequeue();
+                txnIdQueue.Enqueue(txnId);
+                testPeer.ExpectDeclare(txnId);
+
+                ISession session = await connection.CreateSessionAsync(AcknowledgementMode.Transactional);
+                IQueue queue = await session.GetQueueAsync("myQueue");
+
+                // Expect the browser enumeration to create a underlying consumer
+                testPeer.ExpectReceiverAttach();
+
+                // Expect initial credit to be sent, respond with some messages that are tagged with
+                // a sequence number we can use to determine if order is maintained.
+                testPeer.ExpectLinkFlowRespondWithTransfer(message: CreateMessageWithNullContent(), count: messageCount, addMessageNumberProperty: true);
+
+                IMessageConsumer consumer = await session.CreateConsumerAsync(queue);
+
+                for (int i = 0; i < messageCount; i++)
+                {
+                    // Then expect an *settled* TransactionalState disposition for each message once received by the consumer
+                    testPeer.ExpectSettledTransactionalDisposition(txnId);
+
+                    IMessage message = await consumer.ReceiveAsync(TimeSpan.FromMilliseconds(500));
+                    Assert.NotNull(message);
+                    Assert.AreEqual(i, message.Properties.GetInt(TestAmqpPeer.MESSAGE_NUMBER));
+
+                    // Expect an unsettled 'discharge' transfer to the txn coordinator containing the txnId,
+                    // and reply with accepted and settled disposition to indicate the commit succeeded
+                    testPeer.ExpectDischarge(txnId, dischargeState: false);
+
+                    // Expect the next transaction to start.
+                    txnId = txnIdQueue.Dequeue();
+                    txnIdQueue.Enqueue(txnId);
+                    testPeer.ExpectDeclare(txnId);
+                    
+                    await session.CommitAsync();
+                }
+                
+                // Expect an unsettled 'discharge' transfer to the txn coordinator containing the txnId,
+                // and reply with accepted and settled disposition to indicate the rollback succeeded
+                testPeer.ExpectDischarge(txnId, dischargeState: true);
+                testPeer.ExpectEnd();
+
+                await session.CloseAsync();
+                
+                testPeer.ExpectClose();
+                await connection.CloseAsync();
+                
+                testPeer.WaitForAllMatchersToComplete(3000);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Apache-NMS-AMQP-Test/Integration/ConsumerIntegrationTest.cs b/test/Apache-NMS-AMQP-Test/Integration/ConsumerIntegrationTest.cs
index cdb25e6..f42ba51 100644
--- a/test/Apache-NMS-AMQP-Test/Integration/ConsumerIntegrationTest.cs
+++ b/test/Apache-NMS-AMQP-Test/Integration/ConsumerIntegrationTest.cs
@@ -459,6 +459,8 @@
                 testPeer.WaitForAllMatchersToComplete(2000);
             }
         }
+        
+       
 
         [Test, Timeout(20_000)]
         public void TestCreateProducerInOnMessage()
diff --git a/test/Apache-NMS-AMQP-Test/Integration/IntegrationTestFixture.cs b/test/Apache-NMS-AMQP-Test/Integration/IntegrationTestFixture.cs
index fac9499..52af9af 100644
--- a/test/Apache-NMS-AMQP-Test/Integration/IntegrationTestFixture.cs
+++ b/test/Apache-NMS-AMQP-Test/Integration/IntegrationTestFixture.cs
@@ -15,10 +15,12 @@
  * limitations under the License.
  */
 
+using System.Threading.Tasks;
 using Amqp.Framing;
 using Amqp.Types;
 using Apache.NMS;
 using Apache.NMS.AMQP;
+using Apache.NMS.AMQP.Util.Synchronization;
 using NMS.AMQP.Test.TestAmqp;
 
 namespace NMS.AMQP.Test.Integration
@@ -29,9 +31,14 @@
         {
             Tracer.Trace = new NLogAdapter();
         }
-        
+
         protected IConnection EstablishConnection(TestAmqpPeer testPeer, string optionsString = null, Symbol[] serverCapabilities = null, Fields serverProperties = null, bool setClientId = true)
         {
+            return EstablishConnectionAsync(testPeer, optionsString, serverCapabilities, serverProperties, setClientId).GetAsyncResult();
+        }
+        
+        protected async Task<IConnection> EstablishConnectionAsync(TestAmqpPeer testPeer, string optionsString = null, Symbol[] serverCapabilities = null, Fields serverProperties = null, bool setClientId = true)
+        {
             testPeer.ExpectSaslPlain("guest", "guest");
             testPeer.ExpectOpen(serverCapabilities: serverCapabilities, serverProperties: serverProperties);
 
@@ -40,7 +47,7 @@
 
             var remoteUri = BuildUri(testPeer, optionsString);
             var connectionFactory = new NmsConnectionFactory(remoteUri);
-            var connection = connectionFactory.CreateConnection("guest", "guest");
+            var connection = await connectionFactory.CreateConnectionAsync("guest", "guest");
             if (setClientId)
             {
                 // Set a clientId to provoke the actual AMQP connection process to occur.
@@ -52,6 +59,11 @@
 
         protected INMSContext EstablishNMSContext(TestAmqpPeer testPeer, string optionsString = null, Symbol[] serverCapabilities = null, Fields serverProperties = null, bool setClientId = true, AcknowledgementMode acknowledgementMode = AcknowledgementMode.AutoAcknowledge)
         {
+            return EstablishNMSContextAsync(testPeer, optionsString, serverCapabilities, serverProperties, setClientId, acknowledgementMode).GetAsyncResult();
+        }
+        
+        protected async Task<INMSContext> EstablishNMSContextAsync(TestAmqpPeer testPeer, string optionsString = null, Symbol[] serverCapabilities = null, Fields serverProperties = null, bool setClientId = true, AcknowledgementMode acknowledgementMode = AcknowledgementMode.AutoAcknowledge)
+        {
             testPeer.ExpectSaslPlain("guest", "guest");
             testPeer.ExpectOpen(serverCapabilities: serverCapabilities, serverProperties: serverProperties);
 
@@ -60,7 +72,7 @@
 
             var remoteUri = BuildUri(testPeer, optionsString);
             var connectionFactory = new NmsConnectionFactory(remoteUri);
-            var context = connectionFactory.CreateContext("guest", "guest", acknowledgementMode);
+            var context = await connectionFactory.CreateContextAsync("guest", "guest", acknowledgementMode);
             if (setClientId)
             {
                 // Set a clientId to provoke the actual AMQP connection process to occur.
@@ -83,12 +95,17 @@
                 return baseUri + "?" + optionsString;
 
         }
-        
+
         protected static Amqp.Message CreateMessageWithContent()
         {
             return new Amqp.Message() { BodySection = new AmqpValue() { Value = "content" } };
         }
         
+        protected static Amqp.Message CreateMessageWithValueContent(object value)
+        {
+            return new Amqp.Message() { BodySection = new AmqpValue() { Value = value } };
+        }
+        
         protected static Amqp.Message CreateMessageWithNullContent()
         {
             return new Amqp.Message() { BodySection = new AmqpValue() { Value = null } };
diff --git a/test/Apache-NMS-AMQP-Test/Integration/MessageDeliveryTimeTest.cs b/test/Apache-NMS-AMQP-Test/Integration/MessageDeliveryTimeTest.cs
index dbe936a..6c85757 100644
--- a/test/Apache-NMS-AMQP-Test/Integration/MessageDeliveryTimeTest.cs
+++ b/test/Apache-NMS-AMQP-Test/Integration/MessageDeliveryTimeTest.cs
@@ -78,7 +78,7 @@
         {
             using (TestAmqpPeer testPeer = new TestAmqpPeer())
             {
-                var connection = EstablishConnection(testPeer, "amqp.traceFrames=true");
+                var connection = EstablishConnection(testPeer);
                 connection.Start();
                 testPeer.ExpectBegin();
                 var session = connection.CreateSession(AcknowledgementMode.AutoAcknowledge);
diff --git a/test/Apache-NMS-AMQP-Test/Integration/NMSConsumerIntegrationTest.cs b/test/Apache-NMS-AMQP-Test/Integration/NMSConsumerIntegrationTest.cs
index c1bb33b..542f720 100644
--- a/test/Apache-NMS-AMQP-Test/Integration/NMSConsumerIntegrationTest.cs
+++ b/test/Apache-NMS-AMQP-Test/Integration/NMSConsumerIntegrationTest.cs
@@ -16,10 +16,12 @@
  */
 
 using System;
+using System.Collections.Generic;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using Amqp.Framing;
+using Amqp.Types;
 using Apache.NMS;
 using Apache.NMS.AMQP.Message;
 using Apache.NMS.AMQP.Util;
@@ -72,7 +74,7 @@
         //             .Setup(listener => listener.OnConsumerClosed(It.IsAny<IMessageConsumer>(), It.IsAny<Exception>()))
         //             .Callback(() => consumerClosed.Set());
         //
-        //         var context = (NmsContext) EstablishNMSContext(testPeer, "amqp.traceFrames=true");
+        //         var context = (NmsContext) EstablishNMSContext(testPeer);
         //         context.ConnectionInterruptedListener += () => { consumerClosed.Set(); };// AddConnectionListener(mockConnectionListener.Object);}
         //         // context.list ConnectionInterruptedListener += () => { consumerClosed.Set(); };// AddConnectionListener(mockConnectionListener.Object);}
         //         context.ExceptionListener += exception => { exceptionFired.Set(); };
@@ -971,5 +973,91 @@
                 testPeer.WaitForAllMatchersToComplete(2000);
             }
         }
+
+        [TestCaseSource("TestReceiveBodyCaseSource")]
+        [Timeout(20_000)]
+        public void TestReceiveBody<T>(T inputValue)
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = EstablishNMSContext(testPeer);
+
+                testPeer.ExpectBegin();
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(CreateMessageWithValueContent(inputValue));
+                testPeer.ExpectDisposition(true, _ => { } );
+                
+
+                IQueue destination = context.GetQueue("myQueue");
+                var consumer = context.CreateConsumer(destination);
+
+                T body = consumer.ReceiveBody<T>();
+                Assert.AreEqual(inputValue, body);
+                Assert.AreNotSame(inputValue, body);
+
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                
+                context.Close();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+        
+        [TestCaseSource("TestReceiveBodyCaseSource")]
+        [Timeout(20_000)]
+        public void TestReceiveBodyNoWait<T>(T inputValue)
+        {
+            using (TestAmqpPeer testPeer = new TestAmqpPeer())
+            {
+                var context = EstablishNMSContext(testPeer);
+
+                ManualResetEvent beforeFlow = new ManualResetEvent(false);
+                testPeer.ExpectBegin();
+                testPeer.ExpectReceiverAttach();
+                testPeer.ExpectLinkFlowRespondWithTransfer(CreateMessageWithValueContent(inputValue), 1, false, false, false, false,
+                    (credit) => { beforeFlow.WaitOne(); }, 1);
+                testPeer.ExpectDisposition(true, _ => { } );
+                
+                IQueue destination = context.GetQueue("myQueue");
+                var consumer = context.CreateConsumer(destination);
+
+                T initialBody = consumer.ReceiveBodyNoWait<T>();
+                // Assert initially its null
+                Assert.AreEqual(default(T), initialBody);
+                
+                // Release and allow link to flow
+                beforeFlow.Set();
+                // Give short time to arrive
+                Thread.Sleep(100);
+                
+                T body = consumer.ReceiveBodyNoWait<T>();
+                Assert.AreEqual(inputValue, body);
+                Assert.AreNotSame(inputValue, body);
+
+
+                testPeer.ExpectEnd();
+                testPeer.ExpectClose();
+                
+                context.Close();
+
+                testPeer.WaitForAllMatchersToComplete(2000);
+            }
+        }
+
+        public static IEnumerable<object> TestReceiveBodyCaseSource()
+        {
+            yield return new Map()
+            {
+                ["Parameter1"] = "test",
+                ["Parameter2"] = 23423
+            };
+            yield return 1233;
+            yield return "test";
+            yield return (uint) 1233;
+            yield return (ulong) 1233;
+            yield return (long) -1233;
+        }
     }
 }
\ No newline at end of file
diff --git a/test/Apache-NMS-AMQP-Test/Message/Facade/AmqpNmsMessageFacadeTest.cs b/test/Apache-NMS-AMQP-Test/Message/Facade/AmqpNmsMessageFacadeTest.cs
new file mode 100644
index 0000000..4aef2b4
--- /dev/null
+++ b/test/Apache-NMS-AMQP-Test/Message/Facade/AmqpNmsMessageFacadeTest.cs
@@ -0,0 +1,29 @@
+using System;
+using Apache.NMS.AMQP.Message;
+using Apache.NMS.AMQP.Provider.Amqp;
+using Apache.NMS.AMQP.Provider.Amqp.Message;
+using Apache.NMS.AMQP.Util;
+using Apache.NMS.Util;
+using Moq;
+using NUnit.Framework;
+
+namespace NMS.AMQP.Test.Message.Facade
+{
+    [TestFixture]
+    public class AmqpNmsMessageFacadeTest
+    {
+        [Test]
+        public void TestNMSDeliveryTime_Set_ShouldHaveMessageAnnotations()
+        {
+            AmqpNmsMessageFacade msg = new AmqpNmsMessageFacade();
+            
+            Mock<IAmqpConnection> mockAmqpConnection = new Mock<IAmqpConnection>();
+            
+            msg.Initialize(mockAmqpConnection.Object);
+            var deliveryTime = DateTime.UtcNow.AddMinutes(2);
+            msg.DeliveryTime = deliveryTime;
+
+            Assert.AreEqual(new DateTimeOffset(deliveryTime).ToUnixTimeMilliseconds(), msg.MessageAnnotations[SymbolUtil.NMS_DELIVERY_TIME]);
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Apache-NMS-AMQP-Test/Message/Foreign/ForeignNmsMessage.cs b/test/Apache-NMS-AMQP-Test/Message/Foreign/ForeignNmsMessage.cs
index e9f0c38..d25eaed 100644
--- a/test/Apache-NMS-AMQP-Test/Message/Foreign/ForeignNmsMessage.cs
+++ b/test/Apache-NMS-AMQP-Test/Message/Foreign/ForeignNmsMessage.cs
@@ -16,6 +16,7 @@
  */
 
 using System;
+using System.Threading.Tasks;
 using Apache.NMS;
 using Apache.NMS.AMQP.Message;
 using NMS.AMQP.Test.Message.Facade;
@@ -31,6 +32,11 @@
             message.Acknowledge();
         }
 
+        public Task AcknowledgeAsync()
+        {
+            return message.AcknowledgeAsync();
+        }
+
         public void ClearBody()
         {
             message.ClearBody();
diff --git a/test/Apache-NMS-AMQP-Test/Provider/Mock/MockProvider.cs b/test/Apache-NMS-AMQP-Test/Provider/Mock/MockProvider.cs
index 9baccff..d6c26b6 100644
--- a/test/Apache-NMS-AMQP-Test/Provider/Mock/MockProvider.cs
+++ b/test/Apache-NMS-AMQP-Test/Provider/Mock/MockProvider.cs
@@ -72,6 +72,11 @@
             }
         }
 
+        public async Task CloseAsync()
+        {
+            Close();
+        }
+
         public void SetProviderListener(IProviderListener providerListener)
         {
             listener = providerListener;
diff --git a/test/Apache-NMS-AMQP-Test/TestAmqp/TestAmqpPeerRunner.cs b/test/Apache-NMS-AMQP-Test/TestAmqp/TestAmqpPeerRunner.cs
index 21f0687..e0bb031 100644
--- a/test/Apache-NMS-AMQP-Test/TestAmqp/TestAmqpPeerRunner.cs
+++ b/test/Apache-NMS-AMQP-Test/TestAmqp/TestAmqpPeerRunner.cs
@@ -38,6 +38,8 @@
         private SocketAsyncEventArgs args;
         private Socket acceptSocket;
 
+        private bool pumpEnabled = true;
+
         public TestAmqpPeerRunner(TestAmqpPeer testAmqpPeer, IPEndPoint ipEndPoint)
         {
             this.testAmqpPeer = testAmqpPeer;
@@ -107,13 +109,13 @@
         {
             try
             {
-                while (true)
+                while (pumpEnabled)
                 {
                     byte[] buffer = new byte[8];
                     Read(stream, buffer, 0, 8);
                     testAmqpPeer.OnHeader(stream, buffer);
 
-                    while (true)
+                    while (pumpEnabled)
                     {
                         Read(stream, buffer, 0, 4);
                         int len = AmqpBitConverter.ReadInt(buffer, 0);
@@ -173,6 +175,8 @@
 
         public void Close()
         {
+            pumpEnabled = false;
+            
             acceptSocket?.Dispose();
             acceptSocket = null;
             
diff --git a/test/Apache-NMS-AMQP-Test/Utils/Synchronization/NmsSynchronizationMonitorTest.cs b/test/Apache-NMS-AMQP-Test/Utils/Synchronization/NmsSynchronizationMonitorTest.cs
new file mode 100644
index 0000000..6ac8e66
--- /dev/null
+++ b/test/Apache-NMS-AMQP-Test/Utils/Synchronization/NmsSynchronizationMonitorTest.cs
@@ -0,0 +1,437 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Apache.NMS.AMQP.Util.Synchronization;
+using NUnit.Framework;
+
+namespace NMS.AMQP.Test.Utils.Synchronization
+{
+    [TestFixture]
+    public class NmsSynchronizationMonitorTest
+    {
+        public class EventList
+        {
+            public List<string> list = new List<string>();
+            public Dictionary<string, List<string>> listsByPrefix = new Dictionary<string, List<string>>();
+
+            public void Add(string ev)
+            {
+                lock (this)
+                {
+                    list.Add(ev);
+                    string prefix = new string(new[] {ev[0]});
+                    if (!listsByPrefix.ContainsKey(prefix))
+                    {
+                        listsByPrefix[prefix] = new List<string>();
+                    }
+                    listsByPrefix[prefix].Add(ev);
+                }
+            }
+
+            public string ToString(string prefix)
+            {
+                return listsByPrefix.ContainsKey(prefix) ? string.Join("",listsByPrefix[prefix]) : string.Empty;
+            }
+            
+            public override string ToString()
+            {
+                return string.Join("", list);
+            }
+        }
+
+
+        [Test]
+        public void TestNestedLockAndWait()
+        {
+            NmsSynchronizationMonitor syncRoot = new NmsSynchronizationMonitor();
+            ManualResetEvent lockedEvent = new ManualResetEvent(false);
+
+            EventList evList = new EventList();
+
+            var task = Task.Run(() =>
+            {
+                Thread.Sleep(5);
+                using (syncRoot.Lock())
+                {
+                    evList.Add("A1");
+                    Thread.Sleep(1);
+                    evList.Add("A1");
+                    Thread.Sleep(1);
+                    evList.Add("A1");
+                    Thread.Sleep(1);
+
+                    using (syncRoot.Lock())
+                    {
+                        evList.Add("A1");
+                        Thread.Sleep(1);
+                        evList.Add("A1");
+                        Thread.Sleep(1);
+                        evList.Add("A1");
+                        Thread.Sleep(1);
+
+                        lockedEvent.Set();
+                        syncRoot.Wait();
+
+                        evList.Add("A2");
+                        Thread.Sleep(1);
+                        evList.Add("A2");
+                        Thread.Sleep(1);
+                        evList.Add("A2");
+                        Thread.Sleep(1);
+                    }
+
+                    evList.Add("A2");
+                    Thread.Sleep(1);
+                    evList.Add("A2");
+                    Thread.Sleep(1);
+                    evList.Add("A2");
+                    Thread.Sleep(1);
+                }
+            });
+
+            var taskB = Task.Run(() =>
+            {
+                while (!task.IsCompleted)
+                {
+                    using (syncRoot.Lock())
+                    {
+                        evList.Add("B");
+                        Thread.Sleep(1);
+                    }
+                }
+            });
+
+            lockedEvent.WaitOne();
+            Thread.Sleep(10); // to give Task A Time to go to sleep and B to work
+
+
+            Task.Run(() => { syncRoot.Pulse(); });
+
+            task.Wait();
+            taskB.Wait();
+
+
+            // Now Asses that block A1 and A2 are not intersected by B, however A1 And A2 should have some B between (during sleep period)
+            var events = evList.list.Select((a, i) => new Tuple<string, int>(a, i));
+            var a1 = events.Where(a => a.Item1 == "A1").ToList();
+            var a2 = events.Where(a => a.Item1 == "A2").ToList();
+            Assert.AreEqual(6, a1.Count());
+            Assert.AreEqual(5, a1.Last().Item2 - a1.First().Item2); // not intersected by anything
+
+            Assert.AreEqual(6, a2.Count());
+            Assert.AreEqual(5, a2.Last().Item2 - a2.First().Item2); // not intersected by anything
+
+            Assert.Greater(Math.Abs(a1.Last().Item2 - a2.First().Item2), 1, "A1 and A2, should be intersected by B, and not happening one right after another");
+        }
+
+        [Test]
+        public void TestNestedLockAndWaitAsync()
+        {
+            NmsSynchronizationMonitor syncRoot = new NmsSynchronizationMonitor();
+            ManualResetEvent lockedEvent = new ManualResetEvent(false);
+
+            EventList evList = new EventList();
+
+            var task = Task.Run(async () =>
+            {
+                Thread.Sleep(5);
+                using (await syncRoot.LockAsync())
+                {
+                    evList.Add("A1");
+                    await Task.Delay(1);
+                    await Task.Yield();
+                    evList.Add("A1");
+                    await Task.Delay(1);
+                    await Task.Yield();
+                    evList.Add("A1");
+                    await Task.Delay(1);
+                    await Task.Yield();
+
+                    using (await syncRoot.LockAsync())
+                    {
+                        evList.Add("A1");
+                        await Task.Delay(1);
+                        await Task.Yield();
+                        evList.Add("A1");
+                        await Task.Delay(1);
+                        await Task.Yield();
+                        evList.Add("A1");
+                        await Task.Delay(1);
+                        await Task.Yield();
+
+                        lockedEvent.Set();
+                        await syncRoot.WaitAsync();
+
+                        evList.Add("A2");
+                        await Task.Delay(1);
+                        await Task.Yield();
+                        evList.Add("A2");
+                        await Task.Delay(1);
+                        await Task.Yield();
+                        evList.Add("A2");
+                        await Task.Delay(1);
+                        await Task.Yield();
+                    }
+
+                    evList.Add("A2");
+                    await Task.Delay(1);
+                    await Task.Yield();
+                    evList.Add("A2");
+                    await Task.Delay(1);
+                    await Task.Yield();
+                    evList.Add("A2");
+                    await Task.Delay(1);
+                    await Task.Yield();
+                }
+            });
+
+            var taskB = Task.Run(async () =>
+            {
+                while (!task.IsCompleted)
+                {
+                    using (await syncRoot.LockAsync())
+                    {
+                        evList.Add("B");
+                        await Task.Delay(1);
+                    }
+                }
+            });
+
+            lockedEvent.WaitOne();
+            Thread.Sleep(10); // to give Task A Time to go to sleep and B to work
+
+
+            Task.Run(() => { syncRoot.Pulse(); });
+
+            task.Wait();
+            taskB.Wait();
+
+
+            // Now Asses that block A1 and A2 are not intersected by B, however A1 And A2 should have some B between (during sleep period)
+            var events = evList.list.Select((a, i) => new Tuple<string, int>(a, i));
+            var a1 = events.Where(a => a.Item1 == "A1").ToList();
+            var a2 = events.Where(a => a.Item1 == "A2").ToList();
+            Assert.AreEqual(6, a1.Count());
+            Assert.AreEqual(5, a1.Last().Item2 - a1.First().Item2); // not intersected by anything
+
+            Assert.AreEqual(6, a2.Count());
+            Assert.AreEqual(5, a2.Last().Item2 - a2.First().Item2); // not intersected by anything
+
+            Assert.Greater(Math.Abs(a1.Last().Item2 - a2.First().Item2), 1, "A1 and A2, should be intersected by B, and not happening one right after another");
+        }
+
+
+        [TestCase(1,3500,6)]
+        [TestCase(0,2000,200)]
+        [Timeout(20_000)]
+        public void TestConcurrentProducersSyncAndAsync(int sleepTimeMs, int testTimeMs, int minimumOccurences)
+        {
+            EventList evListCommon = new EventList();
+            
+            NmsSynchronizationMonitor syncRootA = new NmsSynchronizationMonitor();
+            NmsSynchronizationMonitor syncRootB = new NmsSynchronizationMonitor();
+            bool runTest = true;
+
+            // int sleepTimeMs = 1;
+            
+            var task1 = Task.Run(async () =>
+            {
+                int counter = 0;
+                while (runTest)
+                {
+                    var lockA = (counter % 2 == 0) ? syncRootA.Lock() : await syncRootA.LockAsync();
+                    using (lockA)
+                    {
+                        await Task.Delay(sleepTimeMs);
+                        await Task.Yield();
+                        evListCommon.Add("A1");
+                        await Task.Delay(sleepTimeMs);
+                        await Task.Yield();
+                        evListCommon.Add("A1");
+                        await Task.Delay(sleepTimeMs);
+                        await Task.Yield();
+                        evListCommon.Add("A1");
+                        await Task.Delay(sleepTimeMs);
+                        await Task.Yield();
+
+                        var lockB = (counter % 2 == 0) ? syncRootB.Lock() : await syncRootB.LockAsync();
+                        using (lockB)
+                        {
+                            evListCommon.Add("B1");
+                            await Task.Delay(sleepTimeMs);
+                            await Task.Yield();
+                            evListCommon.Add("B1");
+                            await Task.Delay(sleepTimeMs);
+                            await Task.Yield();
+                            evListCommon.Add("B1");
+                            await Task.Delay(sleepTimeMs);
+                            await Task.Yield();
+                        }
+                        
+                        await Task.Delay(sleepTimeMs);
+                        await Task.Yield();
+                        evListCommon.Add("A1");
+                        await Task.Delay(sleepTimeMs);
+                        await Task.Yield();
+                        evListCommon.Add("A1");
+                        await Task.Delay(sleepTimeMs);
+                        await Task.Yield();
+                        evListCommon.Add("A1");
+                        await Task.Delay(sleepTimeMs);
+                        await Task.Yield();
+                    }
+
+                    counter++;
+                }
+            });
+
+            var task2 = Task.Run(async () =>
+            {
+                int counter = 0;
+
+                // Also test the reentrancy of lock and mix of async and non async
+                async Task ResourceAccess(string symbol, int level, NmsSynchronizationMonitor syncRoot)
+                {
+                    var locked = (counter % 2 == 0) ? syncRoot.Lock() : await syncRoot.LockAsync();
+                    using (locked)
+                    {
+                        int depth = counter % 4;
+                        if (level == depth)
+                        {
+                            await Task.Delay(sleepTimeMs);
+                            await Task.Yield();
+                            evListCommon.Add(symbol);
+                            await Task.Delay(sleepTimeMs);
+                            await Task.Yield();
+                            evListCommon.Add(symbol);
+                            await Task.Delay(sleepTimeMs);
+                            await Task.Yield();
+                            evListCommon.Add(symbol);
+                            await Task.Delay(sleepTimeMs);
+                            await Task.Yield();
+                        }
+                        else
+                        {
+                            await ResourceAccess(symbol, level + 1, syncRoot);
+                        }
+                    }
+                } 
+                
+                while (runTest)
+                {
+                    await ResourceAccess("A2",0, syncRootA);
+                    await ResourceAccess("B2",0,syncRootB);
+                    
+                    counter++;
+                }
+            });
+            
+            var task3 = Task.Run(() =>
+            {
+                int counter = 0;
+                while (runTest)
+                {
+                    var lockA = (counter % 2 == 0) ? syncRootA.Lock() : syncRootA.LockAsync().GetAsyncResult();
+                    using (lockA)
+                    {
+                        Thread.Sleep(sleepTimeMs);
+                        evListCommon.Add("A3");
+                        Thread.Sleep(sleepTimeMs);
+                        evListCommon.Add("A3");
+                        Thread.Sleep(sleepTimeMs);
+                        evListCommon.Add("A3");
+                        Thread.Sleep(sleepTimeMs);
+                    }
+                    
+                    var lockB = (counter % 2 == 0) ? syncRootB.Lock() : syncRootB.LockAsync().GetAsyncResult();
+                    using (lockB)
+                    {
+                        evListCommon.Add("B3");
+                        Thread.Sleep(sleepTimeMs);
+                        evListCommon.Add("B3");
+                        Thread.Sleep(sleepTimeMs);
+                        evListCommon.Add("B3");
+                        Thread.Sleep(sleepTimeMs);
+                    }
+
+
+                    counter++;
+                }
+            });
+            
+            // Let it run for one sec
+            Thread.Sleep(testTimeMs);
+
+            runTest = false;
+            task1.Wait();
+            task2.Wait();
+            task3.Wait();
+
+            var sequenceCommon = evListCommon.ToString();
+            var sequenceA = evListCommon.ToString("A");
+            var sequenceB = evListCommon.ToString("B");
+            
+            // remove B locks interfering with A sequence of rvalidating task1
+            
+            Enumerable.Range(0,10).ToList().ForEach( (i) => sequenceCommon = sequenceCommon
+                .Replace("A1B2", "A1")
+                .Replace("A1B3", "A1")
+                .Replace("B2A1","A1")
+                .Replace("B3A1","A1")
+            );
+            sequenceCommon = sequenceCommon.Replace("A1A1A1B1B1B1A1A1A1", "");
+            // The only allowed sequence in common for task1 is nested sequence
+            Assert.IsFalse(sequenceCommon.Contains("A1"), "Sequence should only contain task 1 resource in right order:"+sequenceCommon);
+            Assert.IsFalse(sequenceCommon.Contains("B1"), "Sequence should only contain task 1 resource in right order:"+sequenceCommon);
+
+            int countA1 = sequenceA.Where(a => a == '1').Count();
+            int countA2 = sequenceA.Where(a => a == '2').Count();
+            int countA3 = sequenceA.Where(a => a == '3').Count();
+            
+            int countB1 = sequenceB.Where(a => a == '1').Count();
+            int countB2 = sequenceB.Where(a => a == '2').Count();
+            int countB3 = sequenceB.Where(a => a == '3').Count();
+
+            // Assert that all of the threads had their fair share of action
+            Assert.GreaterOrEqual(countA1, minimumOccurences);
+            Assert.GreaterOrEqual(countA2, minimumOccurences);
+            Assert.GreaterOrEqual(countA3, minimumOccurences);
+            Assert.GreaterOrEqual(countB1, minimumOccurences);
+            Assert.GreaterOrEqual(countB2, minimumOccurences);
+            Assert.GreaterOrEqual(countB3, minimumOccurences);
+            
+            
+            sequenceA = sequenceA.Replace("A1A1A1", "");
+            sequenceA = sequenceA.Replace("A2A2A2", "");
+            sequenceA = sequenceA.Replace("A3A3A3", "");
+            
+            sequenceB = sequenceB.Replace("B1B1B1", "");
+            sequenceB = sequenceB.Replace("B2B2B2", "");
+            sequenceB = sequenceB.Replace("B3B3B3", "");
+
+            
+            Assert.AreEqual(0,sequenceA.Length, "There were illegal sequences of execution for resource A: "+sequenceA);
+            Assert.AreEqual(0,sequenceB.Length, "There were illegal sequences of execution for resource B: "+sequenceB);
+            
+        }
+    }
+}
\ No newline at end of file