OnixS C++ FIX Engine 4.14.0
Users' manual and API documentation
Loading...
Searching...
No Matches
Asynchronous Logon/Logout

OnixS::FIX::Session::logonAsInitiatorAsync and OnixS::FIX::Session::logoutAsync are the asynchronous counterparts of OnixS::FIX::Session::logonAsInitiator and OnixS::FIX::Session::logout. They return without waiting for the acknowledgment Logon or Logout message.

The returned OnixS::Threading::SharedFuture represents the operation's result, not proof that it succeeded. Its OnixS::Threading::SharedFuture::get() method blocks if the result is not ready, then returns on success or throws on failure. Failures can include connection errors, timeouts, and invalid Logon responses.

Protect both the initiating call and get() with exception handling. An asynchronous call can throw before returning a future: for example, calling logoutAsync on the same session from an outbound callback throws synchronously if the Logout message would break the order of already prepared outgoing messages. That exception is not stored in a returned future. See Sending Messages from Outbound Callbacks and Error Reporting for reporting channels and recovery rules.

Warning
Do not wait for a logon or logout future from a callback or thread needed for that operation to progress. Starting an asynchronous operation from an inbound callback avoids waiting there only if the callback returns without calling get() or otherwise waiting for completion. Observe the result later outside the callback. In ExternalThread mode, continue dispatching events while the operation is pending; see ExternalThread Event Loop and Asynchronous Logon and Logout.

Example

This example runs on an application thread outside Engine callbacks. The Engine must already be initialized and the counterparty configured. The session uses DedicatedThreads so network processing continues while this application thread waits. Include <atomic> for the listener's counters.

The listener only counts notifications; it does not wait, perform I/O, or call the session. The counts are observations, not operation results. Zero notifications do not guarantee success, and a notification does not necessarily mean that the operation failed. Production diagnostics should also capture the reason and description through an application-owned, thread-safe reporting mechanism; copy callback data before retaining it, as described in Callback Data Lifetime.

Both initiation and completion are inside the try block. A failure skips the remaining normal operations. The session is destroyed before the listener on success and during exception unwinding; its destruction shuts it down while the listener is still alive. No other application thread uses this session. The Engine must remain alive until session cleanup finishes; see Shutting down the whole Application.

using namespace OnixS::FIX;
class FailureReportingListener : public ISessionListener
{
public:
void onInboundApplicationMsg(Message &, Session *) override
{
// Application message handling is omitted from this failure-reporting example.
}
void onError(ErrorReason::Enum, const std::string &, Session *) override
{
// Record the notification without waiting or calling the session.
++errorCount;
}
void onWarning(WarningReason::Enum, const std::string &, Session *) override
{
++warningCount;
}
std::atomic<unsigned> errorCount{0};
std::atomic<unsigned> warningCount{0};
};
void asynchronousLogonLogout()
{
// Keep the listener alive until session cleanup has finished on every exit path.
FailureReportingListener listener;
const char * const SessionSetupStage = "Session setup";
const char * const LogonStage = "Logon";
const char * const LogoutStage = "Logout";
const char * stage = SessionSetupStage;
try
{
const std::string SenderCompId = "SenderCompId";
const std::string TargetCompId = "TargetCompId";
const std::string Host = "localhost";
const int Port = Engine::instance()->listenPort();
const int HeartbeatIntervalSec = 30;
const bool SetResetSeqNumFlag = false;
const std::string LogoutReason = "Asynchronous Logout";
Session initiator(SenderCompId, TargetCompId, ProtocolVersion::FIX_42, &listener);
initiator.threadingModel(ThreadingModel::DedicatedThreads);
// The call itself can throw; returning a future does not guarantee success.
stage = LogonStage;
OnixS::Threading::SharedFuture<void> logonResult = initiator.logonAsInitiatorAsync(
Host, Port, HeartbeatIntervalSec, nullptr, SetResetSeqNumFlag);
// Perform other application work here before observing completion.
// This application thread may wait; Engine callbacks must not wait on this future.
logonResult.get();
std::clog << "Logon completed successfully." << std::endl;
// Exchange application messages here after successful logon.
stage = LogoutStage;
OnixS::Threading::SharedFuture<void> logoutResult = initiator.logoutAsync(LogoutReason);
// Perform other application work here, then wait outside Engine callbacks.
logoutResult.get();
std::clog << "Logout completed successfully." << std::endl;
// Scope exit shuts down and destroys the session before the listener.
}
catch (const FirstMessageNotLogonException & ex)
{
std::clog << stage << " failed (FirstMessageNotLogonException): " << ex.what() << std::endl;
}
catch (const LinkErrorException & ex)
{
std::clog << stage << " failed (LinkErrorException): " << ex.what() << std::endl;
}
catch (const TimeoutException & ex)
{
std::clog << stage << " failed (TimeoutException): " << ex.what() << std::endl;
}
{
std::clog << stage << " failed (UnexpectedSequenceNumberException): " << ex.what() << std::endl;
}
{
std::clog << stage << " failed (ConfirmationLogonMessageErrorException): " << ex.what() << std::endl;
}
catch (const std::exception & ex)
{
std::clog << stage << " failed: " << ex.what() << std::endl;
}
// Counts include cleanup notifications.
std::clog << "Observed error notifications: " << listener.errorCount.load()
<< "; warning notifications: " << listener.warningCount.load() << std::endl;
}