Search code examples
c#ooprefactoringrequest-cancelling

How to cancel a deeply nested process


I have a class that is a "manager" sort of class. One of it's functions is to signal that the long running process of the class should shut down. It does this by setting a boolean called "IsStopping" in class.

public class Foo
{
    bool isStoping

    void DoWork() {
        while (!isStopping)
        {
            // do work...
        }
    }
}

Now, DoWork() was a gigantic function, and I decided to refactor it out and as part of the process broke some of it into other classes. The problem is, Some of these classes also have long running functions that need to check if isStopping is true.

public class Foo
{
    bool isStoping

    void DoWork() {
        while (!isStopping)
        {
            MoreWork mw = new MoreWork()
            mw.DoMoreWork() // possibly long running
            // do work...
        }
    }
}

What are my options here?

I have considered passing isStopping by reference, which I don't really like because it requires there to be an outside object. I would prefer to make the additional classes as stand alone and dependancy free as possible.

I have also considered making isStopping a property, and then then having it call an event that the inner classes could be subscribed to, but this seems overly complex.

Another option was to create a "Process Cancelation Token" class, similar to what .net 4 Tasks use, then that token be passed to those classes.

How have you handled this situation?

EDIT:

Also consider that MoreWork might have a EvenMoreWork object that it instantiates and calls a potentially long running method on... and so on. I guess what i'm looking for is a way to be able to signal an arbitrary number of objects down a call tree to tell them to stop what they're doing and clean up and return.

EDIT2:

Thanks for the responses so far. Seems like there's no real consensus on methods to use, and everyone has a different opinion. Seems like this should be a design pattern...


Solution

  • You can go two ways here:

    1) The solution you've already outlined: pass a signaling mechanism to your subordinate objects: a bool (by ref), the parent object itself cloaked in an interface (Foo: IController in the example below), or something else. The child objects check the signal as needed.

    // Either in the MoreWork constructor
    public MoreWork(IController controller) {
        this.controller = controller;
    }
    
    // Or in DoMoreWork, depending on your preferences
    public void DoMoreWork(IController controller) {
        do {
            // More work here
        } while (!controller.IsStopping);
    }
    

    2) Turn it around and use the observer pattern - which will let you decouple your subordinate objects from the parent. If I were doing it by hand (instead of using events), I'd modify my subordinate classes to implement an IStoppable interface, and make my manager class tell them when to stop:

    public interface IStoppable {
        void Stop();
    }
    
    public class MoreWork: IStoppable {
        bool isStopping = false;
        public void Stop() { isStopping = true; }
        public void DoMoreWork() {
            do {
                // More work here
            } while (!isStopping);
        }
    }
    

    Foo maintains a list of its stoppables and in its own stop method, stops them all:

    public void Stop() {
        this.isStopping = true;
        foreach(IStoppable stoppable in stoppables) {
            stoppable.Stop();
        }
    }