patterncsharpMinor
Interprocess CancellationToken
Viewed 0 times
cancellationtokeninterprocessstackoverflow
Problem
I have a situation where I have a program will start up another program and it will do some work. I need to be able to stop this work if certain conditions arise. I want to use a CancellationToken on the worker side but as far as I know these can't be used across process boundaries.
I wrote the following code to accomplish this. There are two parts, the the controller and the worker. Here is the code for the controller:
Here is the code for the worker:
```
public sealed class EventWaitHandleCancellationTokenSource : IDisposable
{
private readonly EventWaitHandle waitHandle;
private readonly CancellationTokenSource cancellationTokenSource;
public EventWaitHandleCancellationTokenSource(string eventName)
{
if (!EventWaitHandle.TryOpenExisting(eventName, out waitHandle))
{
waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset, eventName);
}
cancellationTokenSource = new CancellationTokenSource();
ThreadPool.RegisterWaitForSingleObject(
waitHandle,
delegate
{
cancellationTokenSource.Cancel();
}, null, Timeout.InfiniteTimeSpan, true);
}
public bool IsCancellationRequested => waitHandle.WaitOne(0);
public CancellationToken Token => cancellationTokenSource.Token;
public void Dispose()
{
waitHandle.Dispose();
}
}
class Program
{
static void Main(string[] args)
{
var cancellation
I wrote the following code to accomplish this. There are two parts, the the controller and the worker. Here is the code for the controller:
class Program
{
static void Main(string[] args)
{
var eventName = Guid.NewGuid().ToString();
EventWaitHandle waitHandle;
if (!EventWaitHandle.TryOpenExisting(eventName, out waitHandle))
{
waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset, eventName);
}
Process.Start("Worker", eventName);
Console.WriteLine("Press ENTER to cancel...");
Console.ReadLine();
waitHandle.Set();
}
}Here is the code for the worker:
```
public sealed class EventWaitHandleCancellationTokenSource : IDisposable
{
private readonly EventWaitHandle waitHandle;
private readonly CancellationTokenSource cancellationTokenSource;
public EventWaitHandleCancellationTokenSource(string eventName)
{
if (!EventWaitHandle.TryOpenExisting(eventName, out waitHandle))
{
waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset, eventName);
}
cancellationTokenSource = new CancellationTokenSource();
ThreadPool.RegisterWaitForSingleObject(
waitHandle,
delegate
{
cancellationTokenSource.Cancel();
}, null, Timeout.InfiniteTimeSpan, true);
}
public bool IsCancellationRequested => waitHandle.WaitOne(0);
public CancellationToken Token => cancellationTokenSource.Token;
public void Dispose()
{
waitHandle.Dispose();
}
}
class Program
{
static void Main(string[] args)
{
var cancellation
Solution
Let’s refactor it a little bit for clarity and symmetry. We could have this class shared:
Now Controller looks like:
when Worker is:
class NamedTokenSource : CancellationTokenSource
{
const string Namespace = "4978406A-A7C0-4DE7-93C8-6BDB1145ED32";
public static implicit operator NamedTokenSource(string name) =>
new NamedTokenSource(name);
public NamedTokenSource(string name)
: this(new EventWaitHandle(false, EventResetMode.ManualReset, name + Namespace))
{
}
public NamedTokenSource(EventWaitHandle handle)
{
Handle = handle;
ThreadPool.RegisterWaitForSingleObject(handle, (s, to) => Cancel(), null, -1, true);
Token.Register(() => handle.Set());
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
Handle.Dispose();
}
EventWaitHandle Handle { get; }
}Now Controller looks like:
NamedTokenSource cts = "WorkerTokenSource";
Console.WriteLine("Press ENTER to cancel...");
Console.ReadLine();
cts.Cancel();when Worker is:
NamedTokenSource cts = "WorkerTokenSource";
Console.WriteLine("Waiting for cancellation...");
cts.Token.WaitHandle.WaitOne();Code Snippets
class NamedTokenSource : CancellationTokenSource
{
const string Namespace = "4978406A-A7C0-4DE7-93C8-6BDB1145ED32";
public static implicit operator NamedTokenSource(string name) =>
new NamedTokenSource(name);
public NamedTokenSource(string name)
: this(new EventWaitHandle(false, EventResetMode.ManualReset, name + Namespace))
{
}
public NamedTokenSource(EventWaitHandle handle)
{
Handle = handle;
ThreadPool.RegisterWaitForSingleObject(handle, (s, to) => Cancel(), null, -1, true);
Token.Register(() => handle.Set());
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
Handle.Dispose();
}
EventWaitHandle Handle { get; }
}NamedTokenSource cts = "WorkerTokenSource";
Console.WriteLine("Press ENTER to cancel...");
Console.ReadLine();
cts.Cancel();NamedTokenSource cts = "WorkerTokenSource";
Console.WriteLine("Waiting for cancellation...");
cts.Token.WaitHandle.WaitOne();Context
StackExchange Code Review Q#137969, answer score: 2
Revisions (0)
No revisions yet.