Equalizer logo
Collage logo
GPU-SD logo

Packet Handling

Author: eilemann@gmail.com
State: Implemented in 0.1

Background

The packet handling code in Equalizer is the core piece of the networking layer. This document describes the packet dispatch mechanism used by the Equalizer network layer (namespace eq::net). The implementation matches this document at the time of writing (Wed Dec 23 11:47:10 2015 +0100), but may diverge over time.

Packets

Flow chart of the eq::net packet handling
Equalizer Packet Handling

Packets are the base piece of information exchanged between Nodes. A packet has a data type to identify the object it is addressed to, and a command number to identify the purpose of the packet. Packets contain additional data depending on the data type and command, as explained below.

Nodes

Nodes are the basic entity in the network layer. They communicate with each other using Connections. During initialization, a node typically opens a listening connection, and starts the receiver thread. The receiver thread listens on all connections of the node. When a new node connects, he calls handleConnect, which accepts the connection, creates a new Node and adds this connected node to the set of connections. Nodes send each other Packets. The receiving node reads the packet from the connection and calls dispatchCommand.

Commands

Packets read by the node are wrapped in a Command, which holds the packet and node. The Command manages the ownership of the packet and provides convenience functions to access the packet. The ownership management is a lightweight mechanism similar to reference counting, except that the allocated packet can be transferred from one command to another, which is used when the packets are not handled immediately (see redispatch and push cases below). This mechanism allows reuse of handled commands, and avoids copies when the command will be handled later.

The eq::net Base Class

co::Dispatcher provides packet dispatching using a command handler table. During construction, sub classes provide command handlers for each command using registerCommand. The method invokeCommand calls the registered command handler for the provided packet. The command handler may return one of the following command result codes, which is returned by invokeCommand:

COMMAND_HANDLED
The command was handled correctly.
COMMAND_REDISPATCH
The command can not be handled at the moment, and should be redispatched later.
COMMAND_PUSH
The command can not be handled from the command handler, and should be pushed to another entity, typically to another thread.
COMMAND_PUSH_FRONT
Like COMMAND_PUSH, except that the command has a high priority, e.g., it bypasses other commands queued for execution by another thread.
COMMAND_ERROR
A critical error occured during command processing.

Node Packet Dispatch

Depending on the packet's data type, dispatchPacket decides how the packet is handled. For node packets, invokeCommand is called. The result is handled accordingly, that is, COMMAND_REDISPATCH causes the packet to be added to the list of redispatch packets, and for COMMAND_PUSH and COMMAND_PUSH_FRONT pushCommand or pushCommandFront is called, respectively. For Session and Object packets, Session::dispatchPacket is invoked. The session is identified by the sessionID contained in the SessionPacket. For all other data types, handleCommand is called.

Note: Any command handler called directly or indirectly from dispatchPacket is not allowed to block, since it is executed from within the receiver thread and would therefore block the packet handling.

Session Packet Dispatch

The Session inherits, like the node, from co::Dispatcher. The session dispatch works similar to the node dispatch. For session packets, invokeCommand is called, and the result is returned to the node, which handles it accordingly. For object packets, Object::invokeCommand is called and the result is returned. The object is retrieved using the objectID, and possibly instanceID, contained in the ObjectPacket.

Asynchronous Handling of Packets

Several commands for Equalizer entities, for example window initialisation requests, have to be handled by another thread. A thread-safe CommandQueue is used to transfer the packet from the receiver to the consumer thread. This is either done by a special command handler, or by overriding Node::pushCommand or Node::pushCommandFront, depending on the object, command and destination thread.