locked
Slow task cancellation RRS feed

  • Question

  • Running a background task on Windows 10 IoT Core (on Raspberry 3B), debug mode on remote machine

    Creating a Task by calling an async method which performs a while loop when token is not cancel requested.
    Inside loop I have await Task.Delay(delayTime, token); wrapped into a try catch and TaskCancelledException is thrown.

    All ok, almost, my questions are:
    Why is this exception thrown when cts.Cancel() is called, even though I have not called token.ThrowIfCancellationRequested(); ?
    Why is the exception thrown approximately 2000ms (!!) after the call to cts.Cancel()?

    Monday, June 3, 2019 7:32 PM

Answers

  • Hello EuroEager,

    >>>Anyway, you said you got several milliseconds from cts.Cancel() until exception is thrown, shouldn't this be immediately?

    First Windows is not a real-time operating system. Second how long this time depends on your local environment: priority of you app and how busy the CPU etc. How immediate you expected?

    As for exception thrown, there are two situations:

    • If you catch the TaskCanceledException using try-catch statement like you did, the program will execute the catch block when the exception happens. And let you handle the exception and determine to throw the exception and break the app or don't throw the exception and continue the app execution. So that's why the catch block is executed.

    • If you don't use try-catch or you throw the exception in the catch block, the app will result in unhandled exception and break.
                    catch (OperationCanceledException)
                    {
                        throw;
                    }

    Best regards,

    Rita


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • Marked as answer by EuroEager Thursday, June 6, 2019 6:46 AM
    Wednesday, June 5, 2019 6:01 AM

