Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 
catalin
Partner - Contributor III
Partner - Contributor III

Cancellation Token - Awaitable functions .NET SDK

Hello!

How do you control the timeout of an awaitable function, lets say:

Dim fi = Await app.GetFieldAsync(field_name)
Await fi.SelectAsync(value, True)

 

What I would like to achieve is to define a cancellation token and use it to cancel the task after a timeout, catch the exception and retry the same task with a wait period in between. 

 

Dim cts As CancellationTokenSource = New CancellationTokenSource
cts.CancelAfter(TimeSpan.FromSeconds(100))

 

The reason I am asking this is that not rarely I see the application hangs out trying to make those 2 operations. Just hangs indefinitely and never return something.

Is there anyway to achieve a retry policy in case of timeout with .NET SDK's await-able functions?

Thank you very much!

Catalin 

Labels (6)
1 Solution

Accepted Solutions
Øystein_Kolsrud
Employee
Employee

There's no  built in support for this I'm afraid, but you could write a function like this (sorry for the C#, I'm not too fluent in VB):

private static async Task<T> Cancellable<T>(Task<T> task, CancellationToken token)
{
    var cancelTask = Task.Delay(TimeSpan.MaxValue, token);
    await Task.WhenAny(task, cancelTask);
    if (cancelTask.IsCanceled)
        throw new TaskCanceledException(task);
    return await task;
}

Then with that function you should be able to write your code like this:

var tcs = new CancellationTokenSource(TimeSpan.FromSeconds(100));
var token = tcs.Token;
try
{
    var fi = await Cancellable(app.GetFieldAsync(field_name), token);
    await Cancellable(fi.SelectAsync(value, true), token);
}
catch (TaskCanceledException e)
{
    Console.WriteLine("Operation was cancelled: " + e.Message);
}

 But it sounds strange that you should see it stall there in the first place. Is it reproduceable? Or is it non-deterministic?

View solution in original post

5 Replies
Øystein_Kolsrud
Employee
Employee

There's no  built in support for this I'm afraid, but you could write a function like this (sorry for the C#, I'm not too fluent in VB):

private static async Task<T> Cancellable<T>(Task<T> task, CancellationToken token)
{
    var cancelTask = Task.Delay(TimeSpan.MaxValue, token);
    await Task.WhenAny(task, cancelTask);
    if (cancelTask.IsCanceled)
        throw new TaskCanceledException(task);
    return await task;
}

Then with that function you should be able to write your code like this:

var tcs = new CancellationTokenSource(TimeSpan.FromSeconds(100));
var token = tcs.Token;
try
{
    var fi = await Cancellable(app.GetFieldAsync(field_name), token);
    await Cancellable(fi.SelectAsync(value, true), token);
}
catch (TaskCanceledException e)
{
    Console.WriteLine("Operation was cancelled: " + e.Message);
}

 But it sounds strange that you should see it stall there in the first place. Is it reproduceable? Or is it non-deterministic?

catalin
Partner - Contributor III
Partner - Contributor III
Author

Exactly this degree of randomness consistent with  a non-deterministic behavior makes my hair turn white. It's a Windows service that runs a scheduled Quartz.NET job each morning.

This service runs under a specific domain user who is also a root admin in QS. Now, this job iterates over a list of values and apply each of the list's value as a QS selection on that specific field and exports the data behind a table object as an Excel file. 

Now I may have a clue: sometimes there's someone else, another user, who logs in with the same username as the service. 

Is it possible if one opens a QS session with the same username to mess up the running job? 

I'm speculating it would mess up the outputs of either the exported Excel file or the user's selections in the QS GUI, but it would not hang the entire execution. 

Anyhow, I will wrap your nice suggestion for a cancel-able function with a retry policy and I will come back with a feedback.

Thanks a lot, @Øystein_Kolsrud!

Catalin

 

Øystein_Kolsrud
Employee
Employee

Another user could certainly affect the results of the selection if that user connects to the same engine session as your service. You should make sure to use a unique session for the service when you connect to the engine. Something like this:

using(var hub = location.Hub(Session.Random))
{
    ...
}

But depending on your connection type and proxy settings, it could be that you get disconnected if you have the same user connecting from somewhere else.

catalin
Partner - Contributor III
Partner - Contributor III
Author

So actually the Hub is the big daddy for the app object too?

Is this:

using (var app = location.App(appIdentifier, session: Session.Random))
{
...
}

equivalent to calling the hub and then get the app? Means that this calls the hub anyway in the background, right?

Thanks alot!

 

Øystein_Kolsrud
Employee
Employee

Yes, that is correct. The location.App is basically just calling location.Hub followed by hub.OpenApp.