65 typename Acceptor::endpoint endpoint,
66 std::string server_name = std::string(
"Crow/") + VERSION,
67 std::tuple<Middlewares...>* middlewares =
nullptr,
68 unsigned int concurrency = 1,
70 typename Adaptor::context* adaptor_ctx =
nullptr,
72 concurrency_(concurrency),
73 task_queue_length_pool_(concurrency_ - 1),
74 acceptor_(io_context_),
75 signals_(io_context_),
76 tick_timer_(io_context_),
77 accept_timer_(io_context_),
80 server_name_(server_name),
81 middlewares_(middlewares),
82 adaptor_ctx_(adaptor_ctx),
83 tcp_socket_options_(tcp_socket_options)
85 if (startup_failed_) {
86 CROW_LOG_ERROR <<
"Startup failed; not running server.";
92 acceptor_.raw_acceptor().open(endpoint.protocol(), ec);
94 CROW_LOG_ERROR <<
"Failed to open acceptor: " << ec.message();
95 startup_failed_ =
true;
99 acceptor_.raw_acceptor().set_option(Acceptor::reuse_address_option(), ec);
101 CROW_LOG_ERROR <<
"Failed to set socket option: " << ec.message();
102 startup_failed_ =
true;
106 acceptor_.raw_acceptor().bind(endpoint, ec);
108 CROW_LOG_ERROR <<
"Failed to bind to " << acceptor_.address()
109 <<
":" << acceptor_.port() <<
" - " << ec.message();
110 startup_failed_ =
true;
114 acceptor_.raw_acceptor().listen(tcp::acceptor::max_listen_connections, ec);
116 CROW_LOG_ERROR <<
"Failed to listen on port: " << ec.message();
117 startup_failed_ =
true;
124 void set_tick_function(std::chrono::milliseconds d, std::function<
void()> f)
133 tick_timer_.expires_after(std::chrono::milliseconds(tick_interval_.count()));
134 tick_timer_.async_wait([
this](
const error_code& ec) {
144 if (startup_failed_) {
145 CROW_LOG_ERROR <<
"Server startup failed. Aborting run().";
149 uint16_t worker_thread_count = concurrency_ - 1;
150 for (
int i = 0; i < worker_thread_count; i++)
151 io_context_pool_.emplace_back(
new asio::io_context());
152 get_cached_date_str_pool_.resize(worker_thread_count);
153 task_timer_pool_.resize(worker_thread_count);
155 std::vector<std::future<void>> v;
156 std::atomic<int> init_count(0);
157 for (uint16_t i = 0; i < worker_thread_count; i++)
160 std::launch::async, [
this, i, &init_count] {
162 auto last = std::chrono::steady_clock::now();
164 std::string date_str;
165 auto update_date_str = [&] {
166 auto last_time_t = time(0);
169#if defined(_MSC_VER) || defined(__MINGW32__)
170 gmtime_s(&my_tm, &last_time_t);
172 gmtime_r(&last_time_t, &my_tm);
174 date_str.resize(100);
175 size_t date_str_sz = strftime(&date_str[0], 99,
"%a, %d %b %Y %H:%M:%S GMT", &my_tm);
176 date_str.resize(date_str_sz);
179 get_cached_date_str_pool_[i] = [&]() -> std::string {
180 if (std::chrono::steady_clock::now() - last >= std::chrono::seconds(1))
182 last = std::chrono::steady_clock::now();
191 task_timer_pool_[i] = &task_timer;
192 task_queue_length_pool_[i] = 0;
199 if (io_context_pool_[i]->run() == 0)
205 catch (std::exception& e)
207 CROW_LOG_ERROR <<
"Worker Crash: An uncaught exception occurred: " << e.what();
212 if (tick_function_ && tick_interval_.count() > 0)
214 tick_timer_.expires_after(std::chrono::milliseconds(tick_interval_.count()));
215 tick_timer_.async_wait(
216 [
this](
const error_code& ec) {
222 handler_->port(acceptor_.port());
223 handler_->address_is_bound();
224 CROW_LOG_INFO << server_name_
225 <<
" server is running at " << acceptor_.url_display(handler_->ssl_used())
226 <<
" using " << concurrency_ <<
" threads";
227 CROW_LOG_INFO <<
"Call `app.loglevel(crow::LogLevel::Warning)` to hide Info level logs.";
230 [&](
const error_code& ,
int ) {
234 while (worker_thread_count != init_count)
235 std::this_thread::yield();
243 CROW_LOG_INFO <<
"Exiting.";
250 shutting_down_ =
true;
255 if (acceptor_.raw_acceptor().is_open())
257 CROW_LOG_INFO <<
"Closing acceptor. " << &acceptor_;
259 acceptor_.raw_acceptor().close(ec);
262 CROW_LOG_WARNING <<
"Failed to close acceptor: " << ec.message();
266 accept_timer_.cancel();
268 for (
auto& io_context : io_context_pool_)
270 if (io_context !=
nullptr)
272 CROW_LOG_INFO <<
"Closing IO service " << &io_context;
277 CROW_LOG_INFO <<
"Closing main IO service (" << &io_context_ <<
')';
282 uint16_t port()
const {
283 return acceptor_.local_endpoint().port();
289 std::unique_lock<std::mutex> lock(start_mutex_);
291 std::cv_status status = std::cv_status::no_timeout;
292 while (!server_started_ && !startup_failed_ && status == std::cv_status::no_timeout)
293 status = cv_started_.wait_until(lock, wait_until);
303 void signal_add(
int signal_number)
305 signals_.add(signal_number);
309 size_t pick_io_context_idx()
311 size_t min_queue_idx = 0;
316 for (
size_t i = 1; i < task_queue_length_pool_.size() && task_queue_length_pool_[min_queue_idx] > 0; i++)
319 if (task_queue_length_pool_[i] < task_queue_length_pool_[min_queue_idx])
322 return min_queue_idx;
329 size_t context_idx = pick_io_context_idx();
330 asio::io_context& ic = *io_context_pool_[context_idx];
331 auto p = std::make_shared<Connection<Adaptor, Handler, Middlewares...>>(
332 ic, handler_, server_name_, middlewares_,
333 get_cached_date_str_pool_[context_idx], *task_timer_pool_[context_idx], adaptor_ctx_, task_queue_length_pool_[context_idx]);
335 CROW_LOG_DEBUG << &ic <<
" {" << context_idx <<
"} queue length: " << task_queue_length_pool_[context_idx];
337 acceptor_.raw_acceptor().async_accept(
339 [
this, p, &ic](error_code ec) {
342 detail::socket::apply_tcp_socket_options(p->socket(), tcp_socket_options_);
351 if (shutting_down_ || ec == asio::error::operation_aborted)
354 CROW_LOG_ERROR <<
"Failed to accept connection: " << ec.message();
355 if (detail::is_descriptor_exhaustion(ec))
357 accept_timer_.expires_after(std::chrono::milliseconds(100));
358 accept_timer_.async_wait([
this](
const error_code& tec) {
372 std::unique_lock<std::mutex> lock(start_mutex_);
373 server_started_ =
true;
374 cv_started_.notify_all();
378 unsigned int concurrency_{2};
379 std::vector<std::atomic<unsigned int>> task_queue_length_pool_;
380 std::vector<std::unique_ptr<asio::io_context>> io_context_pool_;
381 asio::io_context io_context_;
382 std::vector<detail::task_timer*> task_timer_pool_;
383 std::vector<std::function<std::string()>> get_cached_date_str_pool_;
385 bool shutting_down_ =
false;
386 bool server_started_{
false};
387 bool startup_failed_ =
false;
388 std::condition_variable cv_started_;
389 std::mutex start_mutex_;
390 asio::signal_set signals_;
392 asio::basic_waitable_timer<std::chrono::high_resolution_clock> tick_timer_;
393 asio::steady_timer accept_timer_;
396 std::uint8_t timeout_;
397 std::string server_name_;
400 std::chrono::milliseconds tick_interval_;
401 std::function<void()> tick_function_;
403 std::tuple<Middlewares...>* middlewares_;
405 typename Adaptor::context* adaptor_ctx_;
406 detail::socket::tcp_socket_options tcp_socket_options_;