WCF thread affinity and synchronization.
WCF does not promise thread affinity. This means that WCF does not promise that the thread that started the service operation execution will be the one who will finish the execution. In case the operation waits on some wait handle (wait for IO or DB for example) the thread might return to the thread pool and when execution should resume another thread might do the work.
public class MyService : IContract
{
public static object wait_handle;
public int LongOperation()
{
Before();
LongWait(); //Threads might be switched here
After();
return 0;
}
}
This fact enhance WCF scalability might introduce interesting synchronization scenarios.
If the operation is synchronized using a monitor (Using statement) it means that a Sync_Handle is associated with the thread.
If the execution thread is switched (no thread affinity) the second thread does not have the sync handle and the code will wait. On the other hand the starting thread might be assigned to another WCF request (after it was sent back to the thread pool). This next request have the sync_handle, it would not wait and it might cause damage to synchronized resources.
public class MyService : IContract
{
public static object wait_handle;
public int LongOperation()
{
lock (wait_handle)
{
Before(); //Thread1 has the lock
LongWait(); //Threads might be switched here
After(); // Thread2 does not have the lock but
it runs.
return 0;
}
}
public int SecondOperation()
{
// If accidently Thread1 is allocated it has the
handle and would enter the lock.
lock (wait_handle)
{
DoWork();
}
return 0;
}
}
The solution is simple.
Whene there is no thread affinity do not use monitor and the using statement. Use Events (autoReasetEvent or ManualResetEvent). When WCF perform a thread switch the second thread can set the event and the synchronization will continue as planned.