HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Interprocess CancellationToken

Submitted by: @import:stackexchange-codereview··
0
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:

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:

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.