Skip to content

Instantly share code, notes, and snippets.

@davidfowl
Created May 24, 2012 01:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save davidfowl/2778783 to your computer and use it in GitHub Desktop.
Save davidfowl/2778783 to your computer and use it in GitHub Desktop.
Before SignalR
// From http://msdn.microsoft.com/en-us/magazine/cc163467.aspx
internal class AsyncResult : IAsyncResult
{
// Fields set at construction which never change while
// operation is pending
readonly AsyncCallback m_AsyncCallback;
readonly Object m_AsyncState;
// Fields set at construction which do change after
// operation completes
const Int32 c_StatePending = 0;
const Int32 c_StateCompletedSynchronously = 1;
const Int32 c_StateCompletedAsynchronously = 2;
Int32 m_CompletedState = c_StatePending;
// Field that may or may not get set depending on usage
ManualResetEvent m_AsyncWaitHandle;
// Fields set when operation completes
Exception m_exception;
public AsyncResult(AsyncCallback asyncCallback, Object state)
{
m_AsyncCallback = asyncCallback;
m_AsyncState = state;
}
public void SetAsCompleted(
Exception exception, Boolean completedSynchronously)
{
// Passing null for exception means no error occurred.
// This is the common case
m_exception = exception;
// The m_CompletedState field MUST be set prior calling the callback
Int32 prevState = Interlocked.Exchange(ref m_CompletedState,
completedSynchronously
? c_StateCompletedSynchronously
: c_StateCompletedAsynchronously);
if (prevState != c_StatePending)
throw new InvalidOperationException(
"You can set a result only once");
// If the event exists, set it
if (m_AsyncWaitHandle != null) m_AsyncWaitHandle.Set();
// If a callback method was set, call it
if (m_AsyncCallback != null) m_AsyncCallback(this);
}
public void EndInvoke()
{
// This method assumes that only 1 thread calls EndInvoke
// for this object
if (!IsCompleted)
{
// If the operation isn't done, wait for it
AsyncWaitHandle.WaitOne();
AsyncWaitHandle.Close();
m_AsyncWaitHandle = null; // Allow early GC
}
// Operation is done: if an exception occured, throw it
if (m_exception != null) throw m_exception;
}
#region Implementation of IAsyncResult
public Object AsyncState
{
get { return m_AsyncState; }
}
public Boolean CompletedSynchronously
{
get
{
return Thread.VolatileRead(ref m_CompletedState) ==
c_StateCompletedSynchronously;
}
}
public WaitHandle AsyncWaitHandle
{
get
{
if (m_AsyncWaitHandle == null)
{
Boolean done = IsCompleted;
ManualResetEvent mre = new ManualResetEvent(done);
if (Interlocked.CompareExchange(ref m_AsyncWaitHandle,
mre, null) != null)
{
// Another thread created this object's event; dispose
// the event we just created
mre.Close();
}
else
{
if (!done && IsCompleted)
{
// If the operation wasn't done when we created
// the event but now it is done, set the event
m_AsyncWaitHandle.Set();
}
}
}
return m_AsyncWaitHandle;
}
}
public Boolean IsCompleted
{
get
{
return Thread.VolatileRead(ref m_CompletedState) !=
c_StatePending;
}
}
#endregion
}
internal class AsyncResult<TResult> : AsyncResult
{
// Field set when operation completes
TResult m_result = default(TResult);
public AsyncResult(AsyncCallback asyncCallback, Object state) :
base(asyncCallback, state)
{
}
public void SetAsCompleted(TResult result,
Boolean completedSynchronously)
{
// Save the asynchronous operation's result
m_result = result;
// Tell the base class that the operation completed
// sucessfully (no exception)
base.SetAsCompleted(null, completedSynchronously);
}
public new TResult EndInvoke()
{
base.EndInvoke(); // Wait until operation has completed
return m_result; // Return the result (if above didn't throw)
}
}
$(function () {
function wait() {
$.post('Chat.ashx', {}, function (data) {
$('#messages').append('<li>' + data + '</li>');
wait();
});
}
function send(data) {
$.post('Chat.ashx/send', { data: data }, function () { });
}
wait();
$('#send').click(function () {
send($('#msg').val());
});
});
public class Chat : IHttpAsyncHandler
{
private readonly static ConcurrentDictionary<AsyncResult, bool> _connections = new ConcurrentDictionary<AsyncResult, bool>();
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
AsyncResult ar = null;
if (context.Request.Path.EndsWith("send"))
{
foreach (var connection in _connections.Keys)
{
var writeCallback = (Action<string>)connection.AsyncState;
string data = context.Request["data"];
writeCallback.Invoke(data);
connection.SetAsCompleted(null, false);
}
ar = new AsyncResult(cb, extraData);
ar.SetAsCompleted(null, true);
return ar;
}
Action<string> write = data =>
{
context.Response.Write(data);
context.Response.Flush();
};
ar = new AsyncResult(cb, write);
_connections.TryAdd(ar, true);
return ar;
}
public void EndProcessRequest(IAsyncResult result)
{
bool removed;
_connections.TryRemove((AsyncResult)result, out removed);
}
public void ProcessRequest(HttpContext context)
{
}
public bool IsReusable
{
get
{
return false;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment