/// <summary> /// Read the contents of a file into a byte array, with the specified timeout and CancellationTokenSource /// </summary> /// <param name="fileName"></param> /// <param name="timeoutSeconds"></param> /// <param name="tokenSource"></param> /// <returns></returns> public static async Task <perTaskResult <byte[]> > ReadAllBytesFromFileAsync( string fileName, int timeoutSeconds, CancellationTokenSource tokenSource) { using (var file = OpenFileForAsyncReading(fileName)) { var bytes = new byte[file.Length]; var response = await file.ReadAsync(bytes, 0, (int)file.Length, tokenSource.Token) .RunTaskWithTimeoutAsync(timeoutSeconds, tokenSource).ConfigureAwait(false); var result = new perTaskResult <byte[]> { Status = response.Status, ErrorMessage = response.ErrorMessage, Exception = response.Exception }; if (response.Status == perTaskStatus.CompletedOk) { result.Data = bytes; } return(result); } }
/// <summary> /// run a Task T with the specified timeout / cancellation token and return the result /// </summary> public static async Task <perTaskResult <T> > GetTaskResultWithTimeoutAsync <T>(this Task <T> theTask, int timeoutSeconds, CancellationTokenSource tokenSource) { var taskResult = await theTask.RunTaskWithTimeoutAsync(timeoutSeconds, tokenSource).ConfigureAwait(false); var result = new perTaskResult <T> { Status = taskResult.Status, Exception = taskResult.Exception, ErrorMessage = taskResult.ErrorMessage }; // the task has already completed if status is CompletedOk, but using await once more is better than using theTask.Result if (result.Status == perTaskStatus.CompletedOk) { result.Data = await theTask.ConfigureAwait(false); } return(result); }
/// <summary> /// run a task with the specified timeout and cancellation token /// </summary> /// <remarks> /// tokenSource Parameter is CancellationTokenSource rather than CancellationToken as we want to trigger it in order to cancel /// theTask (assuming it's using the same CancellationTokenSource or its token) in the event of a timeout. /// </remarks> public static async Task <perTaskResult> RunTaskWithTimeoutAsync(this Task theTask, int timeoutSeconds, CancellationTokenSource tokenSource) { var result = new perTaskResult(); // this will kill the timeout task, if the external cancellation token source is cancelled using (var timeoutTokenSource = CancellationTokenSource.CreateLinkedTokenSource(tokenSource.Token)) { var timeoutTask = timeoutSeconds == 0 ? Task.Delay(TimeSpan.FromMilliseconds(-1), timeoutTokenSource.Token) : Task.Delay(TimeSpan.FromSeconds(timeoutSeconds), timeoutTokenSource.Token); var completedTask = await Task.WhenAny(theTask, timeoutTask).ConfigureAwait(false); if (tokenSource.IsCancellationRequested) { result.Status = perTaskStatus.Cancelled; } else if (completedTask == timeoutTask) { result.Status = perTaskStatus.TimedOut; // signal theTask to cancel if possible tokenSource.Cancel(); } else if (theTask.IsFaulted) { result.Status = perTaskStatus.Error; result.Exception = theTask.Exception; result.ErrorMessage = theTask.Exception?.GetText(); } else { result.Status = perTaskStatus.CompletedOk; } // kill the timeoutTask if it's not already cancelled timeoutTokenSource.Cancel(); } return(result); }