Interprocess CancellationToken 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:

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 =&gt; waitHandle.WaitOne(0);
    public CancellationToken Token =&gt; cancellationTokenSource.Token;

    public void Dispose()
    {
        waitHandle.Dispose();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var cancellationTokenSource = new EventWaitHandleCancellationTokenSource(args[0]);

        Console.Write("Waiting for cancellation...");
        cancellationTokenSource.Token.WaitHandle.WaitOne();
    }
}


Is this the best way to do this?  It is the only reasonable way I can think of.
Let’s refactor it a little bit for clarity and symmetry. We could have this class shared:

class NamedTokenSource : CancellationTokenSource
{
    const string Namespace = "4978406A-A7C0-4DE7-93C8-6BDB1145ED32";

    public static implicit operator NamedTokenSource(string name) =&gt;
        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) =&gt; Cancel(), null, -1, true);
        Token.Register(() =&gt; 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();