Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WCF client deadlocking due to callback even when callback IsOneWay

new to WCF.

I have a client which is deadlocking when calling a WCF service.

The service will invoke a callback to the client at the time of the call which is marked as IsOneWay. I have confirmed that the service is not blocking on the callback.

The client then immediately calls the same service again (in a tight loop), without having yet serviced the callback. The client then deadlocks (and a breakpoint on the service side never gets triggered).

So to recap:

CLIENT                                SERVICE
Call service -----------------------> (service breakpoint triggers)
(waiting for dispatch thread) <------ Invoke callback (IsOneWay - doesn't block)
                                      Service returns

Call service again immediately -----? (service breakpoint doesn't trigger)
(deadlock)

I am assuming that the callback has grabbed some WCF lock at the client end, and then the second service call from the client also wants that lock, so deadlock results. But this is just assumption.

I have read about ConcurrencyMode but I can't decide which mode to use, or where to put it because I'm not 100% clear on what is going on, and what is being blocked exactly.

I would also prefer to keep all callbacks being serviced by the dispatch thread if possible as it keeps the code simpler.

Can any WCF experts shed light on exactly what is going on here?

Many thanks

like image 739
GazTheDestroyer Avatar asked Jun 08 '11 09:06

GazTheDestroyer


1 Answers

OK, think I've sussed it.

WCF services default to single threaded. All calls and callbacks get marshalled to a single thread (or SynchronizationContext to be more accurate).

My app is a single threaded WPF app, so the SynchronizationContext gets set to the dispatch thread.

When the callback comes in it tries to marshal the call to the dispatch thread, which of course is sat blocking on the original service call. I'm not clear it locks exactly, but there's obviously some global lock that it tries to get before waiting for the dispatch thread.

When the dispatch thread then calls the service again, it deadlocks on this global lock.

Two ways around it:

1) Create the service proxy on a different thread in the first place. All calls will get marshalled through this thread instead and it won't matter that the dispatch thread is blocked.

2) Apply [CallbackBehavior(UseSynchronizationContext = false)] attribute to the client class that implements the callback. This means WCF will ignore the synchronisation context when the callback comes in, and it will service it on any available thread.

I went with 2. Obviously this means I need to marshal callbacks that could update the GUI to the dispatch thread myself, but luckily my callback implementation is a small wrapper anyway, so I just do a _dispatcher.BeginInvoke() in each callback method to marshal ASYNCHRONOUSLY. The dispatch thread will then service when it gets a chance which is what I wanted in the first place.

like image 86
GazTheDestroyer Avatar answered Sep 28 '22 17:09

GazTheDestroyer