All pastes #442821 Raw Edit

zeroflag

public text v1 · immutable
·published 2007-04-16 14:10 UTC
rendered paste body
#region Header
//********************************************************************
//	author:         Thomas "zeroflag" Kraemer
//	author email:   zeroflag@zeroflag.de
//	
//	Copyright (C) 2006-2007  Thomas "zeroflag" Kraemer
//	
//	license:	(LGPL)
//	
//		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
//
//		http://www.gnu.org/licenses/lgpl.html#TOC1
//
//*********************************************************************
#endregion Header

using System;
using System.Collections.Generic;

namespace Sage.Threading
{
	/// <summary>
	/// A single reader single writer message queues without locking.
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <remarks>Update must be called by the writer when lots (= more than the buffer size, 2048 by default) of messages were sent before but nothing is added.</remarks>
	public class MessageQueue<T> : Sage.Threading.IMessageQueue<T>
		where T : class // <-- must be a reference type or atomic type to be threadsafe
	{
		Queue<T> m_WriteQueue = new Queue<T>();
		/// <summary>
		/// Queue for new values before they're added to the buffer (through Update).
		/// </summary>
		protected Queue<T> WriteQueue
		{
			get { return m_WriteQueue; }
		}

		#region MessageContainer
		/// <summary>
		/// A box arround the stored objects.
		/// With this one can set a value in the array without causing it to synchronize to the other threads.
		/// </summary>
		protected class MessageContainer
		{
			T m_Value;
			/// <summary>
			/// Gets or sets the value.
			/// Getting the value will result in it's deletion (null).
			/// </summary>
			public T Value
			{
				get
				{
					T value = m_Value;
					if (value != null)
					{
						this.m_Value = null;
					}
					return value;
				}
				set
				{
					m_Value = value;
				}
			}
			/// <summary>
			/// Whether the box has a value or not.
			/// </summary>
			public bool HasValue
			{
				get
				{
					if (m_Value != null)
						return true;
					else
						return false;
				}
			}
		}
		#endregion MessageContainer

		public MessageQueue(int bufferSize)
		{
			this.m_Buffer = new MessageContainer[bufferSize];
			this.Clear();
		}

		/// <summary>
		/// Creates a new message queue and initializes it's buffer.
		/// </summary>
		public MessageQueue()
		{
			this.Clear();
		}

		protected void Clear()
		{
			for (int i = 0; i < this.Buffer.Length; i++)
			{
				this.Buffer[i] = new MessageContainer();
			}
		}

		private int ReadIndex = 0;
		private int WriteIndex = 0;

		MessageContainer[] m_Buffer = new MessageContainer[2048];
		/// <summary>
		/// The queue's ring buffer.
		/// Default size is 2048 entries.
		/// </summary>
		protected MessageContainer[] Buffer
		{
			get { return m_Buffer; }
		}

		#region IMessageQueue<T> Members
		/// <summary>
		/// Whether there are values ready to be read.
		/// *** READER THREAD ***
		/// </summary>
		public bool IsEmpty
		{
			get { return !this.Buffer[this.ReadIndex].HasValue; }
		}
		/// <summary>
		/// Pops a value from the queue.
		/// *** READER THREAD ***
		/// </summary>
		/// <returns></returns>
		public T Pop()
		{
			if (this.Buffer[this.ReadIndex].HasValue)
			{
				// grab the current value...
				T value = this.Buffer[this.ReadIndex].Value;

				// continue to the next...
				this.ReadIndex++;
				if (this.ReadIndex >= this.Buffer.Length)
				{
					this.ReadIndex = 0;
				}

				return value;
			}
			else
			{
				// no value waiting...
				return null;
			}
		}
		/// <summary>
		/// Pushes a new value to the queue and updates the buffer.
		/// *** WRITER THREAD ***
		/// </summary>
		/// <param name="msg"></param>
		public void Push(T msg)
		{
			this.WriteQueue.Enqueue(msg);

			this.Update();
		}

		/// <summary>
		/// Fills the buffer from the queue.
		/// *** WRITER THREAD ***
		/// </summary>
		public void Update()
		{
			while (this.PushToBuffer()) ;
		}

		#endregion
		/// <summary>
		/// Pushes a single value from the WriteQueue to the Buffer.
		/// </summary>
		/// <returns>True if a value was pushed, false if it wasn't (like no items in queue or no free cell in buffer).</returns>
		protected bool PushToBuffer()
		{
			if (!this.Buffer[this.WriteIndex].HasValue && this.WriteQueue.Count > 0)
			{
				// get the message...
				T value = this.WriteQueue.Dequeue();

				// store the message...
				this.Buffer[this.WriteIndex].Value = value;

				// move the index...
				this.WriteIndex++;
				if (this.WriteIndex >= this.Buffer.Length)
				{
					this.WriteIndex = 0;
				}
				// yes, we pushed a new message from the write-queue to the buffer...
				return true;

			}
			else
			{
				// no, the exchange table is full...
				return false;
			}
		}
	}
}