async Task<TResult> CancelAfterAsync<TResult>(Func<CancellationToken, Task<TResult>> startTask, TimeSpan timeout, CancellationToken cancellationToken)
{
using (var timeoutCancellation = new CancellationTokenSource())
using (var combinedCancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCancellation.Token))
{
var originalTask = startTask(combinedCancellation.Token);
var delayTask = Task.Delay(timeout, timeoutCancellation.Token);
var completedTask = await Task.WhenAny(originalTask, delayTask);
// Cancel timeout to stop either task:
// - Either the original task completed, so we need to cancel the delay task.
// - Or the timeout expired, so we need to cancel the original task.
// Canceling will not affect a task, that is already completed.
timeoutCancellation.Cancel();
if (completedTask == originalTask)
{
// original task completed
return await originalTask;
}
else
{
// timeout
throw new TimeoutException();
}
}
}
Asynchronously wait for Task<T> to complete with timeout
I found this async method here at stackoverflow and I created an extension method of this method:
public static async Task<TResult> CancelAfterAsync<TResult>(this Func<CancellationToken, Task<TResult>> startTask, TimeSpan timeout, CancellationToken cancellationToken)
{
using (var timeoutCancellation = new CancellationTokenSource())
using (var combinedCancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCancellation.Token))
{
var originalTask = startTask(combinedCancellation.Token);
var delayTask = Task.Delay(timeout, timeoutCancellation.Token);
var completedTask = await Task.WhenAny(originalTask, delayTask);
// Cancel timeout to stop either task:
// - Either the original task completed, so we need to cancel the delay task.
// - Or the timeout expired, so we need to cancel the original task.
// Canceling will not affect a task, that is already completed.
timeoutCancellation.Cancel();
if (completedTask == originalTask)
{
// original task completed
return await originalTask;
}
else
{
// timeout
throw new TimeoutException();
}
}
}
But I don't know how to call this type of extension method in another class.
What is the first parameter CancellationToken and the last parameter cancellationToken in the async method?
I want to create a task of the following async method UpdatePlayerCountryData and use it with the extension method to find out if UpdatePlayerCountryData completes in 5 seconds and if not then throw new TimeoutException();. I get an error message because I don't know what the two missing arguments are:
Error CS0839: Argument missing
var test = await Extensionmethods.CancelAfterAsync( , UpdatePlayerCountryData("Germany", "Berlin"), new TimeSpan(0, 0, 0, 5, 0), );
How can I use UpdatePlayerCountryData with the extension method? How can I call CancelAfterAsync from another class?
private async Task UpdatePlayerCountryData(string country, string city)
{
var resultprofile = await PlayFabClientAPI.UpdateUserDataAsync(new PlayFab.ClientModels.UpdateUserDataRequest()
{
Data = new Dictionary<string, string>() {
{"Country", country},
{"City", city}
},
Permission = PlayFab.ClientModels.UserDataPermission.Public
});
if (resultprofile.Error != null)
Console.WriteLine(resultprofile.Error.GenerateErrorReport());
else
{
Console.WriteLine("Successfully updated user data");
}
}
UPDATE: I changed my code. Will the method UpdatePlayerCountryData be canceled with token.IsCancellationRequested after 10 seconds(TimeSpan.FromSeconds(10)) or will cancelling not work when await PlayFabClientAPI.UpdateUserDataAsync takes longer than 10 seconds?
Will (token.IsCancellationRequested) only be executed after await PlayFabClientAPI.UpdateUserDataAsync finished, even if it would take minutes?
if (token.IsCancellationRequested)
{
return;
}
Complete code:
public async Task PlayerAccountDetails()
{
TimeSpan timeout = TimeSpan.FromSeconds(10);
CancellationTokenSource cts = new CancellationTokenSource();
try
{
await UpdatePlayerCountryData("Country", "City", cts.Token).CancelAfter(timeout, cts.Token);
}
catch (Exception ex)
{
//catch exception here for logging
}
}
public static async Task CancelAfter(this Task @task, TimeSpan timeout, CancellationToken token)
{
var timeoutTask = Task.Delay(timeout, token);
var originalTask = @task;
var completedTask = await Task.WhenAny(timeoutTask, originalTask);
if (completedTask == timeoutTask)
throw new TimeoutException();
await completedTask;
}
private static async Task UpdatePlayerCountryData(string country, string city, CancellationToken token)
{
var resultprofile = await PlayFabClientAPI.UpdateUserDataAsync(new PlayFab.ClientModels.UpdateUserDataRequest()
{
Data = new Dictionary<string, string>() {
{"Country", country},
{"City", city}
},
Permission = PlayFab.ClientModels.UserDataPermission.Public
});
if (token.IsCancellationRequested)
{
return;
}
if (resultprofile.Error != null)
Console.WriteLine(resultprofile.Error.GenerateErrorReport());
else
{
Console.WriteLine("Successfully updated user data");
}
}