Reputation: 934
I hope I can word this correctly. I have a WCF Service that I'm using (duplex channel communications) in which one client registers with the service. The service's registration method returns a value. I want the the method of the called service registration method to also call the callback method that will send out notification of the client registration (I have my reasons for this and explaining it here will only confuse the issue). The problem is that the client's implemented callback has to run in the main application thread to work correctly (due mostly to integration with a third-party application). The service registration method call is also occuring in this same thread, so it effectively locks up since the client is looking for a return from the service registration method holding on to the thread preventing the callback method from being able to run. If I tell it to call all callback methods for all contexts other than the one just registered, it works just fine. But if I tell it to include it, obviously it locks up because that thread is already locked up. I can set the callback attribute property for UseSynchronizationContext to false, but then this means the callback method is called on a separate thread from the main and now the rest of the program will not work. Any help would be greatly appreciated.
Here's basically that registration method (first draft..)
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,
UseSynchronizationContext = false,
ConcurrencyMode = ConcurrencyMode.Multiple,
Namespace = "http://MyApp/Design/CADServiceTypeLibrary/2012/12")]
public class DTOTransactionService : IDTOTransactionService, IDisposable
{
//some more stuff
public CADManager RegisterCADManager(int processID, bool subscribeToMessages)
{
List<CADManager> cadMgrs = this.CADManagers;
bool registered = false;
//Create new CADManager mapped to process id
CADManager regCADManager = new CADManager(processID);
//Add to CADManagers List and subscribe to messages
if (regCADManager.IsInitialized)
{
cadMgrs.Add(regCADManager);
this.CADManagers = cadMgrs;
//Subscribe to callbacks
if (subscribeToMessages)
SubscribeCallBack(regCADManager.ID);
registered = true;
}
//Send registration change notification
RegistrationState state;
if (registered)
state = RegistrationState.Registered;
else
state = RegistrationState.RegistrationException;
foreach (CallBackSubscriber subscriber in this.CallBackSubscribers)
{
subscriber.CallBackProxy.CADManagerRegistrationNotification(regCADManager.ID, state);
}
return regCADManager;
}
}
Upvotes: 1
Views: 1580
Reputation: 934
I think I've got it figured out. It struck me that a little deeper what's happening is that since the call to the service method is expecting a return value and since the callback will occur in the same thread as the client method expecting a return value that this could be the result of the deadlock condition. I then decided to try calling the callback methods in the service using a different thread to work around the current thread condition. In other words, work around the current thread whose method has yet to have provided a return value from the service method. It worked! Was this the right approach? I have enough experience to be dangerous here, so if someone else's experience shows this to be the wrong way to handle this, I'm all ears.
Thread notifyThread = new Thread(delegate()
{
foreach (CallBackSubscriber subscriber in this.CallBackSubscribers)
{
subscriber.CallBackProxy.CADManagerRegistrationNotification(regCADManager.ID, state);
}
});
Update:
Yes, the threading and deadlock condition was the issue, however the more appropriate fix I recently discovered is to use SynchronizationContext. To use, create a property or field of the type SynchronizationContext, then assign the value to the field/property while in the context you wish to capture using SynchronizationContext.Current. Then, use the Post() method (providing it a delegate via the SendOrPostCallback object) in the callback method being called by the Service. A short example:
private SynchronizationContext _appSyncContext = null;
private DTOCommunicationsService()
{
this.AppSyncContext = SynchronizationContext.Current;
//Sets up the service proxy, etc, etc
Open();
}
// Callback method
public void ClientSubscriptionNotification(string clientID, SubscriptionState subscriptionState)
{
SendOrPostCallback callback = delegate(object state)
{
object[] inputArgs = (object[])state;
string argClientID = (string)inputArgs[0];
SubscriptionState argSubState = (SubscriptionState)inputArgs[1];
//Do stuff with arguments
};
_appSyncContext.Post(callback, new object[] { clientID, subscriptionState });
}
Upvotes: 1