1

I have a WCF service running on CentOS 6.6 + Mono 4.2.1. The service works if contract methods are synchronous, but I would like to make use of async if possible (since there is a lot of I/O intensive tasks that go into each request). When I try and implement any of the service as async, though, I get an exception when I run under Mono. I've included a dummy test method for repro:

Server:

[OperationContract]
Task<string> GetUrl(string url);

async public Task<string> GetUrl(string url)
{
    MailUtils.LogText(LogLevel.Verbose, () => String.Format("Got request for {0}", url));
    string result = String.Empty;

    try
    {
        using (var client = new WebClient())
            result = await client.DownloadStringTaskAsync(url);
    }
    finally
    {
        MailUtils.LogText(LogLevel.Verbose, () => String.Format("Leaving GetUrl(); Got {0} byte(s)", result.Length));
    }
    return result;
}

Client (command line utility):

static void Main(string[] args)
{
    if (args.Length == 2 && args[0] == "-testasync")
    {
        try
        {
            MailSorterServiceClient client = new MailSorterServiceClient();
            var task = client.GetUrlAsync(args[1]);

            task.Wait();

            Console.WriteLine(String.Format("Success: Got {0} byte(s)", (task.Result ?? "").Length));
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex.InnerException.Message);
            Console.WriteLine(ex.InnerException.StackTrace);
        }
        return;
    }

So far as I can tell the server does not receive the request and the client gets an exception:

Object reference not set to an instance of an object
  at System.ServiceModel.Dispatcher.BaseMessagesFormatter.DeserializeReply (System.ServiceModel.Channels.Message message, System.Object[] parameters) [0x000ea] in /root/src/mono-4.2.1/mcs/class/System.ServiceModel/System.ServiceModel.Dispatcher/BaseMessagesFormatter.cs:289

Additional:

The client generated by Visual Studio has two methods:

Task GetUrlAsync(string url) and string GetUrl(string url)

If I change my client code to call GetUrl() the request makes it through to the server process and the server log shows:

[12/11/2015 3:06 PM]: Got request for http://www.google.com/
[12/11/2015 3:06 PM]: Leaving GetUrl(); Got 188337 byte(s)

... but the client returns before it receives the response from the server (client gets back an empty string and no indication of an error).

The async code works as expected under Windows.

Is this a bug in Mono or am I doing something wrong?

I am okay with blocking (with a timeout_ on the client but want the server to be async). Any help is appreciated.

Note: A did see a similar sounding problem on Stack Overflow but it sounds like it was addessed in Mono 4.2.

Community
  • 1
  • 1
Kris Oye
  • 1,158
  • 14
  • 27
  • 1
    That's a mono bug because Mono internal code just crashed which is always a bug. Besides that I'll link you my standard discussion for whether async IO is even the right choice (might be, often is not): http://stackoverflow.com/a/25087273/122718 Why does the EF 6 tutorial use asychronous calls? http://stackoverflow.com/a/12796711/122718 Should we switch to use async I/O by default? – usr Dec 12 '15 at 00:22
  • Also, from the way you are calling the service (useless async IO + Wait call) you might be suffering from this particular misconception: http://stackoverflow.com/a/22591516/122718 – usr Dec 12 '15 at 00:23
  • My sample code is not representative of what my service is actually doing. The actual service takes incoming mail messages, parses them, and runs through a set of user-defined rules to determine where they should go (whitelist/graylist/blacklist). This process hits the disk and several databases. Each message can take between 20 and 100ms to fully process and there may be 10 clients making request per second. Each client is only responsible for submitting a single message, though. – Kris Oye Dec 12 '15 at 00:49
  • This is a really clear case for *not* going async. To service that load you will need 1 thread on average (0.1s*10). You will not achieve anything at all going async and you sacrifice productivity. Frankly, the answers I linked to outline exactly that and allow you to make the decision. – usr Dec 12 '15 at 12:10
  • I'll continue to read through those posts. I figured yielding execution while some I/O operation was going elsewhere was what async was all about. – Kris Oye Dec 14 '15 at 22:03
  • Yes, that's what async is about (on the server) and you almost never need that. We did it for 15 years on .NET without it and our services did fine. – usr Dec 14 '15 at 22:12

1 Answers1

0

Change your code like this;

Your main method;

  if (args.Length == 2 && args[0] == "-testasync")
  {
       CallWebService(args[1]);
  }

Then your web service call;

   private static async void CallWebService(string url)
   {
          try
          {
              using (MailSorterServiceClient client = new MailSorterServiceClient())
               {
                   var result = await client.GetUrlAsync(url);

                   Console.WriteLine(result.ToString());
               }
           }
           catch (Exception ex)
           {
               Console.WriteLine(ex.InnerException.Message);
               Console.WriteLine(ex.InnerException.StackTrace);
           }
     }
Kosala W
  • 2,133
  • 1
  • 15
  • 20
  • Hmm interesting. I would not expect that to compile given CallWebService() is being called in a synchronous context. Regardless, the outcome was the same (exception). – Kris Oye Dec 12 '15 at 01:36