Hosting services in Mono using the BasicHttpBinding will block incomming requests until the previous request is handled. In dotNET this is not the case. Another difference between dotNET and Mono.
I’ll demonstrate this by using a self calling service. See code below. The service will call itself when some other value than 99 is submitted.
class Program { static void Main(string[] args) { // host the service var host = new ServiceHost(typeof(SelfService)); host.AddServiceEndpoint(typeof(ISelfService), new BasicHttpBinding(), "http://localhost:666/SelfService"); host.Open(); try { // Subsequal calls will be blocked until this call is ready var result = SelfServiceProxy.CreateDefault().CallMe(0); Console.WriteLine(result); } catch (Exception ex) { Console.WriteLine("Main thread exception"); } Console.ReadLine(); host.Close(); } } [ServiceContract] public interface ISelfService { [OperationContract] string CallMe(int value); } // serviceimplementation public class SelfService : ISelfService { public string CallMe(int value) { if (value == 99) return "Hello recursive"; // recursive call with the correct value (will be blocked on BasicHttpBinding under Mono) else return SelfServiceProxy.CreateDefault().CallMe(99); } }
Running this in dotNET will result in the text “Hello recursive” in the console. In Mono the execution takes a long time and eventualy prints the “Main thread exception” from the catch-block. Inspecting the exception shows it’s a System.TimeoutException.
What’s happening? The main thread proxy will call the CallMe operation with value = 0. The service will try to call itself with value = 99, the channel is opened and the request is made, but the execution is blocked by the first call (the main thread) Now whe have a deadlock, everybody is waiting for the service to complete but it can’t. Eventually the default timeout of 1 minute kicks in and the main thread gets an exception. The blocked recursive call from the service (value=99) actually gets executed, but the calling code is no longer there.
The only real solution to this problem is to use some other binding (like net.tcp) that doesn’t use the HttpListener. Or to use another address for each subsequal call but then you’ll need to know the number of subsequal requests. In my sample project I will also show the use of OperationTimeout to make the recursive call fail before the main thread fails. To set the OperationTimeOut in Mono you should hack a little bit in your proxy, see code below
public class SelfServiceProxy : ClientBase<ISelfService>, ISelfService { public SelfServiceProxy(Binding binding, EndpointAddress address) : base(binding, address){} public static SelfServiceProxy CreateShortTimeout() { var address = new EndpointAddress("http://localhost:666/SelfService"); var binding = new BasicHttpBinding() { ReceiveTimeout = TimeSpan.FromSeconds(4) }; var result = new SelfServiceProxy(binding, address); // timeout should be set by binding ReceiveTimeout, but manual set is needed result.InnerChannel.OperationTimeout = TimeSpan.FromSeconds(4); return result; } }
The idea of setting the OperationTimeout is to use a shorter timeout for the subsequal calls than for the main thread proxy. That way the main thread will continue without an exception.
Pingback: DataContract weird characters and Mono | Erictummers's Blog
just made a bug report of this https://bugzilla.xamarin.com/show_bug.cgi?id=11606