OnixS C++ FIX Engine 4.14.0
Users' manual and API documentation
Loading...
Searching...
No Matches
Listening to Session Events

OnixS C++ FIX Engine exposes the OnixS::FIX::ISessionListener class, whose virtual members are called by an instance of the OnixS::FIX::Session class, as response to a particular event occurred during the session life-time.

To be notified about a particular session event, override the appropriate member of the OnixS::FIX::ISessionListener class, and supply a derived-class instance to the OnixS::FIX::Session's constructor.

The following table describes the meaning of all events, exposed by the OnixS::FIX::Session class in terms of OnixS::FIX::ISessionListener class members.

Method
(Event handler)
Description
onInboundApplicationMsg Application-level message is received from the counterparty.
onInboundSessionMsg Session-level message is received from the counterparty.
onOutboundApplicationMsg Application-level message will be sent to the counterparty. This callback could be used to change fields set by the FIX Engine.
onOutboundSessionMsg Session-level message will be sent to the counterparty. This callback could be used to change fields set by the FIX Engine.
onReceivedBytes Bytes are received from the wire.
onMessageSending Just before the FIX message is sent to the wire.
onResendRequest Sent application-level message is about to be resent to the counterparty.
onResendingStarted ResendRequest message is received and the session is about to start resending messages.
onResendingFinished Resending process is completed.
onWarning Warning condition is detected.
onError Error condition is detected.
onStateChange Session state is changed.

Callback Safety

Note
Callbacks run synchronously: the Engine waits for a callback to return before continuing that operation. Keep callbacks short. Do not wait for work that needs the same session or reactor to make progress, including waiting for a logon or logout future. Defer shutdown and other operations that change session state.

See FIX Engine Threading Models for the threads that can invoke callbacks.

Callback Operations to avoid and deadlock risks Data to copy for later use
onInboundApplicationMsg, onInboundSessionMsg Avoid slow processing and blocking waits; use Asynchronous Processing of Incoming Messages. Required message fields or an independent message copy.
onReceivedBytes Do not block network processing. Received bytes and timestamp values.
onOutboundApplicationMsg, onOutboundSessionMsg Same-session sends have batch and resend restrictions; see Sending Messages from Outbound Callbacks. Cross-session calls can deadlock. Required message fields or an independent message copy.
onMessageSending Do not send on the same session. Cross-session calls can deadlock. Sequence number and any available bytes; FlatMessage supplies no buffer.
onResendRequest, onResendingStarted, onResendingFinished Keep processing short; do not send on the same session from onResendRequest. See Resending Messages for the resend policy. Required message fields from onResendRequest.
onStateChange Avoid calls that change another session's state and can create a lock cycle. State values.
onError, onWarning Do not send on the same session when reporting a condition from its sending operation. Avoid recursive error handling. Reason and description values.
Warning
Outbound callbacks (onOutboundApplicationMsg, onOutboundSessionMsg, and onMessageSending) are called under the session's lock. onError and onWarning are also called under that lock when they report a condition while a message is being prepared for sending. Calling another session from these callbacks can deadlock if another thread holds that session's lock and waits for this session. Application locks can create the same kind of cycle.
Warning
onStateChange is called under a separate lock to serialize state-change notifications. Calls that change another session's state can deadlock if its callback calls back into this session.

Callback Data Lifetime

The Engine supplies temporary message wrappers to message callbacks. Do not retain their addresses or references, field references, or pointers into their contents after the callback returns. Likewise, copy raw bytes, timestamp values, and error or warning descriptions before using them later. Do not assume that holding a pointer keeps the data alive.

Copy the fields needed by deferred work into application-owned values, or make an independent message copy. A copied pointer to message user data does not copy the object it points to; its lifetime remains the application's responsibility. FlatMessage field references can also become invalid when the message is changed or sent; see Accessing FIX Fields. The Async Processing Sample demonstrates copying incoming messages for a worker thread.

Listener Lifetime and Registration

The Engine does not own session listeners. Register all listeners before connecting, and keep them alive until the session has finished calling them. Do not register or unregister listeners from a callback of that same session, or concurrently with callback dispatch: dispatch iterates the listener collection, and changing that collection can invalidate the iteration.

Both OnixS::FIX::Session::unregisterListener and OnixS::FIX::Session::unregisterAllListeners throw unless the session is OnixS::FIX::SessionState::Disconnected. Being disconnected does not itself wait for callbacks already running, so arrange for dispatch and other calls that can invoke callbacks to finish before removing or destroying listeners.

Sending Messages from Outbound Callbacks

The outbound callbacks are called while the session is preparing a message for sending, so a message sent from such a callback can reach the wire before the message being prepared, while carrying a higher MsgSeqNum value. The counterparty then receives a message whose sequence number is less than expected while the PossDupFlag is not set, which the FIX protocol treats as a serious error requiring the FIX session to be terminated.

