main.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. #include <spdlog/spdlog.h>
  2. #include <atomic>
  3. #include <csignal>
  4. #include <cstdlib>
  5. #include <string>
  6. #include <thread>
  7. #include <unistd.h>
  8. #include "smartbotic/common.hpp"
  9. #include "smartbotic/llm/config.hpp"
  10. #include "smartbotic/llm/grpc/server.hpp"
  11. #include "smartbotic/llm/server_config.hpp"
  12. namespace {
  13. // Global server pointer for signal handling
  14. smartbotic::llm::grpc::GrpcServer* g_server = nullptr;
  15. // Atomic flag for shutdown tracking
  16. std::atomic<bool> g_shutdown_requested{false};
  17. // Self-pipe for async-signal-safe notification
  18. int g_shutdown_pipe[2] = {-1, -1};
  19. void SignalHandler(int signal) {
  20. if (g_shutdown_requested.exchange(true)) {
  21. // Second signal - force exit
  22. const char* msg = "Received second signal, forcing immediate exit\n";
  23. [[maybe_unused]] auto n = write(STDERR_FILENO, msg, 48);
  24. std::_Exit(1);
  25. }
  26. // Write to pipe to notify shutdown thread (async-signal-safe)
  27. char sig = static_cast<char>(signal);
  28. [[maybe_unused]] auto n = write(g_shutdown_pipe[1], &sig, 1);
  29. }
  30. // Shutdown thread that waits for signal and calls gRPC Shutdown()
  31. void ShutdownThread() {
  32. char sig = 0;
  33. // Block until signal is received (read from pipe)
  34. ssize_t n = read(g_shutdown_pipe[0], &sig, 1);
  35. if (n > 0) {
  36. const char* signal_name = (sig == SIGINT) ? "SIGINT" : "SIGTERM";
  37. spdlog::info("Received {} ({}), initiating graceful shutdown...", signal_name,
  38. static_cast<int>(sig));
  39. // Safe to call gRPC Shutdown() from this thread (not from signal handler)
  40. if (g_server != nullptr) {
  41. g_server->RequestShutdown();
  42. }
  43. }
  44. }
  45. auto SetLogLevel(smartbotic::llm::LogLevel level) {
  46. switch (level) {
  47. case smartbotic::llm::LogLevel::kTrace:
  48. spdlog::set_level(spdlog::level::trace);
  49. break;
  50. case smartbotic::llm::LogLevel::kDebug:
  51. spdlog::set_level(spdlog::level::debug);
  52. break;
  53. case smartbotic::llm::LogLevel::kInfo:
  54. spdlog::set_level(spdlog::level::info);
  55. break;
  56. case smartbotic::llm::LogLevel::kWarn:
  57. spdlog::set_level(spdlog::level::warn);
  58. break;
  59. case smartbotic::llm::LogLevel::kError:
  60. spdlog::set_level(spdlog::level::err);
  61. break;
  62. case smartbotic::llm::LogLevel::kCritical:
  63. spdlog::set_level(spdlog::level::critical);
  64. break;
  65. case smartbotic::llm::LogLevel::kOff:
  66. spdlog::set_level(spdlog::level::off);
  67. break;
  68. }
  69. }
  70. } // namespace
  71. int main(int argc, char* argv[]) {
  72. // Initialize common logging first (with default settings)
  73. smartbotic::common::Initialize("smartbotic-llm");
  74. spdlog::info("SmartBotic LLM Service starting...");
  75. // Load configuration from file, environment, and command line
  76. auto config = smartbotic::llm::ConfigLoader::Load(argc, argv);
  77. // Apply configured log level
  78. SetLogLevel(config.log_level);
  79. // Print configuration
  80. smartbotic::llm::ConfigLoader::PrintConfig(config);
  81. // Create self-pipe for async-signal-safe shutdown notification
  82. if (pipe(g_shutdown_pipe) == -1) {
  83. spdlog::error("Failed to create shutdown pipe");
  84. smartbotic::common::Shutdown();
  85. return 1;
  86. }
  87. // Convert LlmConfig to ServerConfig for the gRPC server
  88. auto server_config = smartbotic::llm::ServerConfig::FromConfig(config);
  89. // Create and start the server
  90. smartbotic::llm::grpc::GrpcServer server(server_config);
  91. g_server = &server;
  92. // Start shutdown thread that will call gRPC Shutdown() when signaled
  93. std::thread shutdown_thread(ShutdownThread);
  94. // Set up signal handlers for graceful shutdown (SIGTERM/SIGINT)
  95. struct sigaction sa{};
  96. sa.sa_handler = SignalHandler;
  97. sigemptyset(&sa.sa_mask);
  98. sa.sa_flags = 0;
  99. if (sigaction(SIGINT, &sa, nullptr) == -1) {
  100. spdlog::error("Failed to set SIGINT handler");
  101. close(g_shutdown_pipe[1]);
  102. shutdown_thread.join();
  103. close(g_shutdown_pipe[0]);
  104. smartbotic::common::Shutdown();
  105. return 1;
  106. }
  107. if (sigaction(SIGTERM, &sa, nullptr) == -1) {
  108. spdlog::error("Failed to set SIGTERM handler");
  109. close(g_shutdown_pipe[1]);
  110. shutdown_thread.join();
  111. close(g_shutdown_pipe[0]);
  112. smartbotic::common::Shutdown();
  113. return 1;
  114. }
  115. spdlog::info("Signal handlers registered for SIGINT and SIGTERM");
  116. if (!server.Start()) {
  117. spdlog::error("Failed to start server");
  118. close(g_shutdown_pipe[1]);
  119. shutdown_thread.join();
  120. close(g_shutdown_pipe[0]);
  121. smartbotic::common::Shutdown();
  122. return 1;
  123. }
  124. // Wait for server to stop (either by signal or error)
  125. server.Wait();
  126. // Wait for shutdown thread to complete
  127. shutdown_thread.join();
  128. // Perform cleanup after Wait() returns (safe to do in main thread)
  129. server.Cleanup();
  130. // Close pipe
  131. close(g_shutdown_pipe[0]);
  132. close(g_shutdown_pipe[1]);
  133. spdlog::info("SmartBotic LLM Service shutdown complete");
  134. g_server = nullptr;
  135. smartbotic::common::Shutdown();
  136. return 0;
  137. }