diff options
author | Vladislav Vaintroub <wlad@mariadb.com> | 2020-11-26 23:31:42 +0100 |
---|---|---|
committer | Vladislav Vaintroub <wlad@mariadb.com> | 2020-11-30 16:46:05 +0100 |
commit | 5bb5d4ad3a687ac61a9c5f8ffff6dd231f9b581a (patch) | |
tree | 626de47d94dfff5725d1ed07542a8d52cbc335ca /tpool/tpool_generic.cc | |
parent | 111963477b5ed57b00d347424d7a4d479c044c64 (diff) | |
download | mariadb-git-5bb5d4ad3a687ac61a9c5f8ffff6dd231f9b581a.tar.gz |
MDEV-24295 Reduce wakeups by tpool maintenance timer, when server is idle
If maintenance timer does not do much for prolonged time, it will
wake up less frequently, once every 4 seconds instead of once every 0.4
second.
It will wakeup more often if thread creation is throttled, to avoid stalls.
Diffstat (limited to 'tpool/tpool_generic.cc')
-rw-r--r-- | tpool/tpool_generic.cc | 119 |
1 files changed, 109 insertions, 10 deletions
diff --git a/tpool/tpool_generic.cc b/tpool/tpool_generic.cc index e508c84f442..7e6a7f0444d 100644 --- a/tpool/tpool_generic.cc +++ b/tpool/tpool_generic.cc @@ -212,6 +212,17 @@ class thread_pool_generic : public thread_pool /** True, if threadpool is being shutdown, false otherwise */ bool m_in_shutdown; + /** Maintenance timer state : true = active(ON),false = inactive(OFF)*/ + enum class timer_state_t + { + OFF, ON + }; + timer_state_t m_timer_state= timer_state_t::OFF; + void switch_timer(timer_state_t state); + + /* Updates idle_since, and maybe switches the timer off */ + void check_idle(std::chrono::system_clock::time_point now); + /** time point when timer last ran, used as a coarse clock. */ std::chrono::system_clock::time_point m_timestamp; @@ -233,7 +244,6 @@ class thread_pool_generic : public thread_pool /* maintenance related statistics (see maintenance()) */ size_t m_last_thread_count; unsigned long long m_last_activity; - std::unique_ptr<timer> m_maintenance_timer_task; void worker_main(worker_data *thread_data); void worker_end(worker_data* thread_data); @@ -357,6 +367,22 @@ public: thr_timer_settime(this, 1000ULL * initial_delay_ms); } + /* + Change only period of a periodic timer + (after the next execution). Workarounds + mysys timer deadlocks + */ + void set_period(int period_ms) + { + std::unique_lock<std::mutex> lk(m_mtx); + if (!m_on) + return; + if (!m_pool) + thr_timer_set_period(this, 1000ULL * period_ms); + else + m_period = period_ms; + } + void disarm() override { std::unique_lock<std::mutex> lk(m_mtx); @@ -380,7 +406,7 @@ public: disarm(); } }; - + timer_generic m_maintenance_timer; virtual timer* create_timer(callback_func func, void *data) override { return new timer_generic(func, data, this); @@ -526,6 +552,52 @@ void thread_pool_generic::worker_main(worker_data *thread_var) worker_end(thread_var); } + +/* + Check if threadpool had been idle for a while + Switch off maintenance timer if it is in idle state + for too long. + + Helper function, to be used inside maintenance callback, + before m_last_activity is updated +*/ +constexpr auto invalid_timestamp= std::chrono::system_clock::time_point::max(); +constexpr auto max_idle_time= std::chrono::minutes(1); + +/* Time since maintenance timer had nothing to do */ +static std::chrono::system_clock::time_point idle_since= invalid_timestamp; +void thread_pool_generic::check_idle(std::chrono::system_clock::time_point now) +{ + DBUG_ASSERT(m_task_queue.empty()); + + /* + We think that there is no activity, if there were at most 2 tasks + since last time, and there is a spare thread. + The 2 tasks (and not 0) is to account for some periodic timers. + */ + bool idle= m_standby_threads.m_count > 0; + + if (!idle) + { + idle_since= invalid_timestamp; + return; + } + + if (idle_since == invalid_timestamp) + { + idle_since= now; + return; + } + + /* Switch timer off after 1 minute of idle time */ + if (now - idle_since > max_idle_time) + { + idle_since= invalid_timestamp; + switch_timer(timer_state_t::OFF); + } +} + + /* Periodic job to fix thread count and concurrency, in case of long tasks, etc @@ -555,6 +627,7 @@ void thread_pool_generic::maintenance() if (m_task_queue.empty()) { + check_idle(m_timestamp); m_last_activity = m_tasks_dequeued + m_wakeups; return; } @@ -622,7 +695,12 @@ bool thread_pool_generic::add_thread() if (now - m_last_thread_creation < std::chrono::milliseconds(throttling_interval_ms(n_threads, m_concurrency))) { - /* Throttle thread creation.*/ + /* + Throttle thread creation and wakeup deadlock detection timer, + if is it off. + */ + switch_timer(timer_state_t::ON); + return false; } } @@ -693,7 +771,7 @@ thread_pool_generic::thread_pool_generic(int min_threads, int max_threads) : m_max_threads(max_threads), m_last_thread_count(), m_last_activity(), - m_maintenance_timer_task() + m_maintenance_timer(thread_pool_generic::maintenance_func, this, nullptr) { if (m_max_threads < m_concurrency) @@ -703,11 +781,8 @@ thread_pool_generic::thread_pool_generic(int min_threads, int max_threads) : if (!m_concurrency) m_concurrency = 1; - if (min_threads < max_threads) - { - m_maintenance_timer_task.reset(new timer_generic(thread_pool_generic::maintenance_func, this, nullptr)); - m_maintenance_timer_task->set_time((int)m_timer_interval.count(), (int)m_timer_interval.count()); - } + // start the timer + m_maintenance_timer.set_time(0, (int)m_timer_interval.count()); } @@ -780,6 +855,30 @@ void thread_pool_generic::wait_end() } } + +void thread_pool_generic::switch_timer(timer_state_t state) +{ + if (m_timer_state == state) + return; + /* + We can't use timer::set_time, because mysys timers are deadlock + prone. + + Instead, to switch off we increase the timer period + and decrease period to switch on. + + This might introduce delays in thread creation when needed, + as period will only be changed when timer fires next time. + For this reason, we can't use very long periods for the "off" state. + */ + m_timer_state= state; + long long period= (state == timer_state_t::OFF) ? + m_timer_interval.count()*10: m_timer_interval.count(); + + m_maintenance_timer.set_period((int)period); +} + + /** Wake up all workers, and wait until they are gone Stop the timer. @@ -794,7 +893,7 @@ thread_pool_generic::~thread_pool_generic() m_aio.reset(); /* Also stop the maintanence task early. */ - m_maintenance_timer_task.reset(); + m_maintenance_timer.disarm(); std::unique_lock<std::mutex> lk(m_mtx); m_in_shutdown= true; |