Question

I have the following setup of TransformBlock:

private void SetupTestModule()
{
    Func<int, int> func1 = new Func<int, int>(input =>
        {
            return (input + 1);
        });

    Func<int, int> func2 = new Func<int, int>(input =>
        {
            return (input + 2);
        });

    TransformBlock<int, int> transform = new TransformBlock<int, int>(func1);
}

I wonder whether I can, during run-time, exchange func1 for func2 and whether all the linkages between this transformBlock and other data blocks will remain intact? Ideally upon swapping the Funcs I simply want the new transformation to apply to all newly incoming items. Obviously with this simplistic approach I would not make assumptions about items that are currently in the in-queue and how they are handled. I just wonder whether assigning a new Func would cause a runtime error or otherwise cause the transformBlock to be unlinked. Anyone who could share some insights?

Edit: Post Jon's suggestions and code here some very basic test code. What makes me curious is that it works with and without the volatile keyword. Why do we need volatile?

public class TransformBlockHotSwap
{
    private TransformBlock<int, int> transformBlock;
    private ActionBlock<int> actionBlock;

    public TransformBlockHotSwap()
    {

        SwappableFunction<int, int> swappable = new SwappableFunction<int, int>(item => item + 1);
        transformBlock = new TransformBlock<int, int>(item => swappable.Execute(item));
        actionBlock = new ActionBlock<int>(item => Console.WriteLine(item));
        transformBlock.LinkTo(actionBlock);

        Func<int, int> func2 = new Func<int,int>(item => item * item);

        for (int index = 1; index <= 100; index++)
        {
            transformBlock.Post(index);

            Thread.Sleep(500);

            if (index == 5)
            {
                swappable.Swap(func2);
            }
        }
    }
}

public class SwappableFunction<TInput, TOutput>
{
    private Func<TInput, TOutput> func;

    public SwappableFunction(Func<TInput, TOutput> func)
    {
        this.func = func;
    }

    public void Swap(Func<TInput, TOutput> newFunc)
    {
        func = newFunc;
    }

    public TOutput Execute(TInput input)
    {
        return func(input);
    }
}

Edit (to include predicate swapping):

public class TransformBlockHotSwap
{
    private TransformBlock<int, int> transformBlock;
    private ActionBlock<int> actionBlock;

    public TransformBlockHotSwap()
    {
        Func<int, int> defaultFunction = new Func<int, int>(item => item);
        Func<int, int> func2 = new Func<int, int>(item => item * item);
        Predicate<int> defaultPredicate = new Predicate<int>(item => true);
        Predicate<int> pred2 = new Predicate<int>(item =>
            {
                if (item % 2 == 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            });

        SwappableFunction<int, int> swappableFunction = new SwappableFunction<int, int>(defaultFunction);
        SwappablePredicate<int> swappablePredicate = new SwappablePredicate<int>(defaultPredicate);

        transformBlock = new TransformBlock<int, int>(item => swappableFunction.Execute(item));
        actionBlock = new ActionBlock<int>(item => Console.WriteLine(item));
        transformBlock.LinkTo(actionBlock, item => swappablePredicate.Execute(item));

        for (int index = 1; index <= 100; index++)
        {
            transformBlock.Post(index);

            if (index == 10)
            {
                swappablePredicate.Swap(pred2);
            }

            Thread.Sleep(200);
        }

        Console.WriteLine("Done");
        Console.ReadKey();
    }
}

public class SwappableFunction<TInput, TOutput>
{
    private volatile Func<TInput, TOutput> func;

    public SwappableFunction(Func<TInput, TOutput> defaultFunction)
    {
        this.func = defaultFunction;
    }

    public void Swap(Func<TInput, TOutput> newFunc)
    {
        func = newFunc;
    }

    public TOutput Execute(TInput input)
    {
        return func(input);
    }
}

public class SwappablePredicate<TInput>
{
    private volatile Predicate<TInput> predicate;

    public SwappablePredicate(Predicate<TInput> defaultPredicate)
    {
        this.predicate = defaultPredicate;
    }

    public void Swap(Predicate<TInput> newPredicate)
    {
        predicate = newPredicate;
    }

    public bool Execute(TInput input)
    {
        return predicate(input);
    }
}
Was it helpful?

Solution

I wouldn't expect you to be able to do so - but you could easily write a function which delegates:

public class SwappableFunction<TInput, TOutput>
{
    private volatile Func<TInput, TOutput> func;

    public SwappableFunction(Func<TInput, TOutput> func)
    {
        this.func = func;
    }

    public void Swap(Func<TInput, TOutput> newFunc)
    {
        func = newFunc;
    }

    public TOutput Execute(TInput input)
    {
        return func(input);
    }
}

Then:

var swappable = new SwappableFunction<int, int>(input => input + 1);
var block = new TransformBlock<int, int>(swappable.Execute);
// Later...

swappable.Swap(input => input + 2);

Important - I'm not 100% sure about the use of volatile here - I generally don't like using volatile as its semantics are confusing. It's possible that using Interlocked.CompareExchange would be better - but the code would be significantly longer, and I wanted to get the main point across first :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top