Souvik Ghosh
Souvik Ghosh

Reputation: 4616

Task unable to timeout

I have a implemented a simple task using TPL. It waits for 10 seconds to execute and returns true/false.

var checkCFOPTask = Task.Run(() => CheckCFOPExists());
checkCFOPTask.Wait(TimeSpan.FromSeconds(10));
if (checkCFOPTask.Result)
{

}
else
{

}

The problem is my code is stuck within the if statement.

if (checkCFOPTask.Result)

Each time I pause the debugger it still keeps waiting on the above line of code. This happened for the first time. Ideally it should return true/false within 10 seconds.

Below are the function definitions-

CheckCFOExists: Executed by the task.

private bool CheckCFOPExists()
{
    bool found = false;

    try
    {
        while (!found)
        {
            try
            {
                if (ieDriver.FindElement(By.Id("popup_message")).Text == "Não existem itens para realizar o rateio.")
                {
                    ResetInvoiceSearchScreen();
                    break;
                }
            }
            catch (Exception ex)
            {

            }

            try
            {
                if (arrCFOPList.Contains(ieDriver.FindElement(By.Id("vendorNF.cfopOperCode")).GetAttribute("value")))
                {
                    found = true;
                }
            }
            catch (Exception ex)
            {

            }
        }
    }
    catch (Exception ex)
    {

    }
    return found;
}

ResetInvoiceSearchScreen: Executed within the above function

private void ResetInvoiceSearchScreen()
{
    try
    {
        ieDriver.FindElement(By.Id("popup_ok")).Click();
        ieDriver.FindElement(By.Id("ltmCnpjCpf")).Clear();
        ieDriver.FindElement(By.Id("notaFiscalNbr")).Clear();
        ieDriver.FindElement(By.Id("inbNotaFiscalId")).Clear();
        ieDriver.FindElement(By.Id("seriesFrmCd")).Clear();
    }
    catch (Exception ex)
    {

    }
}

Is there something else that is needed ensure the function times out correctly? Please let me know if I can provide some more details.

Edit

I see the below message for checkCFOPTask.Result in the immediate window of Visual Studio-

Id = Cannot evaluate expression because the code of the current method is optimized., Status = Cannot evaluate expression because the code of the current method is optimized., Method = Cannot evaluate expression because the code of the current method is optimized., Result = Cannot evaluate expression because the code of the current method is optimized.

Upvotes: 3

Views: 142

Answers (2)

Matthew Watson
Matthew Watson

Reputation: 109712

It looks like you will need to add time-out support to the method that you're calling - because if it doesn't find the thing it's looking for it will loop forever.

The easiest way to do this is to pass a CancellationToken to the method. You should also factor out the testing code into a separate method that returns bool.

Also note that you have a busy loop, which is generally not a good idea when polling! It's better to introduce a small sleep when polling if the thing you're polling for isn't available. (Note: Polling in general is not a good approach if you have a better way of checking for something, but it doesn't look like there's anything else you can use here, so polling will have to do.)

You can write your method like so (I've omitted the code that polls for the thing you're looking for in order to concentrate on the other logic):

private bool CheckCFOPExists(CancellationToken cancel)
{
    TimeSpan retryDelay = TimeSpan.FromMilliseconds(500);

    while (true)
    {
        if (tryToFindTheThing()) // Blocking call.
            return true;

        if (cancel.WaitHandle.WaitOne(retryDelay))
            return false;
    }
}

bool tryToFindTheThing()
{
    return false;  // Your implementation goes here.
}

Then to call this and have a 10-second timeout you would do something like this (compilable console app):

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp3
{
    class Program
    {
        static void Main()
        {
            var test = new Program();

            // Create a cancellation token source that cancels itself after 10 seconds:
            var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(10));

            // Create and run the task:

            var sw = Stopwatch.StartNew();
            var checkCFOPTask = Task.Run(() => test.CheckCFOPExists(cancellation.Token));

            Console.WriteLine("Waiting for task to finish.");
            Console.WriteLine($"Task returned: {checkCFOPTask.Result} after {sw.ElapsedMilliseconds} ms");
        }

        private bool CheckCFOPExists(CancellationToken cancel)
        {
            TimeSpan retryDelay = TimeSpan.FromMilliseconds(500);

            while (true)
            {
                if (tryToFindTheThing()) // Blocking call.
                    return true;

                if (cancel.WaitHandle.WaitOne(retryDelay))
                    return false;
            }
        }

        bool tryToFindTheThing()
        {
            return false;  // Your implementation goes here.
        }
    }
}

Upvotes: 1

Chrille
Chrille

Reputation: 1453

Before using the Result you need to check if your task actually is completed with Task.IsCompleted.

if (checkCFOPTask.IsCompleted && checkCFOPTask.Result)

Upvotes: 0

Related Questions