Posts Tagged ‘named’
Implementing IAsyncResult aka NamedPipeClientStream.BeginConnect
Anybody who is working with asynchronously called methods has probably used IAsyncResult. Well, nobody is really using it, instead you are used to get it as a return-value of some BeginX function and put it back in the corresponding EndX function. …
For my current project, i’m using NamedPipeClientStream to connect to a named pipe server (obviously!). I noticed that it does not support asynchronous connection. As my whole projekt is building on asnychronous techniques, I started to implement it.
Here is the code that I come up with for implementing IAsyncResult
using System;
using System.Threading;
namespace Esskar.Threading
{
public class AsyncExcecuter : IAsyncResult, IDisposable
{
private readonly AsyncCallback m_callback;
private bool m_completed;
private bool m_completedSynchronously;
private ManualResetEvent m_waitHandle = null;
private readonly object m_asyncState;
private readonly object m_syncRoot = new object();
public AsyncExcecuter(AsyncCallback cb, object state)
: this(cb, state, false) { }
public AsyncExcecuter(AsyncCallback cb, object state, bool completed)
{
m_callback = cb;
m_asyncState = state;
m_completed = completed;
m_completedSynchronously = completed;
}
#region IAsyncResult Members
public object AsyncState
{
get { return m_asyncState; }
}
public WaitHandle AsyncWaitHandle
{
get
{
lock (m_syncRoot)
{
if (m_waitHandle == null)
m_waitHandle = new ManualResetEvent(false);
return m_waitHandle;
}
}
}
public bool CompletedSynchronously
{
get { lock (m_syncRoot) { return m_completedSynchronously; } }
private set { lock (m_syncRoot) { m_completedSynchronously = value; } }
}
public bool IsCompleted
{
get { lock (m_syncRoot) { return m_completed; } }
private set { lock (m_syncRoot) { m_completed = value; } }
}
#endregion
#region IDisposable Members
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
lock (m_syncRoot)
{
if (m_waitHandle != null)
{
((IDisposable)m_waitHandle).Dispose();
m_waitHandle = null;
}
}
}
}
public void Execute()
{
this.Execute(false);
}
public void Execute(bool synchron)
{
if (!this.IsCompleted)
{
if (!synchron)
{
if (!System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(this.InvokeCallback)))
// ThreadPool at its limit (Thanks to Ianier Munoz)
throw new NotSupportedException("ThreadPool failed to queue work item.");
}
else
{
this.InvokeCallback(null);
}
this.CompletedSynchronously = synchron;
}
}
private void InvokeCallback(object state)
{
if (m_callback != null)
m_callback(this);
lock (m_syncRoot)
{
if(m_waitHandle != null)
m_waitHandle.Set();
}
this.IsCompleted = true;
}
}
}
The code is quite simple, i hope. The class uses a ManualResetEvent and signals it when the operation is completed (: the callback function returned).
Having that i implemented BeginConnect and EndConnect for as an LinQ Extension Methods.
using System;
using System.Linq;
using System.IO.Pipes;
using Esskar.Threading;
namespace Esskar.IO.Pipes
{
public static class NamedPipeClientStreamExtension
{
public static IAsyncResult BeginConnect(this NamedPipeClientStream pipe, AsyncCallback callback, object state)
{
AsyncExcecuter executor = new AsyncExcecuter(callback, state);
executor.Execute();
return executor;
}
public static void EndConnect(this NamedPipeClientStream pipe, IAsyncResult asyncResult)
{
pipe.Connect();
}
}
}
Done. Quite easy, isn’t it?
NamedEvents in C# unlimited?
Due to the fact that .NET offers a great way to use asynchronous eventing, i forgot about the thread-synchronization-events mechanism (CreateEvent, OpenEvent) that i used when programming old-school WINAPI in C/C++. However, I stumbeled about a problem that i could not solve using asynchronous eventing.
.NET offers the EventWaitHandle class which is located in the System.Threading namespace. AutoResetEvent and ManualResetEvent already use it as base class. As i needed named events and to keep thinks simple a wrote two new classes NamedAutoResetEvent and NamedManualResetEvent that wrap around the EventWaitHandle.
using System;
using System.Threading;
namespace Esskar.Threading
{
public enum NamedEventSpace
{
Default, Local, Global,
}
public abstract class NamedEvent : EventWaitHandle
{
private string m_name;
public NamedEvent(bool initalState, EventResetMode mode, string name, NamedEventSpace nameSpace)
: base(initalState, mode, NamedEvent.BuildName(ref name, nameSpace))
{
m_name = name;
}
public string Name
{
get { return m_name; }
}
private static string BuildName(ref string name, NamedEventSpace nameSpace)
{
if (!string.IsNullOrEmpty(name))
{
switch (nameSpace)
{
case NamedEventSpace.Global: name = @"Global\" + name; break;
case NamedEventSpace.Local: name = @"Local\" + name; break;
}
}
return name;
}
}
public class NamedAutoResetEvent : NamedEvent
{
public NamedAutoResetEvent(bool initialState, string name)
: this(initialState, name, NamedEventSpace.Default) { }
public NamedAutoResetEvent(bool initialState, string name, NamedEventSpace nameSpace)
: base(initialState, EventResetMode.AutoReset, name, nameSpace) { }
}
public class NamedManualResetEvent : NamedEvent
{
public NamedManualResetEvent(bool initialState, string name)
: this(initialState, name, NamedEventSpace.Default) { }
public NamedManualResetEvent(bool initialState, string name, NamedEventSpace nameSpace)
: base(initialState, EventResetMode.ManualReset, name, nameSpace) { }
}
}
Since already knew that i could expect a high number (> 30K) of named events to be created, i needed to know whether or not i run into an upper limit. So i wrote a little test program
using System;
using Esskar.Threading;
namespace Esskar.Testing.NamedEvents
{
class Program
{
static void Main(string[] args)
{
string name = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const int defCount = 10 * 1000;
int count = defCount;
if (args.Length > 0) count = int.Parse(args[0]);
if (count <= 0) count = defCount;
Console.WriteLine(string.Format("About to create {0} named events.", count));
NamedEvent[] tmp = new NamedEvent[count];
for(int i = 0; i < count; i++)
{
tmp[i] = new NamedAutoResetEvent(false, name + i);
if (tmp[i].SafeWaitHandle.IsInvalid)
throw new Exception(string.Format("Unable to create event #{0}", i));
if ((i % 100) == 0) Console.Write('.');
}
Console.ReadLine();
}
}
}
that would create me number of named threads based on a commandline argument. As you try it, you will notice the speed of the progress-points will decrease as the number of created events increases. I was still able to create 100K events without any problem.