2

I have a single synchronous operation that could take a lot of time to complete. The caller of the operation provides a CancellationToken and the operation should be stopped immediately when the token is cancelled (within a few ms after cancellation would also work in this case).

How can I wrap this in a task with a CancellationToken?

I can't change the calling code nor the call itself.

What it used to be: LongOperation();

What I have now: await Task.Run(() => LongOperation(), cancellationToken).ConfigureAwait(false);

Clearly this doesn't work as you have to poll the token inside the action given to Task.Run.

Sam Debruyn
  • 928
  • 1
  • 8
  • 22
  • What have you tried? https://msdn.microsoft.com/en-us/library/dd537607(v=vs.110).aspx – tym32167 Oct 07 '16 at 13:52
  • 2
    As you've spotted, you have to do this within the code you say you can't modify. Just arbitrarily aborting execution without that code being aware of it could easily lead to some resource being left in an invalid/corrupt state. See [this related question](http://stackoverflow.com/questions/4359910/is-it-possible-to-abort-a-task-like-aborting-a-thread-thread-abort-method). – Charles Mager Oct 07 '16 at 13:52
  • You cannot do it. Your sync method needs to accept the CancallationToken and u need periodically check if u should interrupt the execution on the signal of the token. If u cannot change the sync method async/await will NOT help you. You can create a new Thread execute the thread and on demand use Abort. http://stackoverflow.com/questions/2251964/c-sharp-thread-termination-and-thread-abort – Vojta Oct 07 '16 at 14:00

1 Answers1

8

I can't change the calling code nor the call itself.

Clearly this doesn't work as you have to poll the token inside the action given to Task.Run.

By far the easiest solution is to lift one of the requirements: either allow the CancellationToken to be ignored, or change the called code.

If that's really, truly, honestly not possible, then you'll need to run the code in another process. So, you'll need to kick off a child process that has access to that method, marshal all the arguments over to it, and then marshal back any result value or exception. Then, when the token is cancelled, kill the process.

There are less safe ways of doing the same thing: you can run the code in another AppDomain and shutdown the AppDomain on cancel, or you can run the code in another Thread and Abort the Thread on cancel. But both of those can easily cause resource leaks or application stability problems. The only truly safe way is a separate process.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • 1
    To the OP, don't be intimidated by starting and sending arguments to a separate process either. I had to something similar and [WCF over named pipes](http://stackoverflow.com/questions/7353670/wcf-named-pipe-minimal-example) made the process relatively painless. – Scott Chamberlain Oct 07 '16 at 14:06
  • @ScottChamberlain: Good point! The last time I had to do this WCF didn't exist yet (neither did .NET), so it was straight-up stdin/stdout/stderr redirection - with multiple threads to prevent deadlocks. Ugly! – Stephen Cleary Oct 07 '16 at 15:52
  • Also probably not the best idea to terminate a process during a long running synchronous method call. I smell memory leaks. – Botonomous Oct 07 '16 at 19:29
  • @Botonomous: That's not possible - the process is the scope of resource usage from the OS's perspective. If you terminate a process, all its memory will be reclaimed. – Stephen Cleary Oct 07 '16 at 19:42
  • @StephenCleary Will terminating a process ensure that all resources (started from within the new process) are actually disposed? – Botonomous Oct 07 '16 at 19:45
  • @Botonomous The OS frees all resources owned by the process. No "dispose" methods or finalizers are called. – Stephen Cleary Oct 07 '16 at 19:48