Cancel a task that contains an infinite library method (no way to validate cancellation requests)

I am using Pcap.Net to monitor traffic and I need it to receive packets until the user asks for cancellation. I create a monitoring task this way (simplified):

var task1 = Task.Run(() => { communicator.ReceivePackets(0, PacketHandlerCallback); } /*, token*/);

      

Here 0

says execution ReceivePackets

never ends PacketHandlerCallback

- this is the method that will be executed for every packet received. ReceivePackets

is synchronous and does not support undo. Actually, in my question, it could be any other infinite synchronous method, the code of which we cannot edit.

The question is, how do I stop this method from executing?

  • Simply passing the cancellation token to the task doesn't help because we also have to explicitly check if cancellation is required, for example. d. calling token.throwIfCancellationRequested()

    .

  • Passing the token to the callback method is also not a solution, because this method will not be called until a new packet is received, but I want to stop my task immediately after canceling.

  • Usage BackgroundWorker

    raises the same question as we have to check CancellationPending

    .

  • Creating task2

    that periodically checks for cancellation requests and then writing var task = Task.WhenAny(task1, task2)

    does not help as ReceivePackets

    it will still execute .

Should I use Thread.Abort()

or maybe there is another elegant solution?
There are similar questions about TPL on SO, but I cannot find a simple and useful answer.

+3


source to share


3 answers


Usually Thread.Abort

really shouldn't be used as a very outdated and dangerous method. However, your situation looks like you need to kill the thread / process in order to stop the infinite method.

I suggest that you avoid interrupting any of your threads as this can lead to an unstable system as an intermittent thread will not be able to clean up resources properly. You can run your method in a new oneAppDomain

and unload that domain in case of a cancellation request. Also, as Scott pointed out, a standalone option is Process

also a solution.



If and only if, so for some reason this is not an option for you, you can sign up to cancel your token with helpThread.CurrentThread.Abort

, but if I were you, I would avoid that option as much as I could.

You can also create a task from a cancellation token and use it WhenAll

to wait for cancellation asynchronously.

+1


source


In situations where you cannot undo a much better solution than Thread.Abort, you need to put the unrecoverable code in a separate process that you can kill.

This gives you a guaranteed release of all resources that were associated with the thread as the OS will release any unmanaged OS resources such as handles when the process exits, you won't get this behavior if you interrupt the thread or use a separate AppDomain you terminate.



When you have your 2nd process written, you can use something like WCF over named pipes so that you can interact with the external process like any other normal function that is inside your process.

+1


source


There is a PacketCommunicator.Break()

method.

From the documentation code:

/// <summary>
/// Set a flag that will force ReceiveSomePackets(), ReceivePackets() or ReceiveStatistics() to return rather than looping.
/// They will return the number of packets/statistics that have been processed so far, with return value BreakLoop.
/// <seealso cref="ReceiveSomePackets"/>
/// <seealso cref="ReceivePackets"/>
/// <seealso cref="ReceiveStatistics(int, HandleStatistics)"/>
/// </summary>
/// <remarks>
///   <list type="bullet">
///     <item>This routine is safe to use inside a signal handler on UNIX or a console control handler on Windows, as it merely sets a flag that is checked within the loop.</item>
///     <item>The flag is checked in loops reading packets from the OS - a signal by itself will not necessarily terminate those loops - as well as in loops processing a set of packets/statistics returned by the OS.</item>
///     <item>Note that if you are catching signals on UNIX systems that support restarting system calls after a signal, and calling Break() in the signal handler, you must specify, when catching those signals, that system calls should NOT be restarted by that signal. Otherwise, if the signal interrupted a call reading packets in a live capture, when your signal handler returns after calling Break(), the call will be restarted, and the loop will not terminate until more packets arrive and the call completes.</item>
///     <item>ReceivePacket() will, on some platforms, loop reading packets from the OS; that loop will not necessarily be terminated by a signal, so Break() should be used to terminate packet processing even if ReceivePacket() is being used.</item>
///     <item>Break() does not guarantee that no further packets/statistics will be processed by ReceiveSomePackets(), ReceivePackets() or ReceiveStatistics() after it is called; at most one more packet might be processed.</item>
///     <item>If BreakLoop is returned from ReceiveSomePackets(), ReceivePackets() or ReceiveStatistics(), the flag is cleared, so a subsequent call will resume reading packets. If a different return value is returned, the flag is not cleared, so a subsequent call will return BreakLoop and clear the flag.</item>
///   </list>
/// </remarks>

      

+1


source







All Articles