3

I have this piece of code in my Windows Store app where I handle a button click. But somehow, UI freezes when I click this button on rare occasion. It mostly happens when I connect to a Wi-Fi network further away than the one I usually connect to. Taking into consideration that I download a RSS feed from the internet, it is most probably related to my using of async/await keywords. If I correctly understood the mechanics of async/await, UI thread should not block at all. Am I missing something here?

    private async void Add_Click(object sender, RoutedEventArgs e)
    {
        AddButton.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
        DownloadProgressRing.Visibility = Windows.UI.Xaml.Visibility.Visible;
        DownloadProgressRing.IsActive = true;

        RssData rssData = App.Current.Resources["rssData"] as RssData;
        Uri uri = FixUrl(RssUrl.Text);

        if (uri != null)
        {
            newRssFeed = new RssFeed(uri.ToString());
            Task<bool> success = newRssFeed.RetrieveFeed();
            if (await success)
            {
                SubmitButtonPanel.Visibility = Windows.UI.Xaml.Visibility.Visible;
                FeedTitleTextBox.Text = newRssFeed.Title == null ? String.Empty : newRssFeed.Title;
            }
        }
    }

Constructor for RssFeed:

    public RssFeed(string url)
    {
        Url = url; 
    }

RetrieveFeed function:

    public async Task<bool> RetrieveFeed()
    {
        if (Url != null)
        {
            try
            {
                SyndicationClient sc = new SyndicationClient();
                SyndicationFeed sf = await sc.RetrieveFeedAsync(new Uri(Url, UriKind.Absolute));
                if (sf != null)
                {
                    ValidateAndInitFeed(sf);
                    return true;
                }
            }
            catch (Exception ex)
            {
            }
        }

        return false;
    }

    private void ValidateAndInitFeed(SyndicationFeed feed)
    {
        if (feed.Title != null)
            Title = feed.Title.Text;
        if (feed.ImageUri != null)
            Image = feed.ImageUri.ToString();
        if (feed.Subtitle != null)
            Subtitle = feed.Subtitle.Text;
    }
mostruash
  • 4,169
  • 1
  • 23
  • 40
  • Are you sure `newRssFeed = new RssFeed(uri.ToString());` doesn't do anything blocking? – I4V Jan 25 '13 at 17:09
  • @I4V I have included more code. I am having hard time reproducing the problem; but it happens, very rarely. There is no problem when I have no internet connection. It might not be related to it at all. – mostruash Jan 25 '13 at 17:18
  • 1
    This is unrelated to your issue, but you should never just catch and ignore `Exception`. Only catch the exceptions that you know you want to handle. – svick Jan 25 '13 at 19:10
  • Also, are you sure it's not another part of your code that's actually blocking the UI? – svick Jan 25 '13 at 19:11
  • 2
    Suspect that the problem is occurring in SyndicationClient.RetrieveFeed and is probably related to the unfortunate design decision that means the DNS phase of an async HttpWebRequest happens synchronously, blocking prior to asychronously making the request proper. I've posted about this here:http://stackoverflow.com/questions/11480742/dns-begingethost-methods-blocking – spender Jan 28 '13 at 03:07

3 Answers3

2

Try calling RetrieveFeed() on a background thread. Perhaps processing the request or response is what takes a lot of time and if it is started on UI thread - it will run on UI thread.

Task<bool> success = null;
await Task.Run(
    () => success = newRssFeed.RetrieveFeed());
Filip Skakun
  • 31,624
  • 6
  • 74
  • 100
  • 1
    This code does not compile as it is. Maybe your intention was to call Dispatcher.RunAsync inside a lambda action? – mostruash Jan 25 '13 at 17:40
  • Oh yeah, sorry, I put the CoreDispatcherPriority part there unnecessarily. – Filip Skakun Jan 25 '13 at 19:22
  • 2
    BTW, the same code could be written better as `bool success = await Task.Run(() => newRssFeed.RetrieveFeed());`. – svick Jan 25 '13 at 19:57
  • This definitely solves the whole problem. As pointed out by spender (http://stackoverflow.com/questions/14526761/await-blocks-the-ui-thread#comment20305767_14526761), it probably is a problem with DNS look-ups being sync calls. – mostruash Feb 01 '13 at 02:22
0

The use of the async/await look okay. You reference a distant Wi-Fi connection, this may be purely down to a weak signal, hence poor bandwidth.

I hope this helps.

MoonKnight
  • 23,214
  • 40
  • 145
  • 277
  • 3
    But UI should not freeze even if I have a poor connection. Actually I started to consider if it is something to do with the VS2012 debugger. – mostruash Jan 25 '13 at 17:08
  • The operation `newRssFeed.RetrieveFeed();` is being placed on a background thread, the way you have done this looks right (at least I can see no errors in your code). If the connection is poor or intermittent this could be your problem. Follow the code into this method and see what is happening... – MoonKnight Jan 25 '13 at 17:11
  • 1
    The debugger does slow an app down especially if the app outputs trace information to the debugger or otherwise throws exceptions (even ones that are handled). – Filip Skakun Jan 25 '13 at 17:27
  • @mostruash Well, does the problem happen when you run without debugger? – svick Jan 25 '13 at 19:08
0

I've the same issue on some of my Windows Phone apps and I don't have any good explanation, the only good one is the same that Killercam referred.

When you call an async method it doesn't return immediately (I assume that the methods aren't something like return Task.Factory.StartNew(...);), it does some work, sets the callback and then returns, if the work that it makes has anything related to network connections, in a slow connection this can block the function.

A good way to test this symptoms is connect to a router with wifi and then disconnect the router from the internet, you will have the same behavior that you are describing.

DVD
  • 1,744
  • 3
  • 17
  • 34