#region Disclaimer / License // Copyright (C) 2011, Kenneth Skovhede // http://www.hexad.dk, opensource@hexad.dk // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // #endregion using System; using System.Collections.Generic; using System.Text; namespace Duplicati.Library.Utility { /// /// Class for wrapping a as an /// public class BlockingQueueAsEnumerable : IEnumerable { private IEnumerator m_enumerator; public BlockingQueueAsEnumerable(BlockingQueue queue) { m_enumerator = new BlockingQueueEnumerator(queue); } #region IEnumerable implementation public System.Collections.IEnumerator GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } #endregion #region IEnumerable implementation IEnumerator IEnumerable.GetEnumerator() { if (m_enumerator == null) throw new Exception("Can only return a single enumerator for a blocking queue"); IEnumerator tmp = m_enumerator; m_enumerator = null; return tmp; } #endregion private class BlockingQueueEnumerator : IEnumerator { private BlockingQueue m_queue; private bool m_first = true; private T m_current; public BlockingQueueEnumerator(BlockingQueue queue) { m_queue = queue; } #region IDisposable implementation public void Dispose() { } #endregion #region IEnumerator implementation public bool MoveNext() { m_current = m_queue.Dequeue(); return !(object.Equals(m_current, default(T)) && m_queue.Completed); } public void Reset() { if (!m_first) throw new System.NotImplementedException(); } public object Current { get { return m_current; } } #endregion #region IEnumerator implementation T IEnumerator.Current { get { return m_current; } } #endregion } } /// /// Implementation of a blocking queue that serves as a producer/consumer contact point /// /// The type of data in the queue public class BlockingQueue { /// /// The lock that protects the data structures /// private object m_lock = new object(); /// /// The queue storing the elements produced /// private Queue m_queue = new Queue(); /// /// A flag indicating if the queue is now empty forever /// private bool m_completed = false; /// /// The event that signals that items have been produced (consumers wait for this) /// private System.Threading.ManualResetEvent m_itemsProduced = new System.Threading.ManualResetEvent(false); /// /// The event that signals that items have been consumed (producers wait for this) /// private System.Threading.ManualResetEvent m_itemsConsumed = new System.Threading.ManualResetEvent(false); /// /// The maximum capacity of the queue, the producers will be blocked if more elements are put into the queue /// private long m_maxCapacity = 100; /// /// Initializes a new instance of the class with the default capacity /// public BlockingQueue() { } /// /// Gets a value indicating whether this is completed /// public bool Completed { get { return m_completed; } } /// /// Initializes a new instance of the class /// /// The maximum capacity of the queue, the producers will be blocked if more elements are put into the queue public BlockingQueue(long maxCapacity) : this() { if (maxCapacity <= 0) throw new ArgumentException("maxCapacity", "maxCapacity must be a positive number"); m_maxCapacity = maxCapacity; } /// /// Signals that the producers are done and no more elements will be put into the queue /// public void SetCompleted() { m_completed = true; m_itemsProduced.Set(); m_itemsConsumed.Set(); } /// /// Enqueues the specified item. /// /// The item to put into the queue public bool Enqueue(T item) { while (true) { lock(m_lock) { if (m_completed) return false; if (m_queue.Count < m_maxCapacity) { m_queue.Enqueue(item); m_itemsProduced.Set(); return true; } m_itemsConsumed.Reset(); } m_itemsConsumed.WaitOne(); } } /// /// Dequeues an element from the queue /// /// The dequed element or default(T) if there are no more elements public T Dequeue() { while(true) { lock(m_lock) { if (m_queue.Count > 0) { m_itemsConsumed.Set(); return m_queue.Dequeue(); } if (m_completed) return default(T); m_itemsProduced.Reset(); } m_itemsProduced.WaitOne(); } } } }