// Copyright (c) Microsoft Corp., 2004. All rights reserved.
#region Using directives

using System;
using System.Threading;

#endregion

namespace System.Workflow.Runtime.DebugEngine
{
    //
    // IMPORTANT: Do not edit this file without consulting Break Safe Synchronization.doc!
    //
    internal abstract class BreakSafeBase<T> where T : ICloneable, new()
    {
        #region Data members

        private volatile object currentData; // object because type parameters instances cannot be volatile.
        private T nonEEDataClone;
        private volatile bool nonEEDataConsistent;
        private volatile bool nonEEIgnoreUpdate;
        private Mutex nonEELock;
        private object controllerUpdateObject;
        private int controllerManagedThreadId;

        #endregion

        #region Methods and properties

        protected BreakSafeBase(int controllerManagedThreadId)
        {
            this.currentData = new T();
            this.nonEEDataClone = default(T);
            this.nonEEDataConsistent = false;
            this.nonEEIgnoreUpdate = false;
            this.nonEELock = new Mutex(false);
            this.controllerManagedThreadId = controllerManagedThreadId;
        }

        private bool IsEECall
        {
            get
            {
                return Thread.CurrentThread.ManagedThreadId == this.controllerManagedThreadId;
            }
        }

        protected object GetControllerUpdateObject()
        {
            return this.controllerUpdateObject;
        }

        protected void SetControllerUpdateObject(object updateObject)
        {
            // Ensure that the access to the variable this.controllerUpdateObject is exactly one instruction - StFld in this case.
            this.controllerUpdateObject = updateObject;
        }

        protected T GetReaderData()
        {
            // Ensure that the access to the variable this.currentData is exactly one instruction - LdFld in this case.
            object data = this.currentData;
            return (T)data;
        }

        protected T GetWriterData()
        {
            if (IsEECall)
            {
                if (this.nonEEDataConsistent && this.nonEEIgnoreUpdate == false)
                {
                    // Modify the object referred to by this.currentData directly.
                    return (T)this.currentData;
                }
                else
                {
                    // Clone and discard any non-EE update.
                    this.nonEEIgnoreUpdate = true;
                    return (T)((T)this.currentData).Clone();
                }
            }
            else
            {
                // Reset the flag so that we can keep track of any concurrent EE updates.
                this.nonEEIgnoreUpdate = false;

                // Ensure that the access to the variable this.currentData is exactly one instruction - LdFld in this case.
                object data = this.currentData;
                return (T)((T)data).Clone();
            }
        }

        protected void SaveData(T data)
        {
            if (IsEECall)
                this.currentData = data;
            else
            {
                // The non-EE clone is now in a consistent state.
                this.nonEEDataClone = data;
                this.nonEEDataConsistent = true;

                this.controllerUpdateObject = null;

                // If an EE call has already modified the data, it would have also performed current non-EE update
                // when the debugger entered break mode. So discard the update. Asl ensure that the access to the 
                // variable this.currentData is exactly one instruction - StFld in this case.
                if (this.nonEEIgnoreUpdate == false)
                    this.currentData = data;

                // Clear the flag because we will clear the this.nonEEDataClone.
                this.nonEEDataConsistent = false;
                this.nonEEDataClone = default(T);
            }
        }

        protected void Lock()
        {
            // Serialize non-EE calls and do not invoke synchronization primitives during FuncEval.
            if (!IsEECall)
                this.nonEELock.WaitOne();
        }

        protected void Unlock()
        {
            // Serialize non-EE calls and do not invoke synchronization primitives during FuncEval.
            if (!IsEECall)
                this.nonEELock.ReleaseMutex();
        }

        #endregion
    }
}
