//
// Copyright (C) 2002 Robert Hinrichs. All rights reserved.
// bobh@inav.net
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
// WARRANTIES OF MERCHANTIBILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
//
using System;
using System.Collections;
using System.Threading;
using System.Reflection;
namespace CircularBuffer
{
/// <summary>
/// Summary description for CircularBuffer.
/// <newpara>Implements a <b>Circular Buffer</b>. </newpara>
/// The Circular Buffer is a memory queue where memory locations
/// are reused when the data producer (writer) overwrites (modulo
/// the buffer size) previously used locations.
/// The Circular Buffer operates in two modes of operation: the
/// synchronous mode and the asychronous mode. The synchronous mode is
/// useful where one process, either the producer (writer) or the consumer
/// (reader), operates much faster than the other. The faster process
/// would otherwise waste time waiting for the slower process.
/// Instead the faster process can "burst" off the data into the
/// Circular Buffer and continue. The slower process will read the
/// data at its own rate. However, in the synchronous mode,
/// the producer and consumer of the Circular Buffer must access the queue
/// at the same average rate over time or an overflow or underflow of
/// data will occur.
/// In the asychronous mode, we are only interested in the last data
/// items written by the producer--earlier data will be lost. This is useful
/// in debug tracing where debug information is continuously written into
/// the Circular Buffer over a period of time until the error
/// condition is detected. The Circular Buffer is then examined for the
/// sequence of states, commands, etc. written just before the error
/// was detected to help isolate the bug.
/// The actual capacity of the Circular Buffer is one less (N-1) than the
/// total length of the Buffer(N) so that a full buffer and an empty
/// buffer can be differentianted. The default length is N=256 for a
/// default capacity of 255.
/// A client process can be notified that the Circular Buffer has reached
/// a specified count (WaterMark) using the CBEventHandler delegate
/// <newpara>
/// // Example
/// </newpara>
/// <newpara>
/// <code>
/// theCB.SetWaterMark(8); // notify every time cb is at count 8
/// </code>
/// </newpara>
/// <newpara>
/// <code>
/// theCB.WaterMarkNotify += new QueueCB.CBEventHandler(OnWaterMarkEvent);
/// </code>
/// </newpara>
/// </summary>
public class QueueCB : IEnumerable // ...able! not ...ator!
{
/////////////////////////////////////////////////////////////////////
/// <summary>
/// Delegate for the WaterMark event.
/// </summary>
public delegate void CBEventHandler(object sender, System.EventArgs e);
/// <summary>
/// WaterMark event
/// </summary>
public event CBEventHandler WaterMarkNotify;
/////////////////////////////////////////////////////////////////////
private object[] buffer;
private System.Int32 nrp, nwp, Capacity, CountValue;
private System.Int32 add;
private System.Int32 len;
private System.Int32 WaterMarkValue;
private System.Boolean WaterMarkEnableFlag;
private System.Boolean SynchronousMode;
private System.Int32 NumberToAdd = 0;
private CountEvent countevent;
/////////////////////////////////////////////////////////////////////
/// <summary>
/// Returns current number of items in the Circular Buffer.
/// In the asynchronous mode, count will staturate at N-1 if an
/// overflow conditions exists. The read index will then follow the
/// write index so that the lastest items are always available.
/// </summary>
public System.Int32 Count
{
get
{
return CountValue;
}
}
/// <summary>
/// Returns next Read Index
/// </summary>
public System.Int32 ReadIndex
{
get
{
return nrp;
}
}
/// <summary>
/// Returns next Write Index
/// </summary>
public System.Int32 WriteIndex
{
get
{
return nwp;
}
}
/// <remarks>
/// 1> Synchronous Mode -- In the synchronous mode the emptying of the
/// Circular Buffer must occur at the same average rate as the filling
/// of the Circular Buffer so that the overrun condition never occurs.
/// That is, as the tail index chases the head index around the circle
/// of the buffer, the head index always stays ahead (in a modulo N sense)
/// of the tail index. No data is lost. An exception will be thrown when
/// a buffer overrun condition occurs.
/// <newpara>
/// 2> Asynchronous Mode -- In the asynchronous mode, data will be lost
/// as the Circular Buffer is overwritten. Usually in this mode, only
/// the last item written are of interest. When the Write index
/// (head index) catches the Read Index (tail index) the Read Index is
/// automatically incremented and the Circular Buffer count will indicate
/// N-1 items in the Buffer.
/// The synchronous mode is pretty straight forward as most of the
/// responsibility for avoiding buffer overflow is a system issue for the
/// producer and consumer of the data. The circular buffer just checks
/// for error conditions.
/// The asynchronous mode is more difficult as buffer overruns are allowed.
/// So things operate as normal until the maximum occupancy is reached in
/// the asynchronous mode. From then on, while the buffer is still at
/// maximum occupancy, the read index follows the write index. This
/// simulates synchronous operation. I call this the saturation
/// mode. While saturation exists, an additional complication must be
/// handled when the data is read. The read index is stored ahead of valid
/// data (write index so an adjustment must be made before it is used to
/// output data from the circular buffer.
/// Capacity is N-1
/// </newpara>
/// </remarks>
/// <summary>
/// Used to set synchronous or asynchronous modes of operation
/// Modes differ in how they handle the buffer overflow
/// condition.
/// </summary>
/// <value>Property <c>Synchronousmode</c>set to true of false</value>
public System.Boolean Synchronousmode
{
get
{
return SynchronousMode;
}
set
{
lock(this)
{
SynchronousMode = value;
}
}
}
/// <summary>
/// Sets the level (queue count) at which the WaterMarkNotify event
/// will fire.
/// </summary>
/// <value>Property <c>Watermark</c>Pos. integer at which event fires</value>
public System.Int32 Watermark
{
get
{
return WaterMarkValue;
}
set
{
lock(this)
{
WaterMarkValue = value;
}
}
}
/// <summary>
/// Enables WaterMark checking
/// </summary>
/// <value>Property <c>Watermarkflag</c>bool enables watermark checking when true</value>
public System.Boolean Watermarkflag
{
get
{
return WaterMarkEnableFlag;
}
set
{
lock(this)
{
WaterMarkEnableFlag = value;
}
}
}
/// <summary>
/// Returns assembly version string
/// </summary>
/// <return>Version string "n.n.n.n"</return>
public String GetVersion
{
get
{
Assembly asm = Assembly.GetExecutingAssembly();
AssemblyName asmn = asm.GetName();
Version ver = asmn.Version;
return ver.ToString();
}
}
//////////////////////////////////////////////////////////////////////
// Client must set through method.
private System.Int32 capacity
{
get
{
return Capacity;
}
set
{
Capacity = value;
add = 1-capacity;
len = capacity;
this.Clear();
}
}
/// <summary>
/// Default Constructor creates N=256 element
/// Circular Buffer.
/// </summary>
public QueueCB()
{
buffer = new object[256];
capacity = 256;
add = 1-capacity;
len = capacity;
this.Clear();
Synchronousmode = true;
Watermarkflag = true;
CountEvent countevent = new CountEvent();
}
/// <summary>
/// Constructor creates N=256 element
/// Circular Buffer with type specified
/// </summary>
/// <c>QueueCB theCB = new QueueCB(typeof(string));</c>
/// <param name="o">Generic Object</param>
public QueueCB(object o)
{
buffer = new object[256];
capacity = 256;
add = 1-capacity;
len = capacity;
this.Clear();
Synchronousmode = true;
Watermarkflag = true;
countevent = new CountEvent();
}
/// <summary>
/// Constructor creates N=len element
/// Circular Buffer with type specified
/// </summary>
/// <param name="o">Generic Object</param>
/// <param name="len">Circular Buffer Length (# of elements)</param>
public QueueCB(object o, System.Int32 len)
{
buffer = new object[len];
capacity = len;
add = 1-capacity;
len = capacity;
this.Clear();
Synchronousmode = true;
Watermarkflag = true;
countevent = new CountEvent();
}
/// <summary>
/// Constructor creates N=len element
/// Circular Buffer with type unspecified
/// </summary>
/// <param name="len">Circular Buffer Length (# of elements)</param>
public QueueCB(System.Int32 len)
{
buffer = new object[len];
capacity = len;
add = 1-capacity;
len = capacity;
this.Clear();
Synchronousmode = true;
Watermarkflag = true;
countevent = new CountEvent();
}
/// <summary>
/// Clears the Circular Buffer and initializes with null
/// </summary>
public void Clear()
{
nrp = 0;
nwp = 0;
CountValue = 0;
Array.Clear(buffer,0,capacity);
}
/// <summary>
/// Enable WaterMark (queue count) checking
/// </summary>
public void WaterMarkEnable()
{
WaterMarkEnableFlag = true;
}
/// <summary>
/// Disable WaterMark (queue count) checking
/// </summary>
public void WaterMarkDisable()
{
WaterMarkEnableFlag = false;
}
/// <summary>
/// Sets Asynchronous Mode of Circular Buffer operation--
/// see class summary
/// </summary>
public void SetAsynchronousMode()
{
lock(this)
{
Synchronousmode = false;
}
}
/// <summary>
/// Sets Synchronous Mode of Circular Buffer operation--
/// see class summary
/// </summary>
public void SetSynchronousMode()
{
lock(this)
{
Synchronousmode = true;
}
}
/// <summary>
/// Sets WaterMark (queue count) value for
/// WaterMark event operations
/// </summary>
/// <param name="mark">Positive Integer WaterMark event value</param>
public void SetWaterMark(System.Int32 mark)
{
if( mark <= (capacity-1) | (mark < 0))
lock(this)
{
Watermark = mark;
}
else
throw new IndexOutOfRangeException("watermark out of range");
}
/// <summary>
/// Returns item at read index without modifing the queue
/// <exception> throws exception if queue empty</exception>
/// </summary>
/// <returns>Returns the object at the read index
/// <c>without</c> removing it (modifing the index)</returns>
public object Peek()
{
lock(this)
{
object temp;
if(Count >= 1)
{
temp = buffer[nrp];
return(temp);
}
else
throw new IndexOutOfRangeException
("Too few items in circular buffer");
} // unlock
}
/// <summary>
/// Add single item to the Circular Buffer
/// </summary>
/// <param name="o">Generic Object </param>
public void Enqueue(object o)
{
if((Count+1 > capacity-1) && (Synchronousmode==true))
throw new IndexOutOfRangeException
("Circular Buffer capacity exceeded--Synchronous Mode");
else // Async mode
{
NumberToAdd = 1;
loadcb(o);
}
}
/// <summary>
/// Add entire Array[] to the Circular Buffer
/// </summary>
/// <param name="a">Array[]</param>
public void Enqueue(Array a)
{
if((Count+a.Length > capacity-1) && (Synchronousmode==true))
throw new IndexOutOfRangeException
("Circular Buffer capacity exceeded--Synchronous Mode");
else // Async mode
{
NumberToAdd = a.Length;
loadcbArray(a);
}
}
/// <summary>
/// Add a single item to the Circular Buffer but block if no room.
/// Used with synchronous mode only.
/// </summary>
/// <param name="o">Generic Object</param>
public void EnqueueBlocking(object o)
{
if(Synchronousmode==true)
{
if(Count <= (capacity-1)) // if room is available
loadcb(o); // write the item
else
{ // wait for room to be available
countevent.WaitLoad(1);
loadcb(o); // write the item
}
countevent.SetCopy(1);// in case waiting for items
}
else
throw new ArgumentException
("must be in synchronous mode");
}
/// <summary>
/// Add entire Array[] to the Circular Buffer but block if no room.
/// Used with synchronous mode only.
/// </summary>
/// <param name="a">Array[]</param>
public void EnqueueBlocking(Array a)
{
if(Synchronousmode==true)
{
if( (Count+a.Length) <= (capacity-1)) // if room is available
{
loadcbArray(a); // write the item
}
else
{ // wait for room to be available
countevent.WaitLoad(a.Length);
loadcbArray(a); // write the item
}
countevent.SetCopy(a.Length);// in case waiting for items
}
else
throw new ArgumentException
("must be in synchronous mode");
}
/// <summary>
/// Return/Remove single item from the Circular Buffer
/// Will throw exception if no items are in the Circular Buffer
/// regardless of mode.
/// This and CopyTo(Array, index, number) are the only queue
/// item removal methods that will check for underflow and throw an
/// exception. Others will either block or return the number
/// of items they were able to successfully remove.
/// </summary>
/// <returns>returns/removes a single object from the
/// Circular Buffer</returns>
public object Dequeue()
{
if(Count>0)
return copycb(buffer);
else
{
throw new IndexOutOfRangeException
("No items in circular buffer");
}
}
/// <summary>
/// Return/Remove single item from the Circular Buffer but block
/// if empty. Must be in synchronous mode.
/// </summary>
/// <returns>returns/removes a single object from the
/// Circular Buffer</returns>
public object DequeueBlocking()
{
object tempo;
if(Synchronousmode==true)
{
if(Count<=0)
countevent.WaitCopy(1);
tempo = copycb(buffer);
countevent.SetLoad(1); // this is for a blocked enqueue
return tempo;
}
else
{
throw new ArgumentException
("must be in synchronous mode");
}
}
/// <summary>
/// Copy/Remove items from entire Circular Buffer to an Array[]
/// with Array offset of index.
/// </summary>
/// <param name="a">Target Array[]</param>
/// <param name="index">Target Array[] offset</param>
/// <returns>Number of items copied/removed from Circular Buffer
/// </returns>
public System.Int32 CopyTo(Array a, System.Int32 index)
{
System.Int32 NumberOfItems;
lock(this)
{
NumberOfItems = Count;
if(a.Length < NumberOfItems)
{
throw new IndexOutOfRangeException
("too many items for destination array");
}
else
{
copycbArray(a, index, a.Length);
return NumberOfItems;
}
}
}
/// <summary>
/// Copy/Remove num items from Circular Buffer to an Array[]
/// with Array offset of index.
/// This and Dequeue() are the only queue
/// item removal methods that will check for underflow and throw an
/// exception. Others will either block or return the number
/// of items they were able to successfully remove.
/// </summary>
/// <param name="a">Target Array[]</param>
/// <param name="index">Target Array[] offset</param>
/// <param name="num">Number of items to copy/remove</param>
/// <returns>Number of items copied/removed from Circular Buffer
/// </returns>
public System.Int32 CopyTo(Array a, System.Int32 index,
System.Int32 num)
{
System.Int32 NumberOfItems;
lock(this)
{
NumberOfItems = Count;
if(NumberOfItems >= num)
{
copycbArray(a, index, num);
}
else
{
throw new IndexOutOfRangeException
("Not enough items in circular buffer");
}
return NumberOfItems;
}
}
/// <summary>
/// Copy/Remove items from entire Circular Buffer to an Array[]
/// </summary>
/// <param name="a">Target Array[]</param>
/// <returns>Number if items copied/removed from Circular Buffer
/// </returns>
public System.Int32 CopyTo(Array a)
{
System.Int32 NumberOfItems;
lock(this)
{
NumberOfItems = Count;
if(a.Length < NumberOfItems)
{
throw new IndexOutOfRangeException
("too many items for destination array");
}
else
{
copycbArray(a, 0, NumberOfItems);
return NumberOfItems;
}
}
}
/// <summary>
/// Copy/Remove items from entire Circular Buffer to an Array[]
/// with Array offset of index but block if empty. Must be in
/// synchronous mode.
/// </summary>
/// <param name="a">Target Array[]</param>
/// <param name="index">Target Array[] offset</param>
/// <returns>Number if items copied/removed from Circular Buffer
/// </returns>
public System.Int32 CopyToBlocking(Array a, System.Int32 index)
{
System.Int32 NumberOfItems;
if(Synchronousmode==true)
{
// returns all available items from the circular buffer
// to caller's array
lock(this)
{
NumberOfItems = Count;
}
if(a.Length < NumberOfItems)
{
throw new IndexOutOfRangeException
("too many items for destination array");
}
else
{
if(NumberOfItems > 0)
{ // we have at least this many
copycbArray(a, index, NumberOfItems);
// this is the only reason method is called blocking
// this if for a blocked enqueue
countevent.SetLoad(NumberOfItems);
}
}
return NumberOfItems;
}
else
{
throw new ArgumentException
("must be in synchronous mode");
}
}
/// <summary>
/// Copy/Remove items from entire Circular Buffer to an Array[]
/// with Array offset of index but block if empty. Must be in
/// synchronous mode.
/// </summary>
/// <param name="a">Target Array[]</param>
/// <param name="index">Target Array[] offset</param>
/// <param name="numbertocopy">number to copy</param>
/// <returns>Number if items copied/removed from Circular Buffer
/// </returns>
public System.Int32 CopyToBlocking(Array a,
System.Int32 index, System.Int32 numbertocopy)
{
System.Int32 NumberOfItems;
if(Synchronousmode==true)
{
if(Count < numbertocopy)
{ // this is the only CopyToBlocking that will block
// since a specific number was asked for.
countevent.WaitCopy(numbertocopy);
NumberOfItems = numbertocopy;
copycbArray(a, index, numbertocopy);
}
else
{
NumberOfItems = numbertocopy;
copycbArray(a, index, numbertocopy);
}
// this if for a blocked enqueue
countevent.SetLoad(numbertocopy);
return NumberOfItems;
}
else
{
throw new ArgumentException
("must be in synchronous mode");
}
}
/// <summary>
/// Copy/Remove items from entire Circular Buffer to an Array[]
/// with Array offset of zero but block if empty. Must be in
/// synchronous mode.
/// </summary>
/// <param name="a">Target Array[]</param>
/// <returns>Number if items copied/removed from Circular Buffer
/// </returns>
public System.Int32 CopyToBlocking(Array a)
{
System.Int32 NumberOfItems;
if(Synchronousmode==true)
{
// returns all available items from the circular buffer
// to caller's array
NumberOfItems = Count;
if(a.Length < NumberOfItems)
{
throw new IndexOutOfRangeException
("too many items for destination array");
}
else
{
if(NumberOfItems > 0)
{
copycbArray(a, 0, a.Length);
// this is the only reason method is
// called "blocking"
// this if for a blocked enqueue
countevent.SetLoad(NumberOfItems);
}
}
return NumberOfItems;
}
else
{
throw new ArgumentException
("must be in synchronous mode");
}
}
/// <summary>
/// Displays Circular Buffer contents and or state information.
/// Overridable.
/// </summary>
public virtual void DumpCircularBuffer()
{
}
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
// raise the event by invoking the delegates
/// <summary>
/// Raise the watermark event by invoking the delegates
/// </summary>
protected virtual void OnWaterMarkNotify()
{
if(WaterMarkNotify != null) // if someone has signed up
// fire the generic event
WaterMarkNotify(this, System.EventArgs.Empty);
}
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
#region Private Support Functions
//////////////////////////////////////////////////////////////////////
////////////////////////////PRIVATE///////////////////////////////////
////////////////////////Support Functions/////////////////////////////
//////////////////////////////////////////////////////////////////////
private void loadcb(object o)
{
lock(this)
{
System.Int32 i = nwp;
System.Int32 tempCount;
buffer[i] = o;
// for speed, we only update the count after the operation
// but if watermark check is on, we must do it here also
// but don't bother if nobody has registered for the event
if((Watermarkflag == true) && (WaterMarkNotify != null))
{
tempCount = i-nrp;
if(tempCount < 0)
tempCount += Capacity; // modulo buffer size
if(tempCount==Watermark)
OnWaterMarkNotify();
}
i += add;
if(i<0) i+= len;
nwp = i;
updateCount();
} // unlock
}
private void loadcbArray(Array a)
{
lock(this)
{
System.Int32 i = nwp;
System.Int32 n = a.Length;
System.Int32 offset = 0;
System.Int32 tempCount;
while(n-- > 0)
{
buffer[i] = a.GetValue(offset++);
// for speed, we only update the count after the operation
// but if watermark check is on, we must do it here also
// but don't bother if nobody has registered for the event
if((Watermarkflag == true) && (WaterMarkNotify != null))
{
tempCount = i-nrp;
if(tempCount < 0)
tempCount += Capacity; // modulo buffer size
if(tempCount==Watermark)
OnWaterMarkNotify();
}
i += add;
if(i<0) i+= len;
}
nwp = i;
updateCount();
} // unlock
}
private object copycb(object o)
{
lock(this)
{
System.Int32 i = nrp;
object temp;
System.Int32 tempCount;
// A modification to the read index may be required
// if we have been operating in asynchronous saturated mode
if(Synchronousmode==false)
if(Count >= (capacity-1))
i = (nrp-1)%capacity;
temp = buffer[i];
// for speed, we only update the count after the operation
// but if watermark check is on, we must do it here also
// but don't bother if nobody has registered for the event
if((Watermarkflag == true) && (WaterMarkNotify != null))
{
tempCount = nrp-i; // change
if(tempCount < 0)
tempCount += Capacity; // modulo buffer size
if(tempCount==Watermark)
OnWaterMarkNotify();
}
i += add;
if(i<0) i+= len;
nrp = i;
updateCount();
return(temp);
} // unlock
}
private void copycbArray(Array a, System.Int32 index, System.Int32 alen)
{
lock(this)
{
System.Int32 i = nrp;
System.Int32 n = alen - index;
System.Int32 offset = index;
System.Int32 tempCount;
// A modification to the read index may be required
// if we have been operating in asynchronous saturated mode
//if(Synchronousmode==false)
// if(Count >= (capacity-1))
// i = (nrp-1)%capacity;
if(Count >= n)
{
while(n-- > 0)
{
a.SetValue(buffer[i], offset++);
// for speed, we only update the count after the
// operation but if watermark check is on, we must
// do it here also but don't bother if nobody has
// registered for the event
if((Watermarkflag == true) && (WaterMarkNotify
!= null))
{
tempCount = nrp-i; // change
if(tempCount < 0)
tempCount += Capacity; // modulo buffer size
if(tempCount==Watermark)
OnWaterMarkNotify();
}
i += add;
if(i<0) i+= len;
}
}
else
throw new IndexOutOfRangeException
("Too few items in circular buffer");
nrp = i;
updateCount();
} // unlock
}
private void updateCount()
{
if(Synchronousmode==true)
{
CountValue = nwp-nrp;
if(Count < 0)
CountValue += Capacity; // modulo buffer size
}
// if asynchronousmode, adjust the read index to follow the
// write index if buffer is full (saturation)
else
{
// check to see if we are in saturation
if(CountValue+NumberToAdd >= (capacity-1))
{ // if saturated, read index follows write index
nrp = (nwp+NumberToAdd)%capacity;
// and Count remains at max capacity
CountValue = capacity-1;
}
else
{
CountValue = nwp-nrp;
if(Count < 0)
CountValue += Capacity; // modulo buffer size
}
}
}
#endregion
#region IEnumerator Support
/// <summary>
/// IEnumerator support
/// </summary>
/// <returns></returns>
public CBEnumerator GetEnumerator()
{
return new QueueCB.CBEnumerator(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
// a helper class
/// <summary>
/// A helper class
/// </summary>
public class CBEnumerator : IEnumerator // ...ator! ...ator! ...ator!
{
QueueCB cb;
internal CBEnumerator(QueueCB cb)
{
this.cb = cb;
}
// IEnumerator support
/// <summary>
/// IEnumerator support
/// </summary>
/// <returns></returns>
public bool MoveNext()
{
lock(this)
{
if(cb.Count == 0)
return false;
else
return true;
}
}
/// <summary>
/// IEnumerator support
/// </summary>
public void Reset()
{
}
// Client must set through method.
/// <summary>
/// IEnumerator support
/// </summary>
public object Current
{
get
{
lock(this)
{
System.Int32 i = cb.nrp;
object temp;
// A modification to the read index may be required
// if we have been operating in asynchronous
// saturated mode
if(cb.Synchronousmode==false)
if(cb.Count >= (cb.capacity-1))
i = (cb.nrp-1)%cb.capacity;
temp = cb.buffer[i];
i += cb.add;
if(i<0) i+= cb.len;
cb.nrp = i;
cb.updateCount();
return(temp);
} // unlock
}
}
} // end class CBEnumerator
#endregion
} // end class QueueCB
#region Blocking Support
internal class CountEvent
{
private AutoResetEvent loadautoevent;
private AutoResetEvent copyautoevent;
private System.Int32 LoadEventCount = 0;
private System.Int32 CopyEventCount = 0;
// constructor
internal CountEvent()
{
loadautoevent = new AutoResetEvent(false);
copyautoevent = new AutoResetEvent(false);
CopyEventCount = 0;
LoadEventCount = 0;
}
///////////////////////////////////////////////////////////////////
// WaitLoad() and SetLoad() are part of a blocked enqueue operation.
// WaitLoad requests to continue when the number specified can be written.
// SetLoad updates the count as items are removed from the circular buffer.
internal void WaitLoad(System.Int32 cnt)
{
lock(this)
{
LoadEventCount = cnt; // wait for count to reach this value
}
loadautoevent.WaitOne();
}
internal void SetLoad(System.Int32 cnt)
{
lock(this)
{
if(LoadEventCount > 0)
{
LoadEventCount -= cnt; // queue has been emptied by this amount
if(LoadEventCount <= 0)
{
loadautoevent.Set();
}
}
}
}
///////////////////////////////////////////////////////////////////
// WaitCopy() and SetCopy() are part of a blocked dequeue operation.
// WaitCopy requests to continue when the number specified can be read.
// SetCopy updates the count as items are written into the circular buffer.
internal void WaitCopy(System.Int32 cnt)
{
lock(this)
{
CopyEventCount = cnt; // countdown this value
}
copyautoevent.WaitOne();
}
internal void SetCopy(System.Int32 cnt)
{
lock(this)
{
if(CopyEventCount > 0)
{
CopyEventCount -= cnt; // queue has been emptied by this amount
if(CopyEventCount <= 0)
{
copyautoevent.Set();
}
}
}
}
} // end class CountEvent
#endregion
}