//
// Copyright (c) Microsoft Corporation.   All rights reserved.
//

/*******************************************************************/
/*                           WARNING                               */
/* This file should be identical in the Bartok and Singularity     */
/* depots. Master copy resides in Bartok Depot. Changes should be  */
/* made to Bartok Depot and propagated to Singularity Depot.       */
/*******************************************************************/

namespace System.GCs {

    using Microsoft.Bartok.Runtime;
    using System.Threading;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;

    //[NoBarriers]
    internal unsafe class WriteBarrierCMS : UniversalWriteBarrier
    {

        internal static WriteBarrierCMS instance;

        [StructLayout(LayoutKind.Sequential)]
        struct FakeObjectBytes
        {
            public PreHeader preBytes;
            public PostHeader postBytes;
        }
        private static FakeObjectBytes memoryForFakeObject;

        [NoBarriers]
        [NoHeapAllocation]
        internal static WriteBarrierCMS MakeEarlyInstance()
        {
            // We need a write barrier even if we haven't set up enough of the
            // memory system to support allocating from bootstrap memory yet.
            VTable vtable =
                ((RuntimeType) typeof(WriteBarrierCMS)).classVtable;
            UIntPtr numBytes = ObjectLayout.ObjectSize(vtable);
            if (numBytes > (UIntPtr) sizeof(FakeObjectBytes)) {
                return null;    // too big to allocate in memoryForFakeObject
            }
            UIntPtr fakeObjectAddr;
            fixed (PostHeader *middlePtr = & memoryForFakeObject.postBytes) {
                fakeObjectAddr = (UIntPtr) middlePtr;
            }
            Object result = Magic.fromAddress(fakeObjectAddr);
            *result.VTableFieldAddr = Magic.addressOf(vtable);
            return (WriteBarrierCMS) result;
        }

        [PreInitRefCounts]
        [NoBarriers]
        internal static new void Initialize() {
            if (WriteBarrierCMS.instance == null) {
                WriteBarrierCMS.instance = (WriteBarrierCMS)
                    BootstrapMemory.Allocate(typeof(WriteBarrierCMS));
            }
        }

        // NOTE: this code uses ForceInline instead of Inline to indicate that
        // inlining should occur even if the caller is huge.  In general, this
        // attribute should be used with great care.  DO NOT USE IT ELSEWHERE
        // IN THE RUNTIME UNLESS YOU ARE WILLING TO DOCUMENT YOUR USE IN
        // IrSimpleInliner.cs AND Attributes.cs!  AND NEVER USE IT IN
        // APPLICATION OR OS CODE!

        [ForceInline]
        protected override bool AllowFastPathImpl()
        {
            return CMSMarking.referenceCheckIsFast;
        }

        [ForceInline]
        [NoBarriers]
        [NoStackLinkCheckTrans]
        protected override Object AtomicSwapImpl(ref Object reference,
                                                 Object value,
                                                 int mask)
        {
            CMSMarking.ReferenceCheck(ref reference, value, mask);
            UIntPtr resultAddr =
                Interlocked.Exchange(Magic.toPointer(ref reference),
                                     Magic.addressOf(value));
            return Magic.fromAddress(resultAddr);
        }

        [ForceInline]
        [NoBarriers]
        [NoStackLinkCheckTrans]
        protected override
        Object AtomicCompareAndSwapImpl(ref Object reference,
                                        Object newValue,
                                        Object comparand,
                                        int mask)
        {
            CMSMarking.ReferenceCheck(ref reference, newValue, mask);
            UIntPtr resultAddr =
                Interlocked.CompareExchange(Magic.toPointer(ref reference),
                                            Magic.addressOf(newValue),
                                            Magic.addressOf(comparand));
            return Magic.fromAddress(resultAddr);
        }

        [Inline]
        [NoBarriers]
        [NoStackLinkCheckTrans]
        protected override void CloneImpl(Object srcObject, Object dstObject)
        {
            // There is no need to keep track of initial writes, so do nothing!
            CloneNoBarrier(srcObject, dstObject);
        }

        [ForceInline]
        [NoBarriers]
        [NoStackLinkCheckTrans]
        protected override void WriteImpl(UIntPtr *location,
                                          Object value,
                                          int mask)
        {
            CMSMarking.ReferenceCheck(location, value, mask);
            *location = Magic.addressOf(value);
        }

        [ForceInline]
        [NoBarriers]
        [NoStackLinkCheckTrans]
        protected override void WriteImplByRef(ref Object location,
                                               Object value,
                                               int mask)
        {
            CMSMarking.ReferenceCheck(ref location, value, mask);
            location = value;
        }
    }
}
