// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace System.Reactive.Concurrency
{
    /// 
    /// Asynchronous lock.
    /// 
    public sealed class AsyncLock : IDisposable
    {
        private readonly Queue queue = new Queue();
        private bool isAcquired = false;
        private bool hasFaulted = false;
        /// 
        /// Queues the action for execution. If the caller acquires the lock and becomes the owner,
        /// the queue is processed. If the lock is already owned, the action is queued and will get
        /// processed by the owner.
        /// 
        /// Action to queue for execution.
        ///  is null.
        public void Wait(Action action)
        {
            if (action == null)
                throw new ArgumentNullException("action");
            var isOwner = false;
            lock (queue)
            {
                if (!hasFaulted)
                {
                    queue.Enqueue(action);
                    isOwner = !isAcquired;
                    isAcquired = true;
                }
            }
            if (isOwner)
            {
                while (true)
                {
                    var work = default(Action);
                    lock (queue)
                    {
                        if (queue.Count > 0)
                            work = queue.Dequeue();
                        else
                        {
                            isAcquired = false;
                            break;
                        }
                    }
                    try
                    {
                        work();
                    }
                    catch
                    {
                        lock (queue)
                        {
                            queue.Clear();
                            hasFaulted = true;
                        }
                        throw;
                    }
                }
            }
        }
        /// 
        /// Clears the work items in the queue and drops further work being queued.
        /// 
        public void Dispose()
        {
            lock (queue)
            {
                queue.Clear();
                hasFaulted = true;
            }
        }
    }
}