programming with esskar

Just another WordPress.com weblog

Posts Tagged ‘pipes

Implementing IAsyncResult aka NamedPipeClientStream.BeginConnect

with one comment

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? :-)

Written by esskar

June 30, 2009 at 10:23 am

Follow

Get every new post delivered to your Inbox.