I'm using System.IO.File.Copy to copy a file from a remote share to my local system. How can I implement a timeout if the copy takes too long?
-
Maybe better than a timeout is to work asynchronously: [Asynchronous File Copy/Move in C#](http://stackoverflow.com/questions/882686/asynchronous-file-copy-move-in-c-sharp) – Blas Soriano Jun 06 '15 at 10:47
-
1https://msdn.microsoft.com/en-us/magazine/cc163851.aspx – Hans Passant Jun 06 '15 at 11:40
2 Answers
For example, it can be done this way using async
-await
pattern:
Task timeoutTask = Task.Delay(TimeSpan.FromSeconds(10));
// I use a completion source to set File.Copy thread from its own
// thread, and use it later to abort it if needed
TaskCompletionSource<Thread> copyThreadCompletionSource = new TaskCompletionSource<Thread>();
// This will await while any of both given tasks end.
await Task.WhenAny
(
timeoutTask,
Task.Factory.StartNew
(
() =>
{
// This will let main thread access this thread and force a Thread.Abort
// if the operation must be canceled due to a timeout
copyThreadCompletionSource.SetResult(Thread.CurrentThread);
File.Copy(@"C:\x.txt", @"C:\y.txt");
}
)
);
// Since timeoutTask was completed before wrapped File.Copy task you can
// consider that the operation timed out
if (timeoutTask.Status == TaskStatus.RanToCompletion)
{
// Timed out!
Thread copyThread = await copyThreadCompletionSource.Task;
copyThread.Abort();
}
You might encapsulate this to re-use it whenever you want:
public static class Timeout
{
public static async Task<bool> ForAsync(Action operationWithTimeout, TimeSpan maxTime)
{
Contract.Requires(operationWithTimeout != null);
Task timeoutTask = Task.Delay(maxTime);
TaskCompletionSource<Thread> copyThreadCompletionSource = new TaskCompletionSource<Thread>();
// This will await while any of both given tasks end.
await Task.WhenAny
(
timeoutTask,
Task.Factory.StartNew
(
() =>
{
// This will let main thread access this thread and force a Thread.Abort
// if the operation must be canceled due to a timeout
copyThreadCompletionSource.SetResult(Thread.CurrentThread);
operationWithTimeout();
}
)
);
// Since timeoutTask was completed before wrapped File.Copy task you can
// consider that the operation timed out
if (timeoutTask.Status == TaskStatus.RanToCompletion)
{
// Timed out!
Thread copyThread = await copyThreadCompletionSource.Task;
copyThread.Abort();
return false;
}
else
{
return true;
}
}
}
Somewhere in your project you might call the above method this way:
bool success = await Timeout.ForAsync(() => File.Copy(...), TimeSpan.FromSeconds(10));
if(success)
{
// Do stuff if File.Copy didn't time out!
}
Note I've used Thread.Abort()
instead of using CancellationToken
. In your use case you need to call a synchronous method for which you can't use the so-called cancellation pattern, and I believe this can be one of the few cases where Thread.Abort()
could be a valid option.
At the end of the day, if there's a timeout, the code will abort the thread executing the File.Copy
, thus, it should be enough to stop the I/O operation.

- 63,804
- 18
- 124
- 206
-
Thanks for the answer. Now I have a followup question: if the timeout occurs before the File.Copy is complete, can I cancel the File.Copy task, and if so, how? – JeffR Jun 07 '15 at 21:08
-
@JeffR I'll answer you during today or tomorrow... BTW, you can speed up the answer yourself looking at the following article and you should be able to modify my sample to fit your needs: https://msdn.microsoft.com/en-us/library/dd997396%28v=vs.110%29.aspx – Matías Fidemraizer Jun 08 '15 at 05:33
-
Thanks. I saw that, but I don't understand how to interrupt the File.Copy - how can File.Copy throw the exception when requested? – JeffR Jun 08 '15 at 09:25
-
@JeffR You can't interrupt the File.Copy at all... Either today evening I'll give you some hint in an answer's update... – Matías Fidemraizer Jun 08 '15 at 12:22
-
@JeffR I've updated my answer. The updated approach should work in your scenario... Let me know if it worked for you :) – Matías Fidemraizer Jun 08 '15 at 19:12
You can implement a simple method something like the following, built on Stream.CopyToAsync() which accepts a cancellation token:
static async Task Copy(string destFilePath, string sourceFilePath, int timeoutSecs)
{
var cancellationSource = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSecs));
using (var dest = File.Create(destFilePath))
using (var src = File.OpenRead(sourceFilePath))
{
await src.CopyToAsync(dest, 81920, cancellationSource.Token);
}
}
As you can see, it is possible to create a CancellationTokenSource() which automatically cancels itself after the specified time.
You can use the Copy method using async:
try
{
await Copy(@"c:\temp\test2.bin", @"c:\temp\test.bin", 60);
Console.WriteLine("finished..");
}
catch (OperationCanceledException ex)
{
Console.WriteLine("cancelled..");
}
catch (Exception ex)
{
Console.WriteLine("error..");
}
or the old way:
var copyInProgress = Copy(@"c:\temp\test2.bin", @"c:\temp\test.bin", 60);
copyInProgress.ContinueWith(
_ => { Console.WriteLine("cancelled.."); },
TaskContinuationOptions.OnlyOnCanceled
);
copyInProgress.ContinueWith(
_ => { Console.WriteLine("finished.."); },
TaskContinuationOptions.OnlyOnRanToCompletion
);
copyInProgress.ContinueWith(
_ => { Console.WriteLine("failed.."); },
TaskContinuationOptions.OnlyOnFaulted
);
copyInProgress.Wait();
It is easy to improve the above code to use a second cancellation token which can be controlled by the user (via cancel button). All you need to use is CancellationTokenSource.CreateLinkedTokenSource

- 646
- 5
- 7