llfix
Low-latency FIX engine
engine.h
1 /*
2 MIT License
3 
4 Copyright (c) 2026 Coreware Limited
5 
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12 
13 The above copyright notice and this permission notice shall be included in all
14 copies or substantial portions of the Software.
15 
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23 */
24 #pragma once
25 
26 #include "common.h"
27 
28 #include <atomic>
29 #include <cstdint>
30 #include <cstdlib>
31 #include <cstdio>
32 #include <filesystem>
33 
34 #include "core/os/vdso.h"
35 #include "core/os/process_utilities.h"
36 #include "core/os/thread_local_storage.h"
37 #include "core/os/hangup_signal_handler.h"
38 #include "core/os/virtual_memory.h"
39 
40 #ifdef LLFIX_ENABLE_NUMA // VOLTRON_EXCLUDE
41 #include "core/os/numa_utilities.h"
42 #endif // VOLTRON_EXCLUDE
43 
44 #ifdef LLFIX_ENABLE_TCPDIRECT // VOLTRON_EXCLUDE
45 #include "core/solarflare_tcpdirect/tcpdirect_api.h"
46 #endif // VOLTRON_EXCLUDE
47 
48 #ifdef LLFIX_ENABLE_OPENSSL // VOLTRON_EXCLUDE
49 #include "core/ssl/ssl_api.h"
50 #endif // VOLTRON_EXCLUDE
51 
52 #include "core/os/socket.h"
53 #include "core/utilities/logger.h"
54 #include "core/utilities/version.h"
55 #include "core/utilities/allocator.h"
56 
57 #include "engine_settings.h"
58 
59 #include "management_server/management_server_settings.h"
60 #include "management_server/management_server.h"
61 
62 namespace llfix
63 {
64 
71 class Engine
72 {
73  public:
74 
84  static void on_start(const std::string& config_file_path = "", const std::string& config_group_name ="ENGINE")
85  {
86  if(m_engine_initialised.load() == true)
87  {
88  return;
89  }
90 
91  #ifdef __linux__
92  if(ProcessUtilities::has_root_privileges() == false)
93  {
94  fprintf(stderr, "llfix engine : Requires root privileges on Linux.\n");
95  std::exit(-1);
96  }
97  #endif
98 
99  m_application_start_timestamp = VDSO::nanoseconds_monotonic();
100 
101  EngineSettings engine_settings;
102 
103  if(config_file_path.length()>0)
104  {
105  if(engine_settings.load_from_config_file(config_file_path, config_group_name) == false)
106  {
107  fprintf(stderr, "Failed to load %s : %s \n", config_file_path.c_str(), engine_settings.config_load_error.c_str());
108  std::exit(-2);
109  }
110  }
111 
112  if(engine_settings.validate() == false)
113  {
114  fprintf(stderr, "llfix engine : Failed to verify %s : %s \n", config_file_path.c_str(), engine_settings.validation_error.c_str());
115  std::exit(-3);
116  }
117 
118  HangupSignalHandler::set_ignore(engine_settings.ignore_sighup);
119 
120  if( Logger<>::get_instance().initialise(engine_settings.log_file, Logger<>::convert_string_to_log_level(engine_settings.log_level), engine_settings.logger_async, engine_settings.logger_buffer_capacity) == false)
121  {
122  fprintf(stderr, "llfix engine : Logger initialisation failed\n");
123  std::exit(-4);
124  }
125 
126  if(engine_settings.numa_bind_node >= 0)
127  {
128  #ifdef _WIN32
129  fprintf(stderr, "WARNING : numa_bind_node is specified however NUMA features supported only on Linux.\n");
130  #elif __linux__
131  #ifdef LLFIX_ENABLE_NUMA
132  if(NumaUtilities::get_current_numa_node() == -1)
133  {
134  fprintf(stderr, "WARNING : numa_bind_node is set however there is no NUMA node on the system, therefore it won't take affect\n");
135  }
136  else
137  {
138  if( NumaUtilities::bind_to_numa_node(engine_settings.numa_bind_node) == false)
139  {
140  fprintf(stderr, "llfix engine : Failed to bind to NUMA node %d \n", engine_settings.numa_bind_node);
141  std::exit(-5);
142  }
143 
144  LLFIX_LOG_INFO("Bound NUMA node : " + std::to_string(NumaUtilities::get_current_numa_node()));
145  Allocator::set_numa_aware(engine_settings.numa_aware_allocations);
146  }
147  #else
148  fprintf(stderr, "WARNING : numa_bind_node is set however this build not built with LLFIX_ENABLE_NUMA flag, therefore it won't take affect\n");
149  #endif
150  #endif
151  }
152 
153  if(engine_settings.lock_pages)
154  {
155  #ifdef __linux__
156  auto page_lock_success = VirtualMemory::lock_all_pages();
157  LLFIX_LOG_INFO("VM pages locked : " + std::string( (page_lock_success?"1":"0") ));
158  #endif
159  }
160 
161  if (ThreadLocalStorage::get_instance().create() == false)
162  {
163  LLFIX_LOG_ERROR("Failed to create thread local storage");
164  std::exit(-5);
165  }
166 
167  Socket<>::socket_library_initialise();
168 
169  ManagementServerSettings management_server_settings;
170 
171  if (management_server_settings.specified_by_user(config_file_path, config_group_name))
172  {
173  if (management_server_settings.load_from_config_file(config_file_path, config_group_name) == true)
174  {
175  if (management_server_settings.validate() == true)
176  {
177  std::filesystem::path cwd = std::filesystem::current_path();
178  std::filesystem::path full_log_path = cwd / engine_settings.log_file;
179 
180  if (m_management_server.create(management_server_settings, m_application_start_timestamp, m_version.to_string(), full_log_path.string()))
181  {
182  m_management_server_started = true;
183  LLFIX_LOG_INFO("Started management server , port : " + std::to_string(management_server_settings.management_server_port) + " nic : " + management_server_settings.management_server_nic_ip);
184  }
185  else
186  {
187  LLFIX_LOG_ERROR("Failed to create management server");
188  }
189  }
190  else
191  {
192  LLFIX_LOG_ERROR("Failed to validate management server settings. Error : " + management_server_settings.validation_error);
193  }
194  }
195  }
196 
197  LLFIX_LOG_INFO("LLFIX ENGINE Loaded config =>\n" + engine_settings.to_string());
198 
199  #ifndef LLFIX_BUILD_CONFIG_GENERATED
200  LLFIX_LOG_INFO("LLFIX ENGINE LIBRARY TYPE=HEADER-ONLY , version : " + m_version.to_string());
201  #else
202  LLFIX_LOG_INFO("LLFIX ENGINE LIBRARY TYPE=STATIC-LIB , version : " + m_version.to_string());
203  #endif
204 
205  #ifdef LLFIX_ENABLE_TCPDIRECT
206  LLFIX_LOG_INFO("LLFIX ENGINE TCPDIRECT=ON , TCPDirect library version : " + std::string(TCPDirectApi::get_version()));
207  #else
208  LLFIX_LOG_INFO("LLFIX ENGINE TCPDIRECT=OFF");
209  #endif
210 
211  #ifdef LLFIX_ENABLE_NUMA
212  LLFIX_LOG_INFO("LLFIX ENGINE LIBNUMA=ON");
213  #else
214  LLFIX_LOG_INFO("LLFIX ENGINE LIBNUMA=OFF");
215  #endif
216 
217  #ifdef LLFIX_ENABLE_OPENSSL
218  LLFIX_LOG_INFO("LLFIX ENGINE OPENSSL=ON , OpenSSL library version : " + std::string(SSLApi::get_version()));
219 
220  if (SSLApi::get_version_major_number() < 3)
221  {
222  LLFIX_LOG_ERROR("OpenSSL major version >= 3.0.0 required");
223  std::exit(-6);
224  }
225 
226  if ( !(SSLApi::get_version_major_number() == 3 && SSLApi::get_version_minor_number() == 6) )
227  {
228  fprintf(stderr, "WARNING : Supported OpenSSL version is 3.6.\n");
229  }
230 
231  LLFIX_LOG_INFO("LLFIX ENGINE OPENSSL=ON , OpenSSL initialisation : " + std::string(SSLApi::initialise()?"true":"false"));
232  #else
233  LLFIX_LOG_INFO("LLFIX ENGINE OPENSSL=OFF");
234  #endif
235 
236  #ifdef LLFIX_ENABLE_DICTIONARY
237  LLFIX_LOG_INFO("LLFIX ENGINE DICTIONARY=ON");
238  #else
239  LLFIX_LOG_INFO("LLFIX ENGINE DICTIONARY=OFF");
240  #endif
241 
242  #ifdef LLFIX_ENABLE_BINARY_FIELDS
243  LLFIX_LOG_INFO("LLFIX BINARY FIELDS SUPPORT=ON");
244  #else
245  LLFIX_LOG_INFO("LLFIX BINARY FIELDS SUPPORT=OFF");
246  #endif
247 
248  LLFIX_LOG_INFO("LLFIX ENGINE initialised successfully");
249 
250  m_engine_initialised.store(true);
251  }
252 
258  static void shutdown()
259  {
261 
262  ThreadLocalStorage::get_instance().destroy();
263 
264  #ifdef LLFIX_ENABLE_OPENSSL
265  SSLApi::uninitialise();
266  #endif
267 
268  #ifdef LLFIX_ENABLE_TCPDIRECT
269  TCPDirectApi::uninitialise();
270  #endif
271  }
272 
279  {
280  if(m_management_server_stopped == false)
281  {
282  if (m_management_server_started == true)
283  {
284  LLFIX_LOG_INFO("Stopping management server");
285  m_management_server.stop();
286  }
287  m_management_server_stopped = true;
288  }
289  }
290 
297  {
298  return m_management_server;
299  }
300 
301  static bool initialised() { return m_engine_initialised.load(); }
302  static const Version& get_version() { return m_version; }
303  static uint64_t get_application_start_timestamp() { return m_application_start_timestamp; }
304  private:
305  static inline Version m_version{"1.0.1"};
306  static inline uint64_t m_application_start_timestamp = 0;
307  static inline std::atomic<bool> m_engine_initialised = false;
308 
309  static inline ManagementServer m_management_server;
310  static inline bool m_management_server_started = false;
311  static inline bool m_management_server_stopped = false;
312 };
313 
314 } // namespace
llfix::Engine::on_start
static void on_start(const std::string &config_file_path="", const std::string &config_group_name="ENGINE")
Initialise and start the engine.
Definition: engine.h:84
llfix::Engine
Singleton Engine instance.
Definition: engine.h:71
llfix::Engine::stop_management_server
static void stop_management_server()
Stops the management server.
Definition: engine.h:278
llfix::Engine::shutdown
static void shutdown()
Stops the engine by releasing resources.
Definition: engine.h:258
llfix::ManagementServer
Management TCP server for FIX engine runtime control and inspection.
Definition: management_server.h:71
llfix::Engine::get_management_server
static ManagementServer & get_management_server()
Get reference to the management server instance.
Definition: engine.h:296