G
Guest
I have a server application. This application has two pieces of shared data.
They are both hashtables. The untroubling hashtable holds the clients data.
The hashtable that is giving me grief represents a list of all of the
clients connected to the server.
Periodically I go through the client list (a.k.a, the irritating hashtable)
using an enumeration to make sure they are still active. The code looks like
this:
IDictionaryEnumerator i = ClientList.GetEnumerator();
while (i.MoveNext())
{
ClientKeepAliveHandler handler = null;
try
{
handler = (ClientKeepAliveHandler)((object[])i.Value)[1];
IAsyncResult ar = handler.BeginInvoke(new
AsyncCallback(this._asyncCallBack), i.Entry);
}
catch (System.Exception ex)
{
_writeError((int)i.Key,ref ex,MethodBase.GetCurrentMethod().Name);
}
}
Now, this iteration continues happliy along and as the asynchronous call
comes back the _asyncCallBack function gets called that looks like this:
DictionaryEntry clientData = (DictionaryEntry)ar.AsyncState;
ClientKeepAliveHandler del = null;
try
{
del = (ClientKeepAliveHandler)((object[])clientData.Value)[1];
del.EndInvoke(ar);
}
catch (Exception ex)
{
ClientKeepAlive -= del;
Exception e = new Exception("Client to be disconnected (most likely due to
inaccessibility).", ex);
_writeError((int)clientData.Key, ref e, MethodBase.GetCurrentMethod().Name,
"SessionId: " + clientData.Key.ToString() + ", Inner Message: " +
e.InnerException.Message);
CloseSession((int)(clientData.Key));
}
Now, if a client cannot be reached (as in un graceful shutdown) the
EndInvoke method raises an exception of "The remote machine doesn't like you"
or something like that. If that happens the code jumps to the error handler,
removes the delegate from the invokation list of the classes KeepAlive event
and sends an email to the developers who may have random bugs in there code.
Then the last line of the error handler closes the session and removes the
clients data. Keep in mind while this code is executing the original code
that started this may still be looping through the hashtable sending off
asynchrounous calls (and is whenever I experience the problem I'm about to
describe). That being said, when the CloseSession function removes an item
from the Hashtable the enumeration bombs with a "oh no the hashtable has
changed" error, as it should.
Now that I have explained all that the question: How should I implement
locking?
The behavior I want is the _asyncCallBack functions should all wait until I
finish looping before even being able to call EndInvoke. So I tried
implementing this in many ways. lock(ClientList), lock(ClientList.SyncRoot),
Hashtable SyncedHash = Hashtable.Synchronized(ClientList) with a
lock(SyncedHash), Monitor.Enter/Exit, lock(SomeRandomObjectForLocking) all
seem to not work. To be clear, I placed the lock statements right before
ClientKeepAlive -= del in the _asyncCallBack function and right before
IDictionaryEnumerator i = ClientList.GetEnumerator() in the original
snipped...help?
They are both hashtables. The untroubling hashtable holds the clients data.
The hashtable that is giving me grief represents a list of all of the
clients connected to the server.
Periodically I go through the client list (a.k.a, the irritating hashtable)
using an enumeration to make sure they are still active. The code looks like
this:
IDictionaryEnumerator i = ClientList.GetEnumerator();
while (i.MoveNext())
{
ClientKeepAliveHandler handler = null;
try
{
handler = (ClientKeepAliveHandler)((object[])i.Value)[1];
IAsyncResult ar = handler.BeginInvoke(new
AsyncCallback(this._asyncCallBack), i.Entry);
}
catch (System.Exception ex)
{
_writeError((int)i.Key,ref ex,MethodBase.GetCurrentMethod().Name);
}
}
Now, this iteration continues happliy along and as the asynchronous call
comes back the _asyncCallBack function gets called that looks like this:
DictionaryEntry clientData = (DictionaryEntry)ar.AsyncState;
ClientKeepAliveHandler del = null;
try
{
del = (ClientKeepAliveHandler)((object[])clientData.Value)[1];
del.EndInvoke(ar);
}
catch (Exception ex)
{
ClientKeepAlive -= del;
Exception e = new Exception("Client to be disconnected (most likely due to
inaccessibility).", ex);
_writeError((int)clientData.Key, ref e, MethodBase.GetCurrentMethod().Name,
"SessionId: " + clientData.Key.ToString() + ", Inner Message: " +
e.InnerException.Message);
CloseSession((int)(clientData.Key));
}
Now, if a client cannot be reached (as in un graceful shutdown) the
EndInvoke method raises an exception of "The remote machine doesn't like you"
or something like that. If that happens the code jumps to the error handler,
removes the delegate from the invokation list of the classes KeepAlive event
and sends an email to the developers who may have random bugs in there code.
Then the last line of the error handler closes the session and removes the
clients data. Keep in mind while this code is executing the original code
that started this may still be looping through the hashtable sending off
asynchrounous calls (and is whenever I experience the problem I'm about to
describe). That being said, when the CloseSession function removes an item
from the Hashtable the enumeration bombs with a "oh no the hashtable has
changed" error, as it should.
Now that I have explained all that the question: How should I implement
locking?
The behavior I want is the _asyncCallBack functions should all wait until I
finish looping before even being able to call EndInvoke. So I tried
implementing this in many ways. lock(ClientList), lock(ClientList.SyncRoot),
Hashtable SyncedHash = Hashtable.Synchronized(ClientList) with a
lock(SyncedHash), Monitor.Enter/Exit, lock(SomeRandomObjectForLocking) all
seem to not work. To be clear, I placed the lock statements right before
ClientKeepAlive -= del in the _asyncCallBack function and right before
IDictionaryEnumerator i = ClientList.GetEnumerator() in the original
snipped...help?