Helios 2.0 Development Diary 2 - Channels, Config, and the Curiously Recurring Template Pattern

Picking up where I left off in the previous Helios 2.0 diary entry… After clearing the decks of all of any code I wasn’t 100% certain we’d be keeping, I began writing new code.

IChannel, IChannelConfig, and more

I immediately began by porting Netty’s Channel interface to C# (IChannel interface,) as this defines the root element of work behind the Channel API in Helios.

The original Channel interface is chock-full of detailed, helpful comments written by the Netty team and I did my best to port most of those over onto the IChannel interface in Helios, but I got impatient and left a couple of them to be filled-in as TODOs (send me a PR!)

Next I got to work on porting all of the ChannelConfig options, which included porting the AbstractConstant and ChannelOption classes.

The Curiously Recurring Template Pattern

So I noticed something interesting when looking at the original ChannelOption Java classes in Netty…

ChannelOption.java

public final class ChannelOption<T> extends AbstractConstant<ChannelOption<T>> {
	/* ... */
}

Huh, the ChannelOption extends an AbstractConstant class but uses itself as a generic parameter… So I wonder what the deal with the AbstractConstant<T> class is…

AbstractConstant.java

public abstract class AbstractConstant<T extends AbstractConstant<T>> implements Constant<T> {
	/* ... */
}

So the AbstractConstant<T> takes a generic argument of type T, which is constrained to be of type… AbstractConstant<T>.

My braaaaaaaaaaaain

Ooooook. It hurt my brain to even figure out what this code is and why someone would write a generic constraint this way.

I don’t know all of the intricacies of the JVM and all of the nuances of their type system, so my first thought in porting this code was to determine if it’s even possible to implement this pattern in C#.

As it turns out: generic classes with self-referencing constraints are legal in C#.

I took comfort in knowing that even the great Eric Lippert also experiences brain pain at trying to comprehend this code pattern:

But this really hurts my brain:

class Blah<T> where T : Blah<T>

That appears to be circular in (at least) two ways. Is this really legal?

Yes it is legal, and it does have some legitimate uses. I see this pattern rather a lot(**). However, I personally don’t like it and I discourage its use.

This is a C# variation on what’s called the Curiously Recurring Template Pattern in C++, and I will leave it to my betters to explain its uses in that language. Essentially the pattern in C# is an attempt to enforce the usage of the CRTP.

TL;DR; a bunch of evil C++ programmers, in a misguided effort to comply with the Liskov Substitution Principle, invented this smelly pattern which ended up not being able to enforce the LSP anyway.

The Ugliest Boxing

So I decided to design my C# ChannelOption class in Helios to avoid this pattern, mostly to help make the code more comprehensible.

/// <summary>
/// A <see cref="ChannelOption{T}"/> enable us to configure a <see cref="IChannelConfig"/> in a
/// type-safeway. Which <see cref="ChannelOption{T}"/> is supported depends on the actual implementation
/// of <see cref="IChannelConfig"/> and may depend on the nature of the transport it belongs to.
/// </summary>
public sealed class ChannelOption<T> : AbstractConstant
{
    public ChannelOption(int id, string name) : base(id, name, typeof(T))
    {
    }

    /// <summary>
    /// Validate the value which is set for the <see cref="ChannelOption{T}"/>. 
    /// </summary>
    /// <param name="value">The value that will be set for this option.</param>
    public void Validate(T value)
    {
        if(value == null)
            throw new ArgumentNullException("value");
    }

    #region Conversion

    // Cache of previously casted values, since template downcasting works a little differently in C#
    private static readonly ConcurrentDictionary<ChannelOption<object>, ChannelOption<T>> CastedValues 
		= new ConcurrentDictionary<ChannelOption<object>, ChannelOption<T>>();


    public static implicit operator ChannelOption<T>(ChannelOption<object> obj)
    {
        return CastedValues.GetOrAdd(obj, new ChannelOption<T>(obj.Id, obj.Name));
    }

    #endregion
}

You’ll notice the implicit cast between ChannelOption<object> and ChannelOption<T> - I had to implement this because the following is a perfectly valid cast in Java:

public final class ChannelOption<T> extends AbstractConstant<ChannelOption<T>> {

    private static final ConstantPool<ChannelOption<Object>> pool 
		= new ConstantPool<ChannelOption<Object>>() {
        @Override
        protected ChannelOption<Object> newConstant(int id, String name) {
            return new ChannelOption<Object>(id, name);
        }
    };

    /**
     * Returns the {@link ChannelOption} of the specified name.
     */
    @SuppressWarnings("unchecked")
    public static <T> ChannelOption<T> valueOf(String name) {
        return (ChannelOption<T>) pool.valueOf(name);
    }

	//rest of the class
}

This cast (ChannelOption<T>) pool.valueOf(name); casts an object of type ChannelOption<object> into a ChannelOption<T> like it’s no big deal. This cast isn’t legal in C#, so in order to try to stick with Netty’s design as close as I can I had to add implicit operator to ChannelOption<T> in C#.

Plus I had to write some evil-looking code inside DefaultChannelConfig like this, as the result of more usages of this weird cast in the original Java code:

public T GetOption<T>(ChannelOption<T> option)
{
    if(option == null)
        throw new ArgumentNullException("option");
    if (option == ChannelOption.CONNECT_TIMEOUT_MILLIS)
        return (T)(object)ConnectTimeoutMillis;
    if (option == ChannelOption.MAX_MESSAGES_PER_READ)
        return (T) (object) MaxMessagesPerRead;
    if (option == ChannelOption.WRITE_SPIN_COUNT)
        return (T) (object) WriteSpinCount;
    if(option == ChannelOption.ALLOCATOR)
        return (T)(object)Allocator;
    //TODO: RCVBUF_ALLOCATOR
    if (option == ChannelOption.AUTO_READ)
        return (T) (object) AutoRead;
    if (option == ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK)
        return (T) (object) WriteBufferHighWaterMark;
    if (option == ChannelOption.WRITE_BUFFER_LOW_WATER_MARK)
        return (T)(object)WriteBufferLowWaterMark;
    //TODO: MESSAGE_SIZE_ESTIMATOR
    return default(T);
}

For clarification: the above is the C# code I wrote.

Take a closer look this: return (T)(object)ConnectTimeoutMillis;

In this code we are:

  1. Casting an int to an object via boxing;
  2. Casting that object to type T, an int, via boxing again.

No God! Please No!

Oh Java, you and your type system. You so crazy.

So yeah, that’s some of the smelliest statically typed code I’ve ever written.

Thankfully, it’s infrequently used even within Netty (most of these configuration properties are accessed via strongly typed getters and setters) so it doesn’t pose any major performance risks.

Onto the next challenge!

Discussion, links, and tweets

I'm the CTO and co-founder of Petabridge, where I'm making distributed programming for .NET developers easy by working on Akka.NET and Helios.

P.S. Get the latest from Aaronontheweb

Have my most recent essays and articles delivered directly to your mailbox.