Whether this can happen depends on the callback:

  • onOutboundApplicationMsg and onOutboundSessionMsg are called before their message is serialized. When the message is sent by a single-message sending method, nothing is prepared yet, so a message sent from the callback simply takes the lower sequence number and is sent first. This is supported.
  • onMessageSending is called after its message is serialized and has taken its sequence number, so a message sent from this callback would overtake it. This is not supported.
  • When a message batch is being sent, the messages preceding the current one are already serialized and are waiting to be sent. Sending from onOutboundApplicationMsg or onOutboundSessionMsg of any message of the batch except the first one would overtake them. This is not supported.
  • When a message is re-sent in reply to the ResendRequest<2> message, it keeps its original sequence number, which is lower than the number any newly sent message takes. Sending from onResendRequest, or from onOutboundApplicationMsg and onOutboundSessionMsg of a re-sent message, would therefore overtake it. This is not supported.
Warning
The not supported cases above throw an exception, except the sending from onResendRequest, which is not detected by the Engine. The exception is reported through onError with the OnixS::FIX::ErrorReason::ExceptionInEventHandler reason and is then propagated by the sending call which has started the sending. This concerns the OnixS::FIX::Session::send, OnixS::FIX::Session::sendAsIs, OnixS::FIX::Session::sendTestRequest, OnixS::FIX::Session::sendResendRequest, OnixS::FIX::Session::sendReject, OnixS::FIX::Session::logout, OnixS::FIX::Session::logoutAsync and OnixS::FIX::Session::resetSeqNumViaLogonExchange methods. The OnixS::FIX::Session::logoutAsync method performs the check synchronously, therefore it throws the exception from the call itself and does not report it through the returned future. The callbacks are also called for the messages which the FIX Engine originates itself, for example the Heartbeat<0> message; there is no such sending call in the application code for them, therefore the exception is reported through onError only.

The messages which the sending call has already prepared consume their sequence numbers even though none of them is sent. Restore the outgoing sequence number with the OnixS::FIX::Session::outSeqNum method to the value it had before the call, otherwise the next sent message leaves a gap and the counterparty requests the missing messages with the ResendRequest<2> message. Any exception thrown from these callbacks has the same effect.

Of the outbound message callbacks, OnixS::FIX::FlatMessage sending supports only onMessageSending, with a null buffer and zero length; onOutboundApplicationMsg and onOutboundSessionMsg are not called. This applies to single-message and batch sends, send-mode overloads, OnixS::FIX::Session::sendAsIs, and a custom FlatMessage Logout sent by OnixS::FIX::Session::logout or OnixS::FIX::Session::logoutAsync. See FAQ Q18 for the buffer explanation. Therefore, sending a message from within any callback triggered by such a send operation is not supported.

The onError and onWarning callbacks are also called while a message is prepared for sending when they report a condition detected by the sending itself, for example the rejection described above. Sending a message on the same session from them is not supported in such a case and throws an exception for the same reason.

For a supported way to defer sending, see Deferred Processing and Shutdown.

The OnixS::FIX::Session::logout and OnixS::FIX::Session::logoutAsync methods are affected because they produce the Logout message, and the OnixS::FIX::Session::resetSeqNumViaLogonExchange method is affected because it produces the Logon message. The OnixS::FIX::Session::breakConnection method is not affected, because it terminates the FIX connection without the Logout message.

The restriction is per session, so sending a message on a different session from these callbacks is outside this restriction. It can still deadlock through lock ordering; see Callback Safety.

Deferred Processing and Shutdown

Copy the required data in the callback and return. For callbacks triggered by an application sending call, perform deferred operations after that call returns.

ExternalThread Event Loop

In ExternalThread mode, callbacks for dispatched network events run synchronously during OnixS::FIX::ISessionReactor::dispatchEvents. Keep callbacks short, do not block, and do not call dispatchEvents recursively. Record any requests and copy the data needed to act on them after the callback returns. Perform deferred session operations on the reactor-owning thread between calls to dispatchEvents:

while (!stop)
{
stack.dispatchEvents();
processApplicationWork();
}

Here, stop and processApplicationWork() are application-defined. Both calls run on the same thread. Keep application work bounded so that dispatchEvents() is called frequently enough to meet latency requirements and process network events and timers. Calls such as OnixS::FIX::Session::send can invoke callbacks directly, before returning to application work.

Use asynchronous logon and logout, and continue dispatching while those operations are pending; see Asynchronous Logon and Logout. For more capacity, assign separate stacks and session groups to separate threads; see Split the Sessions Processing by Different Stacks in Separate Threads.

Worker Threads

With DedicatedThreads or ThreadPool, a synchronized queue can hand work to another application thread; do not wait for that thread from the callback or hold an application lock while calling the session. If an ExternalThread application offloads processing to a worker, the worker must return any resulting session operations to the reactor-owning thread; see Asynchronous Processing of Incoming Messages.

Shutdown

Request application shutdown outside callbacks. Stop accepting new application work and finish or discard pending work before destroying the objects it uses. Keep listeners alive while sessions shut down. Follow Shutting down the whole Application and, for externally managed reactors, Shutdown.

Example

class SessionStateListener : public ISessionListener
{
public:
void onStateChange(SessionState::Enum newState, SessionState::Enum prevState, Session*) override
{
std::clog
<< "\nSession's state is changed"
<< ". Previous state was " << SessionState::toString(prevState)
<< ", new state is " << SessionState::toString(newState)
<< std::endl;
}
};