1

I have Windows Store MonoGame (based on XAML MonoGame template in Visual Studio 2012) app.
When I connect to LiveConnect, system does all things in background, but when I call LiveConnectClient.GetAsync to get user info it sometimes (and usually) blocks the caller thread, even though it is called using await. Is there any way to make GetAsync call really async? Maybe I should create a new thread to call it?

Here's the caller code. It is called inside MonoGame draw thread (can't access main UI thread in MonoGame).

private static LiveConnectSession session = null;
private static LiveAuthClient liveAuthClient = null;
private static LiveConnectClient liveConnectClient = null;

public static async Task AuthAsync()
{
    liveAuthClient = new LiveAuthClient();
    LiveLoginResult liveLoginResult = await liveAuthClient.InitializeAsync();
    liveLoginResult = await liveAuthClient.LoginAsync(new List<string> { "wl.signin" });
    if (liveLoginResult.Status == LiveConnectSessionStatus.Connected)
    {
        session = liveLoginResult.Session;
        liveConnectClient = new LiveConnectClient(session);
        LiveOperationResult liveOperationResult = await liveConnectClient.GetAsync("me");
        dynamic meResult = liveOperationResult.Result;
        MyEngine.userID = meResult.id;
    }
}
Tertium
  • 6,049
  • 3
  • 30
  • 51
  • You'll have to give use the method which calls `AuthAsync`. If you are calling `AuthAsync.Wait()`, that'll do it. – Nate Diamond Mar 13 '14 at 17:04
  • I call it without Wait() or await (I know I can loose exceptions, but don't care in this case). So I just fire and continue thread execution. My stack (non-main thread): Monogame's void Game.Update-> void MyEngine.Update -> async void CurrentGameLevel.Update-> async Task AuthAsync. If I comment "await liveConnectClient.GetAsync" - everything works smoothly, otherwise game stops for up to 10 seconds - just like I'd use sync call. – Tertium Mar 13 '14 at 17:31
  • 1
    According to [this](http://stackoverflow.com/questions/13556701/connectasync-blocking-ui-thread) you may want to try wrapping it in `Task.Run`. I think what he's doing there is basically making `AuthAsync` into an `async void` method, or just forcibly moving it to another thread. The other option is to do something similar, but using a `TaskCompletionSource` to return the `Task` in `AuthAsync` and having the called `async` method set the result of the `Task`. – Nate Diamond Mar 13 '14 at 17:53
  • Tried Task.Run, but it looks like LiveConnect can't login in such bg thread: throws InnerException {"Unsupported. (Exception from HRESULT: 0x80070032)"} System.Exception. In WP8 I launch similar LiveConnect code in main thread using Deployment.Current.Dispatcher.BeginInvoke and there is no option: only main thread. And the same freeze in spite of all these awaits. – Tertium Mar 13 '14 at 19:12
  • 2
    I think I have an idea what is happening. I think it is checking to see if the user is logged in with a Microsoft Account because if they are not it needs to throw up a login dialog. This may be why it needs the UI Thread. – Nate Diamond Mar 13 '14 at 19:25
  • @NateDiamond Thank you for your help, I think I've found the solution - see answer below. – Tertium Mar 13 '14 at 20:04

2 Answers2

2

Thanks to Nate Diamond, I've found a workaround (or maybe it's the only solution). The trick is to await intialization and connect in main thread (in windows store app it's not the ui thread, but somehow it's the main one), then create thread and await GetAsync in it. For the sake of clarity I've skipped all try..catch..finally and everything unnecessary. Now it let draw thread work w/o freezes. Here' the code:

private static LiveConnectSession session = null;
private static LiveAuthClient liveAuthClient = null;
private static LiveConnectClient liveConnectClient = null;

public static async Task AuthAsync()
{
    await AuthAsyncInternal();
    if (liveConnectClient != null)
    {
        await Task.Run(async () =>
            {
                LiveOperationResult liveOperationResult = 
                    await liveConnectClient.("me");
                dynamic meResult = liveOperationResult.Result;
                MyEngine.userID = meResult.id;
            });
    }
}

private static async Task AuthAsyncInternal()
{
    liveAuthClient = new LiveAuthClient();
    LiveLoginResult liveLoginResult = await liveAuthClient.InitializeAsync();
    liveLoginResult = await liveAuthClient.LoginAsync(new List<string> { "wl.signin" });
    if (liveLoginResult.Status == LiveConnectSessionStatus.Connected)
    {
        session = liveLoginResult.Session;
        liveConnectClient = new LiveConnectClient(session);
    }
}

And here's variant for Windows Phone 8:

private static async Task AuthAsyncInternal()
{
    Deployment.Current.Dispatcher.BeginInvoke(async delegate()
        {
            liveAuthClient = new LiveAuthClient("your client id here");
            LiveLoginResult liveLoginResult = await liveAuthClient.InitializeAsync();
            liveLoginResult = await liveAuthClient.LoginAsync(new List<string> { "wl.signin" });
            if (liveLoginResult.Status == LiveConnectSessionStatus.Connected)
            {
                session = liveLoginResult.Session;
                liveConnectClient = new LiveConnectClient(session);
                await Task.Run(async () =>
                    {
                        LiveOperationResult liveOperationResult = 
                            await liveConnectClient.("me");
                        dynamic meResult = liveOperationResult.Result;
                        MyEngine.userID = meResult.id;
                    });
            }
        });
}
Tertium
  • 6,049
  • 3
  • 30
  • 51
0

Bit off topic (not about threading) but maybe helpful to someone.

I've found that a call to LiveAuthClient's LoginAsync() was taking up to 12 seconds, and usually around 7-8 secs (in a win 8.1 store app in c# & XAML)

e.g.

LiveAuthClient auth = new LiveAuthClient(_redirectDomain);
_loginResult = await auth.LoginAsync(new string[] { "wl.signin", "wl.basic", "wl.emails" });

Found that once I closed Fiddler (an http proxy/inspector) this dropped to a much more reasonable ~2 seconds

This is still pretty slow and lame (no idea why its so slow - note : I think upgrading from 5.5 to 5.6 slowed it down) but obviously much better.

Might help someone.

MemeDeveloper
  • 6,457
  • 2
  • 42
  • 58