context: using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Text;
  using System.Threading;
  using System.Threading.Tasks;
  using NHibernate.Action;
  using NHibernate.Cache;
  using NHibernate.Type;
  using NHibernate.Util;
  
  namespace NHibernate.Engine
  {
  	/// <summary> 
  	/// Responsible for maintaining the queue of actions related to events.
  	/// <para>
  	/// The ActionQueue holds the DML operations queued as part of a session's
  	/// transactional-write-behind semantics. DML operations are queued here
  	/// until a flush forces them to be executed against the database. 
  	/// </para>
  	/// </summary>
  	[Serializable]
  	public partial class ActionQueue
  	{
  		private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(ActionQueue));
  		private const int InitQueueListSize = 5;
  
  		private ISessionImplementor session;
  
  		// Object insertions, updates, and deletions have list semantics because
  		// they must happen in the right order so as to respect referential
  		// integrity
  		private readonly List<AbstractEntityInsertAction> insertions;
  		private readonly List<EntityDeleteAction> deletions;
  		private readonly List<EntityUpdateAction> updates;
  		// Actually the semantics of the next three are really "Bag"
  		// Note that, unlike objects, collection insertions, updates,
  		// deletions are not really remembered between flushes. We
  		// just re-use the same Lists for convenience.
  		private readonly List<CollectionRecreateAction> collectionCreations;
  		private readonly List<CollectionUpdateAction> collectionUpdates;
  		private readonly List<CollectionRemoveAction> collectionRemovals;
  
  		private readonly AfterTransactionCompletionProcessQueue afterTransactionProcesses;
  		private readonly BeforeTransactionCompletionProcessQueue beforeTransactionProcesses;
  		private readonly HashSet<string> executedSpaces;
  
  		public ActionQueue(ISessionImplementor session)
  		{
  			this.session = session;
  			insertions = new List<AbstractEntityInsertAction>(InitQueueListSize);
  			deletions = new List<EntityDeleteAction>(InitQueueListSize);
  			updates = new List<EntityUpdateAction>(InitQueueListSize);
  
  			collectionCreations = new List<CollectionRecreateAction>(InitQueueListSize);
  			collectionUpdates = new List<CollectionUpdateAction>(InitQueueListSize);
  			collectionRemovals = new List<CollectionRemoveAction>(InitQueueListSize);
  
  			afterTransactionProcesses = new AfterTransactionCompletionProcessQueue();
  			beforeTransactionProcesses = new BeforeTransactionCompletionProcessQueue();
  
  			executedSpaces = new HashSet<string>();
  		}
  
  		public virtual void Clear()
  		{
  			updates.Clear();
  			insertions.Clear();
  			deletions.Clear();
  
  			collectionCreations.Clear();
  			collectionRemovals.Clear();
  			collectionUpdates.Clear();
  		}
  
  		public void AddAction(EntityInsertAction action)
  		{
  			insertions.Add(action);
  		}
  
  		public void AddAction(EntityDeleteAction action)
  		{
  			deletions.Add(action);
  		}
  
  		public void AddAction(EntityUpdateAction action)
  		{
  			updates.Add(action);
  		}
  
  		public void AddAction(CollectionRecreateAction action)
  		{
  			collectionCreations.Add(action);
  		}
  
  		public void AddAction(CollectionRemoveAction action)
  		{
  			collectionRemovals.Add(action);
  		}
  
  		public void AddAction(CollectionUpdateAction action)
  		{
  			collectionUpdates.Add(action);
  		}
  
  		public void AddAction(EntityIdentityInsertAction insert)
  		{
  			insertions.Add(insert);
  		}
  
  		public void AddAction(BulkOperationCleanupAction cleanupAction)
  		{
  			RegisterCleanupActions(cleanupAction);
  		}
  
  		//Since v5.1
  		[Obsolete("This method is no longer executed asynchronously and will be removed in a next major version.")]
  		public Task AddActionAsync(BulkOperationCleanupAction cleanupAction, CancellationToken cancellationToken=default(CancellationToken))
  		{
  			if (cancellationToken.IsCancellationRequested)
  			{
  				return Task.FromCanceled(cancellationToken);
  			}
  			try
  			{
  				AddAction(cleanupAction);
  				return Task.CompletedTask;
  			}
  			catch (Exception e)
  			{
  				return Task.FromException(e);
  			}
  		}
  
  		public void RegisterProcess(IBeforeTransactionCompletionProcess process)
  		{
  			beforeTransactionProcesses.Register(process);
  		}
  
  		public void RegisterProcess(IAfterTransactionCompletionProcess process)
  		{
  			afterTransactionProcesses.Register(process);
  		}
  
  		//Since v5.2
  		[Obsolete("This method is not used and will be removed in a future version.")]
  		public void RegisterProcess(BeforeTransactionCompletionProcessDelegate process)
  		{
  			RegisterProcess(new BeforeTransactionCompletionDelegatedProcess(process));
  		}
  
  		//Since v5.2
  		[Obsolete("This method is not used and will be removed in a future version.")]
  		public void RegisterProcess(AfterTransactionCompletionProcessDelegate process)
  		{
  			RegisterProcess(new AfterTransactionCompletionDelegatedProcess(process));
  		}
  
  		private void ExecuteActions<T>(List<T> list) where T: IExecutable
  		{
  			// Actions may raise events to which user code can react and cause changes to action list.
  			// It will then fail here due to list being modified. (Some previous code was dodging the
  			// trouble with a for loop which was not failing provided the list was not getting smaller.
  			// But then it was clearing it without having executed added actions (if any), ...)
  			foreach (var executable in list)
  			{
  				InnerExecute(executable);
  			}
  			list.Clear();
  			session.Batcher.ExecuteBatch();
  		}
  
  		private void PreInvalidateCaches()
  		{
  			if (session.Factory.Settings.IsQueryCacheEnabled && executedSpaces.Count > 0)
  			{
  				session.Factory.UpdateTimestampsCache.PreInvalidate(executedSpaces);
  			}
  		}
  
  		public void Execute(IExecutable executable)
  		{
  			try
  			{
  				InnerExecute(executable);
  			}
  			finally
  			{
  				PreInvalidateCaches();
  			}
  		}
  
  		private void InnerExecute(IExecutable executable)
  		{
  			try
  			{
  				executable.Execute();
  			}
  			finally
  			{
  				RegisterCleanupActions(executable);
  			}
  		}
  
  		private void RegisterCleanupActions(IExecutable executable)
  		{
  			if (executable is IAsyncExecutable asyncExecutable)
  			{
  				RegisterProcess(asyncExecutable.BeforeTransactionCompletionProcess);
  				RegisterProcess(asyncExecutable.AfterTransactionCompletionProcess);
  			}
  			else
  			{
  #pragma warning disable 618,619
  				RegisterProcess(executable.BeforeTransactionCompletionProcess);
  				RegisterProcess(executable.AfterTransactionCompletionProcess);
  #pragma warning restore 618,619
  			}
  
gt: 			if (executable.PropertySpaces != null)