All replies

  • Hello EuroEager,

    • As for time issue, please try deploy the app and run instead of debugging from Visual Studio. Without Visual Studio debugger I get several milliseconds from start the cancel the task to the exception thrown in try-catch. The following is my test code demo you can check. If it don't reproduce your issue you can show your code demo.
       public sealed partial class MainPage : Page
        {
            private CancellationTokenSource cancelTokenSource;
            private Timer timer;
            private Stopwatch stopwatch;
    
            public MainPage()
            {
                this.InitializeComponent();
                stopwatch = new Stopwatch();
            }
    
            protected async override void OnNavigatedTo(NavigationEventArgs e)
            {
                cancelTokenSource = new CancellationTokenSource();
    
                timer = new Timer(CancelReadTask, null, 4000, 2000);
    
                await MyFunc();
            }
    
            private async Task MyFunc()
            {
                while (true)
                {
                    try
                    {
                        await Task.Delay(2000, cancelTokenSource.Token);
                    }
                    catch (Exception ex)
                    {
                        stopwatch.Stop();                   
                        await CoreWindow.GetForCurrentThread().Dispatcher.RunAsync( CoreDispatcherPriority.Normal,()=> {
                            time.Text = stopwatch.ElapsedMilliseconds.ToString();
                        });
    
                        System.Diagnostics.Debug.WriteLine("### TaskCanceledException");
                        break;
                    }
                }
            }
    
            private void CancelReadTask(object obj)
            {
                if (cancelTokenSource != null)
                {
                    if (!cancelTokenSource.IsCancellationRequested)
                    {
                        stopwatch.Start();                    
                        cancelTokenSource.Cancel();
                    }
                }
            }
        }
    • As for exception thrown, if my understanding is correct, you mean although you catch the exception Visual Studio debugger still throw the task cancel exception like this:

    This seems a first-chance exception, related to Visual Studio Debugger feature. It won't affect when you run the app alone without debugger. Refer to "Understanding Exceptions while debugging with Visual Studio" and "Recommended patterns for CancellationToken".

    >>>Why is this exception thrown when cts.Cancel() is called, even though I have not called 

    Can you provide a code sample and reproduce steps for exception thrown "even though I have not called"?

    Best regards,

    Rita


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, June 4, 2019 3:35 AM
  • Thanks

    I haven't tried other than wile debugging. My code is running in a background task on Windows IoT Core and it is therefore somewhat limited possibilities for annunciation of result like these, but I may send result to a desktop (udp or tcp) or write the result to file, will try later on.
    Anyway, you said you got several milliseconds from cts.Cancel() until exception is thrown, shouldn't this be immediately?

    First chance exception notification in the output window is understood, however I don't really understand why the exception is thrown when I haven't asked for it (at least I believe I have not asked for it, see code below.
    I have never called the ThrowIfCancellationRequested method on the token and therefore I do understand why the while loop is exited at cts.Cancel (code not shown here), but I don't understand why the catch block is ever executed (it is indeed executed), must be something I misunderstand about the intention of the ThrowIfCancellationRequested method, please explain.

            private async Task RunCallback(int cyclicity, CancellationToken token, IPEndPoint ipEndPoint)
            {
                while (!token.IsCancellationRequested)
                {
                    Debug.WriteLine($"{DateTime.Now:HH:mm:ss.fff} Faking callback");
                    try
                    {
                        await Task.Delay(cyclicity, token);
                    }
                    catch (TaskCanceledException)
                    {
                        Debug.WriteLine($"{DateTime.Now:HH:mm:ss.fff} Faking callback cancelled");
                    }
                }
            }
    

    Tuesday, June 4, 2019 12:38 PM
  • Hello EuroEager,

    >>>Anyway, you said you got several milliseconds from cts.Cancel() until exception is thrown, shouldn't this be immediately?

    First Windows is not a real-time operating system. Second how long this time depends on your local environment: priority of you app and how busy the CPU etc. How immediate you expected?

    As for exception thrown, there are two situations:

    • If you catch the TaskCanceledException using try-catch statement like you did, the program will execute the catch block when the exception happens. And let you handle the exception and determine to throw the exception and break the app or don't throw the exception and continue the app execution. So that's why the catch block is executed.

    • If you don't use try-catch or you throw the exception in the catch block, the app will result in unhandled exception and break.
                    catch (OperationCanceledException)
                    {
                        throw;
                    }

    Best regards,

    Rita


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • Marked as answer by EuroEager Thursday, June 6, 2019 6:46 AM
    Wednesday, June 5, 2019 6:01 AM
  • I indeed understand that Windows is not a RTOS, however my expectations for response in a system (which is actually idling at the actual point of times I am talking about) is far less than approximately 2000 ms, therefore I asked what you meant by "several milliseconds".
    What is your definition of "several" in this context?

    For the exception thrown, I know how the debugger handles exceptions and indeed how catch blocks work etc., my question is/was thus: "Why is the exception thrown even if I have not asked it to? (I would of course understand why the exception is thrown if the call to ThrowIfCancellationRequested method on the token was ever performed, but it is never called).

    Wednesday, June 5, 2019 8:15 AM
  • Hi Rita

    I just tried a release build thus running release code withoutout any (remote) debugger attached. Running on the RP3B device.

    Callback was thrown "immediately" (less than 1 ms after cts.Cancel).

    So problem solved (but I still think 2 seconds is a very long time though...)

    Thursday, June 6, 2019 6:46 AM
  • Hello EuroEager,

    Glad to hear that the issue solved.

    >>>What is your definition of "several" in this context?

    I saw 1 and 2 etc, all of them are less than 10 milliseconds (much less that 2000 ms).

    >>>my question is/was thus: "Why is the exception thrown even if I have not asked it to?

    This is what I can't reproduce. If you can show a snapshot that represent this I can do a further investigation.

    Best regards,

    Rita


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Thursday, June 6, 2019 7:00 AM
  • Try to run the following console program, first with argument false and then true, why are the outcomes identical?
    (In other words: how is the CancellationToken.ThrowIfCancellationRequested method really working?)


    using System;
    using System.Diagnostics;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            private static Stopwatch stopwatch;
            private static bool toBeThrown;
            static void Main(string[] args)
            {
                toBeThrown = bool.Parse(args[0]);
                CancellationTokenSource cts = new CancellationTokenSource();
                if (toBeThrown) cts.Token.ThrowIfCancellationRequested();
                stopwatch = Stopwatch.StartNew();
                Task waiterTask = GetWaiter(cts.Token);
                Task.Delay(1000).Wait();
                Console.WriteLine($"After {stopwatch.ElapsedMilliseconds} ms: Cancellation will now be requested");
                cts.Cancel();
                Console.ReadKey();
            }
            private async static Task GetWaiter(CancellationToken token)
            {
                try
                {
                    await Task.Delay(10000, token);
                    Console.WriteLine($"After {stopwatch.ElapsedMilliseconds} ms: Completed (without cancellation)");
                }
                catch (OperationCanceledException e)
                {
                    if (toBeThrown) Console.WriteLine($"After {stopwatch.ElapsedMilliseconds} ms: {e.GetType()} thrown and cought (and token.ThrowIfCancellationRequested() was indeed called)");
                    else Console.WriteLine($"After {stopwatch.ElapsedMilliseconds} ms: {e.GetType()} thrown and cought (although token.ThrowIfCancellationRequested() was never called)");
                }
            }
        }
    }


    EDIT: This snippet was ran on a desktop and full .Net framework 4.7.2
    • Edited by EuroEager Friday, June 7, 2019 11:06 AM
    Friday, June 7, 2019 11:04 AM