Re: Question on Socket disconnect handling
- From: Matthew <nospamplease>
- Date: Thu, 28 May 2009 19:58:19 -0700
I might as well post the reference count mechanism I was talking about.
It's inspired from a reference counting technique that's useful when
unloading Plug N Play device drivers. It's a shame such a mechanism is
not part of the classes in the .NET Framework that have asynchronous
callbacks.
Anyone who believes in the orderly shutdown of asynchronous callbacks
can do something like this. (Anyone who does not believe in orderly
shutdown or resource acquisition and release should please stop
developing software.)
(www.developersdex.com seems to make words like `List' and
`ArraySegment' lower case when they shouldn't be below)
using System;
using System.Collections.Generic;
using System.Threading;
using System.Net.Sockets;
namespace Solution
{
class Program
{
static void SendCallback(IAsyncResult ar)
{
Console.WriteLine("SendCallback entered");
SendCallBackArgs args =
(SendCallBackArgs)ar.AsyncState;
try
{
#if true
args.Socket.EndSend(ar);
// simulate timing that can cause callback
// and Close to race
Thread.Sleep(1000);
args.Event.Set();
#else
// simulate timing that can cause callback
// and Close to race
Thread.Sleep(1000);
args.Event.Set();
args.Socket.EndSend(ar);
#endif
}
catch(ObjectDisposedException ex)
{
Console.WriteLine("Error: " + ex.Message);
}
Console.WriteLine("SendCallback returning");
}
static void Main(string[] args)
{
// can be any server that accepts incoming TCP
// connections
const string TEST_HOST = "localhost";
const int TEST_PORT = 80;
ManualResetEvent send_done =
new ManualResetEvent(false);
try
{
SafeSocket socket = new SafeSocket(
new Socket(
AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp
)
);
try
{
socket.Connect(TEST_HOST, TEST_PORT);
List<ArraySegment<byte>> buffer_list =
new List<ArraySegment<byte>>();
ArraySegment<byte> segment =
new ArraySegment<byte>(
new byte[] { 0 }
);
buffer_list.Add(segment);
socket.BeginSend(
buffer_list,
SocketFlags.None,
SendCallback,
new SendCallBackArgs(
send_done, socket
)
);
// simulate timing that can cause
// callback and Close to race
Thread.Sleep(250);
}
finally
{
Console.WriteLine("doing work...");
// try to cancel send (or receive or
// whatever asynchronous callback is
// running) because it's time to
// shutdown
Console.WriteLine(
"shutting down subsystem..."
);
socket.Dispose();
}
}
finally
{
send_done.Close();
Console.WriteLine("Close called on event");
}
// simulate timing that can allow callback to
// proceed (and error)
Thread.Sleep(2000);
Console.WriteLine("end of test");
}
private class SendCallBackArgs
{
public SendCallBackArgs(
ManualResetEvent evt,
SafeSocket socket
)
{
this.evt = evt;
this.socket = socket;
}
private readonly ManualResetEvent evt;
public ManualResetEvent Event
{ get { return evt; } }
private readonly SafeSocket socket;
public SafeSocket Socket
{ get { return socket; } }
}
}
// a shutdown mechanism to prevent asynchronous
// callbacks from potentially accessing resources that
// have already been disposed
// for instance, a callback associated with
// System.Threading.Timer may still run after its
// parameterless Dispose has been called, and the same
// is true of the BeginSend method of the Socket class
class AsyncUsage
{
private bool shutting_down = false;
private bool shut_down = false;
private int count = 1;
private ManualResetEvent unused = null;
public AsyncUsage()
{
unused = new ManualResetEvent(false);
}
// to be called at the beginning of some
// asynchronous callback; returns true if usage is
// acquired
public bool Acquire()
{
// attempt to reserve usage
Interlocked.Increment(ref count);
// check if further asynchronous operations
// are prohibited;
// this must occur _after_ the increment
bool acquired = !shutting_down;
if(!acquired)
{
Release();
}
return acquired;
}
// to be called at the end of some asynchronous
// callback
public void Release()
{
int new_count = Interlocked.Decrement(
ref count
);
if(new_count == 0 && !shut_down)
{
shut_down = true;
unused.Set();
}
}
// to be called before any resources used by
// asynchronous callback(s) are disposed
public void FinalWaitRelease()
{
if(!shutting_down)
{
// prevent any new asynchronous operations
// from beginning; this must occur
// _before_ the Release
shutting_down = true;
// wait for all current asynchronous
// operations to finish
Release();
unused.WaitOne();
unused.Close();
unused = null;
}
}
}
// a socket wrapper that ensures its callback(s) will
// do nothing after a successful return from Dispose()
public class SafeSocket : IDisposable
{
private AsyncUsage async_usage = null;
private Socket socket = null;
private AsyncCallback
send_callback,
receive_callback;
private bool disposed = false;
public SafeSocket(Socket a_socket)
{
async_usage = new AsyncUsage();
socket = a_socket;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if(!this.disposed)
{
if(disposing)
{
// clean up managed resources
async_usage.FinalWaitRelease();
if(socket != null)
{
try
{
socket.Shutdown(
SocketShutdown.Both
);
}
finally
{
socket.Close();
socket = null;
}
}
}
// clean up unmanaged resources (if there
// are any)
disposed = true;
}
}
~SafeSocket()
{
Dispose(false);
}
public void Connect(string host, int port)
{
socket.Connect(host, port);
}
public IAsyncResult BeginSend(
IList<ArraySegment<byte>> buffers,
SocketFlags socketFlags,
AsyncCallback callback,
Object state
)
{
send_callback = callback;
return socket.BeginSend(
buffers,
socketFlags,
SafeSendCallback,
state
);
}
public int EndSend(IAsyncResult asyncResult)
{
return socket.EndSend(asyncResult);
}
public IAsyncResult BeginReceive(
IList<ArraySegment<byte>> buffers,
SocketFlags socketFlags,
AsyncCallback callback,
Object state
)
{
receive_callback = callback;
return socket.BeginReceive(
buffers,
socketFlags,
SafeReceiveCallback,
state
);
}
public int EndReceive(IAsyncResult asyncResult)
{
return socket.EndReceive(asyncResult);
}
private void SafeSendCallback(IAsyncResult ar)
{
if(async_usage.Acquire())
{
try
{
send_callback(ar);
}
finally
{
async_usage.Release();
}
}
}
private void SafeReceiveCallback(IAsyncResult ar)
{
if(async_usage.Acquire())
{
try
{
receive_callback(ar);
}
finally
{
async_usage.Release();
}
}
}
}
}
Output:
SendCallback entered
doing work...
shutting down subsystem...
SendCallback returning
Close called on event
end of test
*** Sent via Developersdex http://www.developersdex.com ***
.
- Follow-Ups:
- Re: Question on Socket disconnect handling
- From: Matthew
- Re: Question on Socket disconnect handling
- References:
- Re: Question on Socket disconnect handling
- From: Peter Duniho
- Re: Question on Socket disconnect handling
- Prev by Date: Re: Hex editing a file?
- Next by Date: Re: Hex editing a file?
- Previous by thread: Re: Question on Socket disconnect handling
- Next by thread: Re: Question on Socket disconnect handling
- Index(es):
Relevant Pages
|
Loading