3

enter image description hereI am using SAP .NET Connector 3.0 and trying to login on using a separate thread so I can have the UI displaying a kind of login animation.

I am using Async and Await to start the login but the UI hangs for about 10 seconds during login.

Here is the code, its rough because I am quickly drafting a program.

async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    Busy.Visibility = System.Windows.Visibility.Visible; // Shows progress animation

    if (await SAPLogin()) // Waits for login to finish, will always be true at the moment
    {
        await GetData(); // does things with sap

        Busy.Visibility = System.Windows.Visibility.Collapsed; // Hides progress animation
    }
 }


private Task<bool> SAPLogin()
{
    bool LoggedIn = true;

    return Task.Run(() =>
        {
           Backend = new BackendConfig();
           RfcDestinationManager.RegisterDestinationConfiguration(Backend);
           SapRfcDestination = RfcDestinationManager.GetDestination(MyServer);  // MyServer is just a string containing sever name

           SapRap = SapRfcDestination.Repository;

           BapiMD04 = SapRap.CreateFunction("MD_STOCK_REQUIREMENTS_LIST_API");

           BapiMD04.SetValue("WERKS", "140");

                return LoggedIn;
         });
}      

I can only imagine that something in the Task is using the UI?

EDIT 1: Sorry forgot to explain what GetData() does. GetData() runs various reports in SAP (lots of code). Visually, I know when its there because my little login animation will change from "Logging In" to "Grabbing Data". When I see the UI hang I see it is during the "Logging In" phase. The login animation has a simple circle spinning. This stops part way through the login and then continues after about 5 seconds.

EDIT 2: The hanging seems to occour at this line here

SapRfcDestination = RfcDestinationManager.GetDestination(MyServer);

EDIT 3: Added a Photo of the threads when pausing application at the point where I see the UI hang.

