Introduction
In the last part of this article,
we addressed some of the fundamental principles in design paradigms as well as faced some new and hopefully useful concepts.
We also had a lot of banter which was somewhat of a tangent and not probably as easygoing as originally intended but hopefully still interesting enough to keep you thinking and reading.
We are going to keep the banter to a minimum this time and we are going to look at some real life problems and then try to determine the best
way to solve them with the principles set forth in the previous article; however just like last time please remember that the ideas and code presented
in this articles are not complete and only are enough to reflect the concepts I am attempting to portray.
Now without further delay, let's take an easy real work example which is going to be for the sake of this argument “Socket” communication.
Typically when you used to have to perform socket operations you needed to do things in particular way (typically synchronously) and any deviation from this pattern was highly scrutinized.
Then came the BeginXXX
methods which helped to alleviate some of this by allowing the “ThreadPool” to help out by performing all the waiting necessary for the signals to occur.
These methods were helpful but drained the “ThreadPool” when used which caused other applications to slow down and possibly the entire computer.
Then the XXXXAsync
methods came around which helped out even more because they used
IOCompletionPort
s, they are not much different than the
BeginXXX
other than that they have a single event ‘Completed
’ which is designed to allow a single core entry point to encapsulate all operations on a particular
socket; connect, send, receive and disconnect.
The synchronous polling is performed away from the thread which which is waiting on the “Completed” event to fire.
Now that we are familiar with the various Socket methods including SocketAsyncEventArgs
object and how it works we will try to solve a real world problem with them.
How do you keep track of data statistics on a socket? Should this even be done at the application layer? What if it needs to be for reasons of latency etc?
Enter SocketStatistics...
They are class which is designed to work with the XXXXAsync
method classes but contains overloads for any other version.
Based on my previous Article you will see I have included another class MediaCounter
which provides a counter with virtually unlimited precision.
The reason for this is a counter is agnostic of operation (send or receive) and this a
SocketStatistic
will need to have Send a Receive counters.
Rather than just making all the fields twice I have broken down the logic into smaller units...
You will also see MediaSocketArgs
which is the base class for
all SocketAsyncEventArgs
based communication in the library.
It is primarily used in the “MediaSocketPool
” which has also been included.
There is nothing too special here except a proper “Reset” method and the ability to store the buffer and initial offset and count so when “Reset” is called it can be retrieved.
The interesting thing about the MediaSocketPool
is the way it
encapsulated the SocketAsyncEventArgs
pattern by providing a single public
method for all operations performed on the pool.
You also never attach to the completed event, the pool handles all of this for you. You only have to notify the pool if the
XXXXAsync
method indicates it will not raise the event by
calling a single method OnPoolWorkerCompleted
.
Another interesting feature is the ability to “hook” into the MediaSocketPool
for all operations, when any MediaSocketArgs
is finished it
will automatically be passed to all “hooks” in the hooks table.
This allows you to truly fire and forget, something I have not seen even in the most advanced pooling implementations.
When operations are performed in the default configuration the workers are set to certain amount for which can access the buffer concurrently without any contention whatsoever.
If a send operation is performed and there is a worker available he will be used, otherwise a new worker will be created if this is
allows by the options given to the “MediaSocketPool”
After an operation completes a worked is checked for a PoolId
which represents the MediaSocketPool
which it was created for, if the options of the MediaSocketPool
given
allow new workers to be integrated into the pool then the PoolId of a dynamically created worker is set to PoolId
of the MediaSocketPool
if not a new Guid
is used to ensure the worker is never returned to the pool.
Upon this happening the Buffer is not provided to the worker and a user must provide their own buffer to send or receive into by using the “SetBuffer” method on the “MediaSocketPool”.
The reason for this is new workers are disposed and not returned to the pool. One immediate improvement I can think of is a polling
mechanism which leaves the workers available for a certain amount of time before disposing them in case send rates are high but adding that functionality
would be trivial at this point.
Now back to the SocketStatistics...
I have already explained everything about those but what about
an additional aspect... say you want to keep track of different types of
packets on the same socket... what if each packet carries a different amount of
overhead? What if they are sent in combined fashions?
All of this usually can be a nightmare especially when keeping track of those
pesky statistics.
This is where EncapsulatedSocketStatistics
comes into play.
It adds a Dictionary
of <Type, SocketStatistics>
which
allows you to keep try of type of any different packet.
I personally have developed the IPacket
interface to allow for
multiple packets to fit under a single facade while then allow them to be sub
categorized by the actual type of packet they implement.
I have further integrated IPacket
by using it in the MediaSocketPool
when packets are sent or received.
It works by looking at the BytesTransferred
property and using that to increment the SocketStatistic
counters underneath the hood but also taking into account what type of packet it was based on the Transferred
property of the MediaSocketArgs
which has been engineered as a IEnumerable<IPacket>.
This is important because it gives you a lot of information, the packet objects which were sent as well as their binary representation in bytes which is automatically partitioned into the OutgoingData
Stack.
Overall the classes are engineered to work together and use the pieces put in place by the smaller units of code.
The IPacket
interface is light and actually makes your code
faster because interfaces don’t suffer from the overhead associated with
virtual calls which will probably be present in your packet class
implementations.
Well now we have certainly covered a lot of topics very quickly!
I had much less banter than usual and hopefully have again
presented some great design patterns for you to enjoy!
All of these + more will be present in the new version of
Managed Media Aggregation which is coming along very nicely!
All of these articles represent the breakdown of existing
concepts employed in the code currently to make it more efficient and scalable
while also allowing more things to be done with it.
Each piece of each class has been professionally engineered to
allow for the most flexibility and the best performance where possible but the
cornerstone of the libraries presented are their unique patterns and
encapsulations which cannot be found elsewhere until now.
Hopefully you will find them useful and they will keep you
coming back for the next articles!
Thanks again for reading and at this time I will bring this
article to a close!
Join me next time for other interesting goodies and related
code!
Here are the MediaSocketArgs
which encapsulate the SocketAsyncEventArgs
pattern
#region Socket Classes
#region MediaSocketArgs
public class MediaSocketArgs : System.Net.Sockets.SocketAsyncEventArgs, IDisposable
{
internal readonly protected Guid Id;
internal readonly protected Guid PoolId;
public MediaTransportContext TransportContext { get; internal protected set; }
public IEnumerable<IPacket> Transferred { get; set; }
public readonly int OrigionalOffset;
public readonly int OrigionalCount;
public readonly byte[] OrigionalBuffer;
public MediaSocketArgs(byte[] buffer, int offset, int count, Guid? poolId = null)
:base()
{
SetBuffer(buffer, (int)offset, (int)count);
OrigionalBuffer = buffer;
OrigionalOffset = offset;
OrigionalCount = count;
PoolId = poolId ?? Guid.NewGuid();
}
internal MediaSocketArgs(MediaSocketArgs other) : this(null, 0, 0) { SetBuffer(other.Buffer, other.Offset, other.Count); }
public MediaSocketArgs() : this(null, 0, 0) { }
~MediaSocketArgs() { SetBuffer(null, 0, 0); Dispose(); }
public void Reset()
{
if (Buffer != OrigionalBuffer || Offset != OrigionalOffset || Count != OrigionalCount)
{
SetBuffer(OrigionalBuffer, OrigionalOffset, OrigionalCount);
}
}
public virtual void Dispose(bool disposing = true)
{
if (disposing)
{
SetBuffer(null, 0, 0);
base.Dispose();
}
}
}
#endregion
Here is the Socket Statistics class which helps you keep track of send and receive rates as well as packets rates when used correctly with the MediaSocketPool
#region SocketStatistics
public class SocketStatistics : IEnumerable<MediaCounter>
{
#region Fields
public readonly DateTime Created = DateTime.UtcNow;
public readonly byte Overhead;
public Guid Id;
public readonly MediaCounter SendCounter, ReceiveCounter;
#endregion
#region Properties
public ulong BytesSent { get { return SendCounter.Value; } }
public ulong BytesReceived { get { return ReceiveCounter.Value; } }
public ulong PacketsSent { get { return SendCounter.Increments; } }
public ulong PacketsReceived { get { return ReceiveCounter.Increments; } }
public DateTime InitalSendOperation { get { return SendCounter.InitialOperation; } }
public DateTime InitialReceiveOperation { get { return ReceiveCounter.InitialOperation; } }
public DateTime LastSendOperation { get { return SendCounter.LastOperation; } }
public DateTime LastReceiveOperation { get { return ReceiveCounter.LastOperation; } }
public BigInteger TotalSentBytes { get { return SendCounter.BigValue; } }
public BigInteger TotalReceivedBytes { get { return ReceiveCounter.BigValue; } }
Here is the EncapsulatedSocketStatistics
class which helps you keep counters per specific System.Type
of IPacket
#region EncapsulatedSocketStatistics
public class EncapsulatedSocketStatistics :
IEnumerable<Type>, IEnumerable<SocketStatistics>, IEnumerable<KeyValuePair<Type,SocketStatistics>>
{
internal protected System.Collections.Concurrent.ConcurrentDictionary<Type, SocketStatistics> TypedStatistics = new System.Collections.Concurrent.ConcurrentDictionary<Type, SocketStatistics>();
public SocketStatistics this[Type index]
{
get { return TypedStatistics[index]; }
internal protected set { Add(index, value); }
}
internal protected virtual SocketStatistics UpdateExistingSocketStatistics(Type type, SocketStatistics value) { Utility.BreakIfAttached(); return TypedStatistics[type]; }
public void Add(Type index, SocketStatistics value) { TypedStatistics.AddOrUpdate(index, value, UpdateExistingSocketStatistics); }
public IEnumerator<Type> GetEnumerator() { return TypedStatistics.Keys.GetEnumerator(); }
IEnumerator<SocketStatistics> IEnumerable<SocketStatistics>.GetEnumerator() { return TypedStatistics.Values.GetEnumerator(); }
IEnumerator<KeyValuePair<Type, SocketStatistics>> IEnumerable<KeyValuePair<Type, SocketStatistics>>.GetEnumerator() { return TypedStatistics.GetEnumerator(); }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return TypedStatistics.GetEnumerator(); }
}
#endregion
Here is the MediaSocketPool
itself!
#region MediaSocketPool
public class MediaSocketPool :
IEnumerable<Media.Common.MediaSocketPool.EventProxy>,
IDisposable
{
#region Statics
public const int DefaultBlockSize = 1024;
#endregion
#region Nested Types
public class MediaSocketPoolExcpetion : MediaException { }
internal class EventProxy
{
internal readonly object Sender;
internal readonly EventHandler<MediaSocketArgs> EventHandler;
public EventProxy(object sender, EventHandler<MediaSocketArgs> eventHandler)
{
Sender = sender;
EventHandler = eventHandler;
}
}
internal class DataChunk
{
internal readonly IEnumerable<IPacket> Source;
internal ArraySegment<byte> Data;
public DataChunk(IEnumerable<IPacket> source, ArraySegment<byte> data)
{
Source = source;
Data = data;
}
}
#endregion
#region Fields
bool m_Disposing;
public readonly Guid PoolId = Guid.NewGuid();
public readonly int BlockSize;
internal int m_BufferSize;
internal byte[] SocketBuffer;
internal System.Collections.Concurrent.ConcurrentStack<MediaSocketArgs> SocketWorkers;
internal System.Collections.Concurrent.ConcurrentStack<DataChunk> OutgoingData;
internal System.Collections.Concurrent.ConcurrentDictionary<Guid, MediaSocketArgs> AddressedWorkers = new System.Collections.Concurrent.ConcurrentDictionary<Guid, MediaSocketArgs>();
internal int SendersAvailable = -1, ReceiversAvailable = -1;
#endregion
#region Properties
public int OutgoingDataSize { get { return OutgoingData.Sum(d => d.Data.Count); } }
public int BufferSize { get { return m_BufferSize; } }
public int MaxSenders { get; internal protected set; }
public int MaxReceivers { get; internal protected set; }
public bool WorkersExhusted { get { return SocketWorkers == null || SocketWorkers.Count == 0; } }
public bool SendingWorkersAvailable { get { return MaxSenders != -1 && SocketWorkers == null && SocketWorkers.Count > MaxSenders; } }
public bool ReceivingWorkersAvailable { get { return MaxReceivers != -1 && SocketWorkers == null && SocketWorkers.Count > MaxReceivers; } }
internal System.Collections.Concurrent.ConcurrentBag<EventProxy> Hooks = new System.Collections.Concurrent.ConcurrentBag<EventProxy>();
#endregion
#region Events
public delegate void PoolWorkerCompletedHandler(MediaSocketPool sender, MediaSocketArgs e);
public event PoolWorkerCompletedHandler PoolWorkerCompleted;
public void OnPoolWorkerCompleted(MediaSocketArgs e) { if (!m_Disposing) PoolWorkerCompleted(this, e); }
static void CoreSocketAsyncEventArgsCompleted(object sender, SocketAsyncEventArgs e)
{
if (sender is MediaSocketPool && e is MediaSocketArgs)
{
MediaSocketPool pool = (MediaSocketPool)sender;
MediaSocketArgs arg = (MediaSocketArgs)e;
if (!pool.m_Disposing)
{
pool.OnPoolWorkerCompleted(arg);
pool.EnqueWorker(arg);
}
}
}
static void MediaSocketPoolWorkerCompleted(MediaSocketPool sender, MediaSocketArgs e)
{
if (!sender.m_Disposing)
foreach (EventProxy proxy in sender.Hooks)
proxy.EventHandler(proxy.Sender ?? sender, e);
}
#endregion
#region Constructors / Destructor
public MediaSocketPool(int blockSize = DefaultBlockSize, int sendingWorkers = -1, int receivingWokers = -1)
{
BlockSize = blockSize;
MaxSenders = sendingWorkers;
MaxReceivers = receivingWokers;
PoolWorkerCompleted += MediaSocketPoolWorkerCompleted;
Initialize();
}
~MediaSocketPool() { Dispose(true); }
#endregion
#region Methods
public void AddHook(object sender, EventHandler<MediaSocketArgs> handler)
{
if (!m_Disposing) Hooks.Add(new EventProxy(sender, handler));
}
public void RemoveHooks(EventHandler<MediaSocketArgs> handler, object sender = null)
{
EventProxy hook;
while (Hooks.TryTake(out hook))
if (hook.EventHandler == handler && (sender != null ? hook.Sender == sender : true))
{
hook = null;
continue;
}
else Hooks.Add(hook);
}
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]
internal protected void Initialize()
{
try
{
MediaSocketArgs arg;
if (MaxSenders < -1) MaxSenders = -1;
if (MaxReceivers < -1) MaxReceivers = -1;
int availble = Math.Abs(MaxSenders + MaxReceivers);
if (availble > 0)
{
if (SocketWorkers == null) SocketWorkers = new System.Collections.Concurrent.ConcurrentStack<MediaSocketArgs>();
if (OutgoingData == null) OutgoingData = new System.Collections.Concurrent.ConcurrentStack<DataChunk>();
if (SocketBuffer == null) SocketBuffer = new byte[m_BufferSize = BlockSize * BlockSize];
else Array.Resize<byte>(ref SocketBuffer, m_BufferSize = (SocketBuffer.Length + BlockSize));
int offset = 0;
while (SocketWorkers.TryPop(out arg) && --availble >= 0)
{
arg.Reset();
offset += arg.Count;
arg.Completed -= CoreSocketAsyncEventArgsCompleted;
SocketWorkers.Push(arg);
}
int maxOffset = SocketBuffer.Length;
while (offset < maxOffset)
{
arg = new MediaSocketArgs(SocketBuffer, offset, BlockSize);
offset += BlockSize;
arg.Completed += CoreSocketAsyncEventArgsCompleted;
SocketWorkers.Push(arg);
}
}
}
catch { throw; }
}
internal bool AddressWorker(MediaSocketArgs worker)
{
if (worker == null) return false;
worker.Completed += CoreSocketAsyncEventArgsCompleted;
return AddressedWorkers.TryAdd(worker.Id, worker);
}
internal bool TryDequeOrCreateWorker(bool tryGetWorker, ref int counter, out MediaSocketArgs worker)
{
if (m_Disposing)
{
worker = null;
return false;
}
if (!tryGetWorker)
{
if (SocketWorkers.TryPop(out worker))
{
if (SendersAvailable > 0) --SendersAvailable;
return AddressWorker(worker);
}
return false;
}
else if (counter == -1)
{
worker = new MediaSocketArgs(null, 0, 0);
return AddressWorker(worker);
}
worker = null;
return false;
}
internal void EnqueWorker(MediaSocketArgs worker)
{
worker.Completed -= CoreSocketAsyncEventArgsCompleted;
if (!m_Disposing && AddressedWorkers.TryRemove(worker.Id, out worker))
{
if (worker.PoolId == PoolId)
{
worker.Reset();
SocketWorkers.Push(worker);
if (worker.LastOperation == SocketAsyncOperation.Send && SendersAvailable > -1) ++SendersAvailable;
else if (worker.LastOperation == SocketAsyncOperation.Receive && ReceiversAvailable > -1) ++ReceiversAvailable;
}
else
{
worker.Dispose();
}
}
}
public bool TryGetWorker(SocketAsyncOperation type, out MediaSocketArgs worker)
{
if (m_Disposing)
{
worker = null;
return false;
}
switch (type)
{
case SocketAsyncOperation.SendPackets:
case SocketAsyncOperation.SendTo:
case SocketAsyncOperation.Send:
{
return TryDequeOrCreateWorker(SendingWorkersAvailable, ref SendersAvailable, out worker);
}
case SocketAsyncOperation.ReceiveMessageFrom:
case SocketAsyncOperation.ReceiveFrom:
case SocketAsyncOperation.Receive:
{
return TryDequeOrCreateWorker(ReceivingWorkersAvailable, ref ReceiversAvailable, out worker);
}
case SocketAsyncOperation.Accept:
case SocketAsyncOperation.Connect:
case SocketAsyncOperation.Disconnect:
{
return TryDequeOrCreateWorker(true, ref ReceiversAvailable, out worker);
}
case SocketAsyncOperation.None:
default:
{
worker = null;
return false;
}
}
}
public void EnqueOutgoingData(IEnumerable<IPacket> packets)
{
if (packets == null || packets.Count() == 0) return;
int pieceOffset = 0;
foreach (IPacket packet in packets)
{
byte[] stack = packet.ToBytes().ToArray();
int pieceSize = Math.Min(BlockSize, stack.Length);
while (pieceOffset > 0)
{
OutgoingData.Push(new DataChunk(pieceOffset == 0 ? packet.Yield() : null, new ArraySegment<byte>(stack, pieceOffset, pieceSize)));
pieceOffset += pieceSize;
}
}
}
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]
internal void SendOutgoingData(Socket sendingSocket)
{
MediaSocketArgs worker;
DataChunk chunk;
if (OutgoingData.TryPeek(out chunk) && TryGetWorker(SocketAsyncOperation.Send, out worker))
{
List<ArraySegment<byte>> segments = new List<ArraySegment<byte>>();
IEnumerable<IPacket> toTransfer = Enumerable.Empty<IPacket>();
while (OutgoingData.TryPeek(out chunk) &&
OutgoingData.TryPop(out chunk))
{
if (chunk.Source != null) toTransfer.Concat(chunk.Source);
segments.Add(chunk.Data);
}
worker.Transferred = toTransfer;
worker.BufferList = segments;
if (!sendingSocket.SendAsync(worker)) OnPoolWorkerCompleted(worker);
}
}
#endregion
#region IDisposable
public void Dispose() { Dispose(true); }
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]
void Dispose(bool disposing)
{
if (m_Disposing = disposing)
{
PoolWorkerCompleted -= MediaSocketPoolWorkerCompleted;
MediaSocketArgs worker;
foreach (Guid key in AddressedWorkers.Keys.ToArray())
{
if (AddressedWorkers.TryGetValue(key, out worker))
{
if (!AddressedWorkers.TryRemove(key, out worker) || worker == null) Utility.BreakIfAttached();
else worker.Dispose();
}
}
while (SocketWorkers.TryPop(out worker)) worker.Dispose();
EventProxy hook;
while (Hooks.TryTake(out hook)) { hook = null; }
}
}
void IDisposable.Dispose() { Dispose(true); }
#endregion
#region IEnumerator
IEnumerator<EventProxy> IEnumerable<EventProxy>.GetEnumerator() { return Hooks.GetEnumerator(); }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return Hooks.GetEnumerator(); }
#endregion
}
#endregion
#endregion
And here is an example of the IPacket
interface
#region Packet Classes
#region IPacket
public interface IPacket
{
IEnumerable<byte> ToBytes();
int Length { get; }
DateTime? Sent { get; set; }
DateTime Created { get; set; }
byte? Channel { get; set; }
}
#endregion
#region MediaPacket
public abstract class MediaPacket : IPacket
{
public abstract int Length { get; }
public byte? Channel { get; set; }
public DateTime Created { get; set; }
public DateTime? Sent { get; set; }
public abstract byte[] Payload { get; internal protected set; }
public abstract IEnumerable<byte> ToBytes();
public MediaPacket(byte? channel = null)
{
Created = DateTime.UtcNow;
if (channel.HasValue) Channel = channel;
}
}
#endregion
#endregion
And Finally the MediaCounter
class which is the smallest unit presented here and is responsible for a majority of the work involved!
#region MediaCounter
public class MediaCounter
{
#region Fields
public readonly DateTime Created = DateTime.UtcNow;
public readonly byte Overhead;
public Guid Id;
internal DateTime
m_InitialOperation = DateTime.MinValue,
m_LastOperation = DateTime.MinValue;
internal ulong
m_Value, m_Cycles, m_Incremented;
#endregion
#region Properties
public bool Cycled { get { return m_Cycles > 0; } }
public bool Incremented { get { return m_Incremented > 0; } }
public ulong Cycles { get { return m_Cycles; } }
public ulong Increments { get { return m_Incremented; } }
public ulong Value { get { return m_Value; } }
public DateTime InitialOperation { get { return m_InitialOperation; } }
public DateTime LastOperation { get { return m_LastOperation; } }
public BigInteger BigValue
{
get
{
if (m_Cycles > 0) return m_Value + (m_Cycles * ulong.MaxValue);
return m_Value;
}
}
public BigInteger CalculatedOverhead { get { return m_Incremented * Overhead; } }
public bool PerformedOperations { get { return (m_InitialOperation != DateTime.MinValue); } }
#endregion
#region Methods
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]
public void Increment(ulong amount, bool includeOverhead = true, bool incrementIncrements = true)
{
DateTime now = DateTime.UtcNow;
if (m_InitialOperation == DateTime.MinValue) m_InitialOperation = now;
if (m_LastOperation == DateTime.MinValue) m_LastOperation = now;
unchecked
{
if (incrementIncrements) ++m_Incremented;
ulong check = m_Value;
if ((m_Value += amount +
(includeOverhead ? (ulong)Overhead : 0)) < check)
++m_Cycles;
}
}
public void Increment(int amount, bool includeOverhead = true, bool incrementIncrements = true) { Increment((ulong)amount, includeOverhead, incrementIncrements); }
public double Rate(double divider, DateTime? start = null, DateTime? end = null) { return CalculateRate(ref m_Value, ref divider, start, end); }
public double IncrementRate(double divider, DateTime? start = null, DateTime? end = null) { return CalculateRate(ref m_Incremented, ref divider, start, end); }
internal double CalculateRate(ref ulong value, ref double divider, DateTime? start = null, DateTime? end = null)
{
if (divider == 0) return 0;
if (!start.HasValue && m_InitialOperation == DateTime.MinValue) start = Created;
if (!end.HasValue && m_LastOperation == DateTime.MinValue) end = DateTime.UtcNow;
start = start ?? m_InitialOperation;
end = end ?? m_LastOperation;
unchecked
{
return value / divider / (start.Value - end.Value).TotalSeconds;
}
}
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]
public void ResetIncrements() { m_Incremented = 0; }
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]
public void ResetCycles() { m_Cycles = 0; }
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]
public void ResetValue() { m_Value = 0; }
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]
public void ResetOperationTimes() { m_InitialOperation = m_LastOperation = DateTime.MinValue; }
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]
public void Reset()
{
ResetOperationTimes();
ResetIncrements();
ResetCycles();
ResetValue();
}
#region Constructors
public MediaCounter(byte overhead = 0)
{
Id = Guid.NewGuid();
Overhead = overhead;
}
public MediaCounter(int overhead = 0)
: this((byte)overhead) { }
public MediaCounter(MediaCounter other, bool copyOverhead = true)
{
m_Value = other.m_Value;
m_Cycles = other.m_Cycles;
if (copyOverhead) Overhead = other.Overhead;
}
#endregion
#endregion
#region Operators
public static MediaCounter operator +(MediaCounter a, MediaCounter b)
{
a.m_Value += b.m_Value;
return a;
}
public static MediaCounter operator -(MediaCounter a, MediaCounter b)
{
a.m_Value -= b.m_Value;
return a;
}
public static MediaCounter operator *(MediaCounter a, MediaCounter b)
{
a.m_Value *= b.m_Value;
return a;
}
public static MediaCounter operator /(MediaCounter a, MediaCounter b)
{
a.m_Value /= b.m_Value;
return a;
}
public static implicit operator BigInteger(MediaCounter counter) { return counter.BigValue; }
public static implicit operator ulong(MediaCounter counter) { return counter.m_Value; }
public static implicit operator long(MediaCounter counter) { return (long)counter.m_Value; }
public static implicit operator int(MediaCounter counter) { return (int)counter.m_Value; }
public static implicit operator uint(MediaCounter counter) { return (uint)counter.m_Value; }
public static implicit operator short(MediaCounter counter) { return (short)counter.m_Value; }
public static implicit operator ushort(MediaCounter counter) { return (ushort)counter.m_Value; }
#endregion
}
#endregion
As previously indicated you will find these classes and much more @ Managed Media Aggregation when RC2 Flawless is released in the coming weeks!
Enjoy these concepts until then!