I've been playing with the value type ring buffer and I realized there was a major downside compared to the reference type version: as soon as you implement a real world scenario where you have multiple steps (something like the diamond sample or the 3 step pipeline), the first consumers (event processors I should say) need to do some processing and often mutate the state of the event.
Let's take the example of LMAX architecture:
- event processor "unmarshaler" deserialise some binary data
- the resulting object is 'attached' to the event
- the business logic event processor then uses the deserialised object
Now imagine you want to do the same thing but this time the event is a struct:
- producer thread creates the struct and commit to the ring buffer
- unmarshaler gets a copy of the event (value type = copy semantic). Unmarshaler can not attach the deserialised object to the event, it only owns a copy... We would have to commit back the whole event to the ring buffer.
2 downsides:
- performance: commiting back the whole struct invalidates all the cache lines containing it
- complexity: the producer side and the consumer side ends up with completly different semantics compared to the reference type version, which means 2 different implementations of most classes of the disruptor. Developer would as well have to use 2 different APIs which is not great...
I may put it back at some point if I come up with a better design, but I'm not sure it will be possible.
Olivier