blob: c3e67c87926a88ed65e76e7c93cd0a11328f5378 [file] [log] [blame]
// $Id$
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace Org.Apache.Etch.Bindings.Csharp.Util
{
/// <summary>
/// A circular queue of a fixed size. Elements are added to one
/// end and removed from the other, with the queue size ranging
/// from empty to full. Operations can optionally wait until
/// finished or return status indicating success or failure.
/// For instance, adding to a full queue can wait until an item
/// is removed before adding the new item or it can wait only
/// a specified amount of time before completing successfully
/// or giving up.
/// </summary>
/// <typeparam name="T">the type of items in the queue.</typeparam>
public sealed class CircularQueue<T>
{
/// <summary>
/// Constructs the Circular Queue
/// </summary>
/// <param name="size">the maximum number of items allowed in the queue.</param>
public CircularQueue( int size )
{
if ( size < 1)
throw new ArgumentOutOfRangeException("size < 1");
this.size = size;
// items = new T[ size ];
items = new Object[size];
}
/// <summary>
/// Constructs the CircularQueue with the maximum number of items
/// defaulted to 10.
/// </summary>
public CircularQueue() : this (10)
{
}
private readonly int size;
// private readonly T[] items;
private readonly Object[] items;
/// <summary>
///
/// </summary>
/// <returns>the maximum number of items that may be put in the queue.</returns>
public int Size()
{
return size;
}
private int count;
/// <summary>
///
/// </summary>
/// <returns>the current number of items in the queue.</returns>
public int Count()
{
return count;
}
/// <summary>
///
/// </summary>
/// <returns>true if the queue is empty.</returns>
public bool IsEmpty()
{
return count == 0;
}
/// <summary>
///
/// </summary>
/// <returns>true if the queue is full.</returns>
public bool IsFull()
{
return count == size;
}
private int head;
private Object Get0()
{
Debug.Assert(count>0,"count > 0");
Object obj = items[head];
Debug.Assert(obj != null,"obj != null");
items[head] = null;
count--;
head++;
if (head == size)
head = 0;
return obj;
}
/* private T IntGet()
{
Debug.Assert( !IsEmpty() );
Debug.Assert( items[head] != null );
T t = items[head];
items[ head ] = default( T ); //null;
// wake up someone waiting for space.
if ( IsFull() )
System.Threading.Monitor.Pulse( this );
count--;
head++;
if (head == size)
head = 0;
return t;
} */
private int tail;
private void Put0(Object obj)
{
Debug.Assert(obj != null, "obj != null");
Debug.Assert(count < size, "count < size");
Debug.Assert(items[tail] == null, "items[tail] == null");
items[tail] = obj;
count++;
tail++;
if (tail == size)
tail = 0;
}
/* private void IntPut( T t )
{
Debug.Assert( t != null );
Debug.Assert( !IsFull() );
Debug.Assert( items[tail] == null );
items[tail] = t;
// wake up someone waiting for an item.
if ( IsEmpty() )
System.Threading.Monitor.Pulse(this);
count++;
tail++;
if (tail == size)
tail = 0;
} */
/// <summary>
/// Gets the next available item from the queue, waiting
/// until an item is available or the queue is closed.
/// </summary>
/// <returns>the item from the queue, or null if the queue
/// is closed.</returns>
/// Exception:
/// throws ThreadInterruptedException if thread is interrupted
public T Get()
{
return Get( 0 );
}
/// <summary>
/// Gets the next available item from the queue, waiting
/// until an item is available or the queue is closed.
/// </summary>
/// <param name="maxDelay">the maximum time in ms to wait for
/// something to be put in the queue; 0 means wait forever,
/// less than 0 means don't wait.</param>
/// <returns>the item from the queue, or null if maxDelay
/// has been exceeded or the queue is closed.</returns>
/// Exception:
/// throws ThreadInterruptedException
[MethodImpl(MethodImplOptions.Synchronized)]
public T Get( int maxDelay )
{
if (!IsEmpty())
return GetAndNotify();
if (IsClosed() || maxDelay < 0)
return default( T ); //null;
// the queue is empty, not closed, and caller has requested a delay
long now = HPTimer.Now();
long end = EndTime(now, maxDelay);
Debug.Assert(end > now, "end > now");
int d;
while ((d = RemTime(end, now)) > 0)
{
// the queue is empty, not closed, and delay has not run out...
Debug.Assert(IsEmpty(), "IsEmpty()");
Debug.Assert(!IsClosed(),"!IsClosed()");
System.Threading.Monitor.Wait(this, d);
if (!IsEmpty())
return GetAndNotify();
if ( IsClosed() )
return default( T ); //null;
now = HPTimer.Now();
}
return default( T ); // null;
}
/// <summary>
/// Puts an item in the queue, waiting until space is available
/// or the queue is closed.
/// </summary>
/// <param name="t">a non-null item to put in the queue.</param>
/// <returns>true if the item was placed in the queue,
/// or false if the queue is closed.</returns>
/// Exception:
/// throws ThreadInterruptedException if the thread is interrupted
public bool Put( T t )
{
return Put( t, 0 );
}
/// <summary>
/// Puts an item in the queue, waiting until space is available
/// or the queue is closed.
/// </summary>
/// <param name="obj">a non-null item to put in the queue.</param>
/// <param name="maxDelay">the maximum time in ms to wait for
/// available space the queue; 0 means wait forever,
/// less than 0 means don't wait.</param>
/// <returns>true if the item was placed in the queue,
/// or false if maxDelay has been exceeded or the queue is closed.</returns>
/// Exception:
/// throws ThreadInterruptedException if the thread is interrupted
[MethodImpl(MethodImplOptions.Synchronized)]
public bool Put( T obj, int maxDelay )
{
if (obj == null)
throw new ArgumentNullException( "t == null" );
if (IsClosed())
return false;
// the queue is not closed.
if (!IsFull())
{
PutAndNotify( obj );
return true;
}
// the queue is not closed, the queue is full.
if (maxDelay < 0)
return false;
long now = HPTimer.Now();
long end = EndTime(now, maxDelay);
int d;
while ((d = RemTime(end, now)) > 0)
{
// the queue is not closed, the queue is full, and delay has not run out...
Debug.Assert( !IsClosed() );
Debug.Assert( IsFull() );
System.Threading.Monitor.Wait(this, d);
if (IsClosed())
return false;
// the queue is not closed.
if (!IsFull())
{
PutAndNotify(obj);
return true;
}
now = HPTimer.Now();
}
return false;
}
/// <summary>
/// Closes the queue so that no more items may be put into it.
/// Get will return null when there are no more items to return.
/// </summary>
[MethodImpl(MethodImplOptions.Synchronized)]
public void Close()
{
if (!closed)
{
closed = true;
System.Threading.Monitor.PulseAll(this);
}
}
/// <summary>
///
/// </summary>
/// <returns>true if the queue is closed.</returns>
public bool IsClosed()
{
return closed;
}
private bool closed;
private T GetAndNotify()
{
bool notify = IsFull();
Object obj = Get0();
notify = notify || !IsEmpty();
if (notify)
System.Threading.Monitor.Pulse(this);
return (T) obj;
}
private void PutAndNotify(T obj)
{
bool notify = IsEmpty();
Put0(obj);
notify = notify || !IsFull();
if (notify)
System.Threading.Monitor.Pulse(this);
}
private long EndTime(long now, int maxDelay)
{
if (maxDelay == 0 || maxDelay == int.MaxValue)
return long.MaxValue;
return now + maxDelay * HPTimer.NS_PER_MILLISECOND;
}
private int RemTime(long end, long now)
{
if (end == long.MaxValue)
return int.MaxValue;
long ms = (end - now) / HPTimer.NS_PER_MILLISECOND;
if (ms > int.MaxValue)
return int.MaxValue;
return (int)ms;
}
}
}