I have a console application which uses an external library. The library insists on always being called from the same thread; it locks up otherwise. (I did try running as STA to see if that would fix it - but no, it really insists you have to always use the same thread. My guess is thread-local storage...)
Previously the application communicated using a raw TCP connection. However, I recently changed it to use WCF. Now it seems that WCF chooses threads at random to run my code, which results in spectacular failure.
I need to absolutely 100% prevent this behaviour from ever happening. I don't care which thread my code runs in, so long as it is always the same thread! I've spent the last few days scouring the face of the Internet and repeatedly smashing my head into the keyboard in a desperate attempt to make WCF stop using threads.
Things I've tried:
InstanceContextMode.Singleforces WCF to use a single object for my stuff, which is useful, but doesn't directly address the problem.ConcurrencyMode = ConcurrencyMode.Singleguarantees that only one thread will be running concurrently, but doesn't promise which one.UseSynchronizationContextseems to not have any effect on anything, as best as I can tell.
Using these flags, I managed to get to the point where each client gets a single thread. But that still means that when the first client disconnects and the next client connects, I get a different thread and the library hangs my program.
I also tried the brute-force approach: I wrote a class that creates its own worker thread and lets you enqueue code to execute on that thread. I tested the class in isolation, and it appears to work perfectly, but when I try to use it in my WCF application, something extremely strange happens. The program processes the first command perfectly, returns a result to the client, and then hangs forever.
This behaviour makes absolutely no sense whatsoever. I can see from the console output that it isn't stuck in the external library, and it isn't stuck in my new work-queueing class either. So where the hell has it got stuck?!
At this point, I would normally start inserting more debug prints - except you can't insert debug prints into WCF, only the code that it calls. So I cannot tell what the service host is trying to do...
I've seen various SO answers on this topic, all of which say that the solution is something completely different. There's one that talks about "synchronisation contexts" and is more or less incomprehensible - but it appears it would do the same thing as my work queue class. There's another about setting various service flags - which I've already done and it didn't fix it. Somebody else suggested implementing your own IOperationBehaviour (which looks insanely complicated).
Basically at this point I'm not sure what the hell to do, and I can't get this stuff to work. Plz help. :-(
[Console application, self-hosted, NetTcpBinding, configuration in code, .NET 4 - in case it matters...]
Here is the work-queue class, in case it matters: [It's a tad large, BTW.]
public sealed class ThreadManager
{
private Thread _thread; // Worker thread.
private volatile Action _action; // Enqueued method.
private volatile object _result; // Method result.
private volatile bool _done; // Has the method finished executing?
public void Start()
{
_action = null;
_result = null;
_done = true;
_thread = new Thread(MainLoop);
_thread.Start();
}
public void ExecuteInWorkerThread(Action action)
{
// Wait for queue to empty...
Monitor.Enter(this); // Lock the object, so we can inspect it.
while (_action != null)
{
Monitor.Pulse(this); // Wake up the next thread waiting on the lock.
Monitor.Wait(this); // Release lock, wait for Pulse(), acquire lock.
}
// Enqueue action...
_action = action;
_done = false;
// Wait for action to complete...
while (! _done)
{
Monitor.Pulse(this); // Wake up the next thread waiting on the lock.
Monitor.Wait(this); // Release lock, wait for Pulse(), acquire lock.
}
// Worker thread has finished doing it's thing now.
Monitor.Pulse(this); // Wake up any threads trying to enqueue work.
Monitor.Exit(this); // Release the lock.
}
public T ExecuteInWorkerThread<T>(Func<T> action)
{
ExecuteInWorkerThread(() => { _result = action(); });
return (T) _result; // If this cast fails, something has gone spectacularly wrong!
}
// Runs forever in worker thread.
private void MainLoop()
{
while (true)
{
// Wait for an action to dequeue...
Monitor.Enter(this); // Lock object so we can inspect it.
while (_action == null)
{
Monitor.Pulse(this); // Wake up the next thread waiting on the lock.
Monitor.Wait(this); // Release lock, wait for Pulse(), acquire lock.
}
// Dequeue action...
var action = _action;
_action = null;
// Perform the action...
action(); // Do the actual action!
_done = true; // Tell the caller we're done.
Monitor.Pulse(this); // Wake the caller up.
Monitor.Exit(this); // Release the lock.
}
}
}
As I said, when I test this in isolation, it appears to work just fine. [Mutter something about multithreaded coding and determinism.] When run in WCF, it always fails at exactly the same point.
and then hangs forever.<-- hangs where? on which call(s)? what are the stack traces of all the threads of your application. you are either deadlocked or a thread is waiting to do something and hasnt been signaled correctly. - walThreadManager, so it seems to be hanging in WCF somewhere. - MathematicalOrchid