////////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity - Singularity ABI
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:   SyncHandle.cs
//
//  Note:
//

using System;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.Singularity;
using Microsoft.Singularity.Memory;
using Microsoft.Singularity.V1.Services;

namespace Microsoft.Singularity.V1.Threads
{
    [CLSCompliant(false)]
    public struct SyncHandle
    {
        public readonly UIntPtr id;

        public static readonly SyncHandle Zero = new SyncHandle();

        [Inline]
        public static implicit operator SyncHandle(MutexHandle handle)
        {
            return new SyncHandle(handle.id);
        }

        [Inline]
        public static implicit operator SyncHandle(AutoResetEventHandle handle)
        {
            return new SyncHandle(handle.id);
        }

        [Inline]
        public static implicit operator SyncHandle(ManualResetEventHandle handle)
        {
            return new SyncHandle(handle.id);
        }

        internal SyncHandle(UIntPtr id)
        {
            this.id = id;
        }

#if false
        /// REVIEW: generalizes waiting across events and endpoints
        /// should review to ensure this is safe
        public WaitHandle GetWaitHandle() {
            return HandleTable.GetHandle(id) as WaitHandle;
        }

        public static implicit operator WaitHandle(SyncHandle s) {
            return HandleTable.GetHandle(s.id) as WaitHandle;
        }
#endif

        //////////////////////////////////////////////////////////////////////
        //
        // The following methods could be moved to WaitHandle if we had
        // struct inheritance.
        //
        [ExternalEntryPoint]
        public static bool WaitOne(SyncHandle handle)
        {
            //
            // Convert the handle to a waitHandle; wait on the waitHandle.
            //
            WaitHandle waitHandle = HandleTable.GetHandle(handle.id) as WaitHandle;
            bool ret = waitHandle.WaitOne(SchedulerTime.MaxValue);

            Tracing.Log(Tracing.Debug, "SyncHandle.WaitOne(id={0:x8})", handle.id);

            return ret;
        }

        [ExternalEntryPoint(IgnoreCallerTransition=1)]
        public static bool WaitOneNoGC(SyncHandle handle)
        {
            WaitHandle waitHandle =
                HandleTable.GetHandle(handle.id) as WaitHandle;
            bool ret = waitHandle.WaitOne(SchedulerTime.MaxValue);
            Tracing.Log(Tracing.Debug, "SyncHandle.WaitOneNoGC(id={0:x8})",
                        handle.id);
            return ret;
        }

        [ExternalEntryPoint]
        public static bool WaitOne(SyncHandle handle,
                                   TimeSpan timeout)
        {
            //
            // Convert the handle to a waitHandle; wait on the waitHandle.
            //
            WaitHandle waitHandle = HandleTable.GetHandle(handle.id) as WaitHandle;
            bool ret = waitHandle.WaitOne(SchedulerTime.Now + timeout);

            Tracing.Log(Tracing.Debug, "SyncHandle.WaitOne(id={0:x8}, time=)",
                        handle.id);

            return ret;
        }

        [ExternalEntryPoint]
        public static bool WaitOne(SyncHandle handle,
                                   SchedulerTime stop)
        {
            //
            // Convert the handle to a waitHandle; wait on the waitHandle.
            //
            WaitHandle waitHandle = HandleTable.GetHandle(handle.id) as WaitHandle;
            bool ret = waitHandle.WaitOne(stop);

            Tracing.Log(Tracing.Debug, "SyncHandle.WaitOne(id={0:x8}, stop=)",
                        handle.id);

            return ret;
        }

        [ExternalEntryPoint]
        [CLSCompliant(false)]
        public static unsafe int WaitAny(SyncHandle * handles,
                                         int handleCount)
        {
            WaitHandle[] waits = Thread.CurrentThread.GetSyncHandles(handleCount);
            for (int i = 0; i < handleCount; i++) {
                waits[i] = HandleTable.GetHandle(handles[i].id) as WaitHandle;;
            }

            int ret = WaitHandle.WaitAny(waits, handleCount, SchedulerTime.MaxValue);

            Tracing.Log(Tracing.Debug,
                        "SyncHandle.WaitAny(handles={0:x8}, count={1}) = {2}",
                        (UIntPtr)handles,
                        (UIntPtr)unchecked((uint)handleCount),
                        (UIntPtr)unchecked((uint)ret));

            for (int i = 0; i < handleCount; i++) {
                waits[i] = null;
            }
            return ret;
        }

        [ExternalEntryPoint]
        [CLSCompliant(false)]
        public static unsafe int WaitAny(SyncHandle * handles,
                                         int handleCount,
                                         TimeSpan timeout)
        {
            WaitHandle[] waits = Thread.CurrentThread.GetSyncHandles(handleCount);
            for (int i = 0; i < handleCount; i++) {
                waits[i] = HandleTable.GetHandle(handles[i].id) as WaitHandle;;
            }

            int ret = WaitHandle.WaitAny(waits, handleCount,
                                         SchedulerTime.Now + timeout);

            Tracing.Log(Tracing.Debug,
                        "SyncHandle.WaitAny(handles={0:x8}, count={1}, time=) = {2}",
                        (UIntPtr)handles,
                        (UIntPtr)unchecked((uint)handleCount),
                        (UIntPtr)unchecked((uint)ret));

            for (int i = 0; i < handleCount; i++) {
                waits[i] = null;
            }
            return ret;
        }

        [ExternalEntryPoint]
        [CLSCompliant(false)]
        public static unsafe int WaitAny(SyncHandle * handles,
                                         int handleCount,
                                         SchedulerTime stop)
        {
            WaitHandle[] waits = Thread.CurrentThread.GetSyncHandles(handleCount);
            for (int i = 0; i < handleCount; i++) {
                waits[i] = HandleTable.GetHandle(handles[i].id) as WaitHandle;;
            }

            int ret = WaitHandle.WaitAny(waits, handleCount, stop);

            Tracing.Log(Tracing.Debug,
                        "SyncHandle.WaitAny(handles={0:x8}, count={1}, stop=) = {2}",
                        (UIntPtr)handles,
                        (UIntPtr)unchecked((uint)handleCount),
                        (UIntPtr)unchecked((uint)ret));

            for (int i = 0; i < handleCount; i++) {
                waits[i] = null;
            }
            return ret;
        }
    }
}
