Skip to content

Navigating the Source (Aeron Transport)

The domain modelling within Aeron Transport is very well done. If you read a description of something, whether it's the Driver Conductor, the Sender, a Network Publication or an Image, all of these concepts map directly to classes in the source code. Some classes are large, like the Driver Conductor, as it naturally has a lot of responsibilities. But it doesn't feel unwieldy once you become familiar with it.

Here are a few classes that serve as useful entry points for navigating through the source.

Client side

  • Aeron - the entry point to Aeron on the client side, for creating Publications and Subscriptions (called on an application thread)
    • addPubliction()
    • addExclusivePubliction()
    • addSubscription()
  • ClientConductor
    • doWork() - has a duty cycle, runs in its own thread by default
  • ConcurrentPublication
    • offer() / tryClaim() - client API for writing to a Publication log buffer from multiple threads
  • ExclusivePublication
    • offer() / tryClaim() - client API for writing to a Publication log buffer from one thread
  • Subscription
    • poll() - client API for reading message fragments
  • Image
    • poll() - reads from a Term, used by Subscription.poll()

Media Driver

  • DriverConductor
    • doWork() - has a duty cycle, runs in its own thread by default
  • Sender
    • doWork() - has a duty cycle, runs in its own thread by default
  • Receiver
    • doWork() - has a duty cycle, runs in its own thread by default
  • NetworkPublication
    • send() - called by the Sender thread
    • updatePublisherPositionAndLimit() - update pub-pos and pub-lmt, called by the Driver Conductor thread
  • IpcPublication
    • updatePublisherPositionAndLimit() - update pub-pos and pub-lmt, called by the Driver Conductor thread
  • PublicationImage
    • trackRebuild() - detect loss, update rcv-pos and rcv-hwm, schedule Status Message

Let's track a method call through the source code. Let's look at creating a Publication with Aeron.addPublication(), which is described in Creating a Publication. I won't link directly to methods as they'll move over time.

Look through the source code yourself while following this.

Application thread

You can see the ClientConductor wants to call into the DriverConductor, but obviously can't because it's in a different process. Instead, it calls into a proxy to the Driver, the DriverProxy. This Proxy pattern is used in several places within Aeron, where a proxy class is used in place of the real target object. Seeing how to navigate through these will help elsewhere.

Now that a PublicationMessage has been written into the to-driver buffer, how do we find what reads that? Go to the PublicationMessageFlyweight class and look at usages of one of the getter methods, such as channel(). Ignoring tests, you'll usually find it's only called from one or two places. In this case, it's only called from ClientCommandAdapter.addPublication().

But what calls that? It's called from ClientCommandAdapter.onMessage(), which is a callback defined in ControlledMessageHandler that's called from a RingBuffer. That makes sense as the PublicationMessage was written to a ring buffer. The ClientCommandAdapter is created in the DriverConductor, which calls ClientCommandAdapter.receive() in DriverConductor.doWork(). It's that receive() method that's calling toDriverCommands.controlledRead() to read from the to-driver buffer. So, we have this:

DriverConductor thread

Here's another pattern that's used widely - the Adapter pattern is used whenever a message is being read from a buffer and needs handling depending on its message type. Here, the DriverConductor is handling a message from the cnc.dat file.

addPublication() branches depending on whether the channel is for an IPC or Network Publication. Let's say it's a Network Publication and continue.

Here's another proxy. The SenderProxy (proxy to the Sender) varies depending on whether the Sender is running on the same thread as the DriverConductor, which depends on the threading model. If it's running on the same thread, the DriverConductor invokes Sender.onRegisterSendChannelEndpoint() directly. If not, it adds a Runnable to a command queue, where the Runnable invokes the method. This command queue is injected into the SenderProxy and Sender. The Sender processes messages from the command queue in its doWork() method.

I won't go into all the remaining details. Ultimately, the DriverConductor needs to send a message back to the application thread to tell it that the Publication has been created. It calls into another proxy: ClientProxy.onPublicationReady(), which writes a PublicationBuffersReady message into the cnc.dat's to-clients buffer. This is what the application thread waiting for, polling in a loop.

Beware multiple threads

Some classes, like NetworkPublication are shared between the Driver Conductor and the Sender, so be aware of which thread a method is being called from. All method calls will ultimately originate from the doWork() method on one of them, which will tell you which thread it's called from.