Gaz83
  • 2,293
  • 4
  • 32
  • 57
  • 3
    What does `GetData` do? – usr Feb 14 '14 at 08:57
  • Sorry I will edit the original post so that everyone can see – Gaz83 Feb 14 '14 at 09:04
  • Comment out the whole `if` to make 100% sure it is causing this (I think it doesn't). `if (false && await SAPLogin())`. – usr Feb 14 '14 at 09:13
  • ok just I commented it out and as expected my animation shows and no hang ups. – Gaz83 Feb 14 '14 at 09:18
  • The other interesting note is that when the UI does hang, if I move the mouse into the programs window then the mouse cursor changes to the busy cursor. – Gaz83 Feb 14 '14 at 09:21
  • Pause the debugger during the hand and look at all running threads. Where do they stand? Is one of them calling into the UI thread? If necessary, enable "Show External Code". – usr Feb 14 '14 at 09:50
  • Try running your program with Ctrl-F5. Does it hang? – AgentFire Feb 14 '14 at 09:52
  • What's the threading model of your SAP library? Have you contacted them about this issue? – Stephen Cleary Feb 14 '14 at 13:17
  • @AgentFire Ctrl-F5 still hangs. – Gaz83 Feb 14 '14 at 13:29
  • @usr not sure what I am looking for so I have included a screenshot – Gaz83 Feb 14 '14 at 13:46
  • Looks like your UI thread ("Main Thread") is busy processing geometry. Could it be that your UI is just overloaded with graphics processing? Look at the stack of that thread. – usr Feb 14 '14 at 13:53
  • Well it shouldn't be? using the same animation in another application and it has no issue there. Also it I comment out the line as you mentioned before so that it doesn't do any waiting and the login process does not happen then I see no UI hanging. It only happens when it awaits – Gaz83 Feb 14 '14 at 14:03
  • Just tried the following which works so if someone wants to explain why in an answer then I will mark it. If I don't use await and instead create a thread and run the login on that then I don't see the UI hang. I thought Task.Run creates a thread from the ThreadPool? – Gaz83 Feb 14 '14 at 14:10
  • 1
    @Gaz83, are you sure nothing inside `GetData` or inside the `Task.Run` lambda is trying to callback the UI thread with `Dispatcher.Invoke`? Anyhow, try [my answer](http://stackoverflow.com/a/21791899/1768303). – noseratio Feb 15 '14 at 02:10
  • @Noseratio 100% sure, I will try your answer now and get back to you. – Gaz83 Feb 17 '14 at 09:29

1 Answers1

2

Presumably, nothing inside GetData or the Task.Run lambda inside SAPLogin is trying to callback the UI thread with Dispatcher.Invoke, Dispatcher.BeginInvoke or Dispatcher.InvokeAsync. Check for such possibility first.

Then, try changing your code like below. Note how Task.Factory.StartNew with TaskCreationOptions.LongRunning is used instead of Task.Run and how GetData is offloaded (despite it's already async, so mind .Unwrap() here). If that helps, try each change independently, to see which one particularly helped, or whether it was a combination of both.

async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    Busy.Visibility = System.Windows.Visibility.Visible; // Shows progress animation

    if (await SAPLogin()) // Waits for login to finish, will always be true at the moment
    {
        //await GetData(); // does things with sap
        await Task.Factory.StartNew(() => GetData(),
            CancellationToken.None,
            TaskCreationOptions.LongRunning,
            TaskScheduler.Default).Unwrap();

        Busy.Visibility = System.Windows.Visibility.Collapsed; // Hides progress animation
    }
}

private Task<bool> SAPLogin()
{
    bool LoggedIn = true;

    return Task.Factory.StartNew(() =>
    {
        Backend = new BackendConfig();
        RfcDestinationManager.RegisterDestinationConfiguration(Backend);
        SapRfcDestination = RfcDestinationManager.GetDestination(MyServer);  // MyServer is just a string containing sever name

        SapRap = SapRfcDestination.Repository;

        BapiMD04 = SapRap.CreateFunction("MD_STOCK_REQUIREMENTS_LIST_API");

        BapiMD04.SetValue("WERKS", "140");

        return LoggedIn;
    }, 
    CancellationToken.None,
    TaskCreationOptions.LongRunning,
    TaskScheduler.Default);
}
noseratio
  • 59,932
  • 34
  • 208
  • 486
  • I can confirm that just changing the SAPLogin() method to your version solved the issue. – Gaz83 Feb 17 '14 at 09:36
  • @Gaz83, then my explanation is, the SAP .NET Connector uses too many threads from the thread pool. So many that there's not much left for you. Bad on them, for something which really should just do async web service calls. To verify this, check `Process.GetCurrentProcess().Threads.Count` and compare it to the result of `ThreadPool.GetMaxThreads`. – noseratio Feb 17 '14 at 09:44
  • Now I am not sure if I have put this in the right place so I tried it after the line `SapRap = SapRfcDestination.Repository` and got the following results. `Process.GetCurrentProcess().Threads.Count = 21` and calling ThreadPool.GetMaxThreads sets 2 output parameters which are as follows. `WorkerThreads = 1023` and `CompletionPortThreads = 1000' – Gaz83 Feb 17 '14 at 10:11
  • @Gaz83, wow, 1023 is a LOT for a client-side app. Anyway, TPL doesn't 'eat' pool threads like crazy, unless `TaskCreationOptions.LongRunning` is used. 21 is about the number when it starts queuing tasks. That's why `LongRunning` helped. [This post](http://stackoverflow.com/a/21806109/1768303) explains what's going on in more details. Make sure you don't abuse `Task.Run`, then you can blame SAP for that, I guess. – noseratio Feb 17 '14 at 10:22
  • 1
    Thanks for the info, its very helpful and I have learnt a lot :-) I am not sure why its that high as my app is very simple. The UI only has 1 button on the screen and some text with a spinning circle animation. Other that that the task generation is as the code that I have posted. – Gaz83 Feb 17 '14 at 10:54